Permalink
Please sign in to comment.
Browse files
Objective-C API for JavaScriptCore
https://bugs.webkit.org/show_bug.cgi?id=105889 Reviewed by Filip Pizlo. ../JavaScriptCore: For a detailed description of the API implemented here, see: JSContext.h APIJSValue.h JSVirtualMachine.h JSExport.h Still to do - (1) Shoud rename APIJSValue.h -> JSValue.h (but we'll have to rename JSValue.h first). (2) Numerous FIXMEs, all with separate bugs filed. * API/APIJSValue.h: Added. - this Objective-C class is used to reference a JavaScript object. * API/JSBase.h: - added JS_OBJC_API_ENABLED macro to control ObjC API support. * API/JSBlockAdaptor.h: Added. - this Objective-C class is used in creating a special NSBlock proxying a JavaScript function. * API/JSBlockAdaptor.mm: Added. (BlockArgument): (BlockArgument::~BlockArgument): (BlockArgumentBoolean): (BlockArgumentBoolean::get): (BlockArgumentNumeric): (BlockArgumentNumeric::get): (BlockArgumentId): (BlockArgumentId::get): (BlockArgumentStruct): (BlockArgumentStruct::BlockArgumentStruct): (BlockArgumentStruct::~BlockArgumentStruct): (BlockArgumentStruct::get): - decoded arguent type information of a JSBlockAdaptor. (BlockArgumentTypeDelegate): (BlockArgumentTypeDelegate::typeInteger): (BlockArgumentTypeDelegate::typeDouble): (BlockArgumentTypeDelegate::typeBool): (BlockArgumentTypeDelegate::typeVoid): (BlockArgumentTypeDelegate::typeId): (BlockArgumentTypeDelegate::typeOfClass): (BlockArgumentTypeDelegate::typeBlock): (BlockArgumentTypeDelegate::typeStruct): - delegate for use in conjunction with parseObjCType. (BlockResult): (BlockResult::~BlockResult): (BlockResultVoid): (BlockResultVoid::set): (BlockResultInteger): (BlockResultInteger::set): (BlockResultDouble): (BlockResultDouble::set): (BlockResultBoolean): (BlockResultBoolean::set): (BlockResultStruct): (BlockResultStruct::BlockResultStruct): (BlockResultStruct::~BlockResultStruct): (BlockResultStruct::set): - decoded result type information of a JSBlockAdaptor. (buildBlockSignature): - partial step in constructing a signature with stack offset information from one without. (-[JSBlockAdaptor initWithBlockSignatureFromProtocol:]): - constructor. (-[JSBlockAdaptor blockMatchesSignature:]): - check whether signature strings match, where only one contains stack frame offsets. (-[JSBlockAdaptor blockFromValue:inContext:withException:]): - use the adaptor to create a special forwarding block. * API/JSCallbackObjectFunctions.h: (JSC::::inherits): - add missing braces to multiline for statement. * API/JSContext.h: Added. - this Objective-C class is used to reference a JavaScript context. * API/JSContext.mm: Added. (-[JSContext init]): - constructor. (-[JSContext initWithVirtualMachine:]): - construct in a given VM (JSGlobalData). (-[JSContext evaluateScript:]): (-[JSContext globalObject]): - evaluate a script, global object accessor. (+[JSContext currentContext]): (+[JSContext currentThis]): (+[JSContext currentArguments]): - These methods obtain context, this, arguments from within a callback. (-[JSContext virtualMachine]): - implementation for .virtualMachine property. (-[JSContext objectForKeyedSubscript:]): (-[JSContext setObject:forKeyedSubscript:]): - support for subscript property access. (contextInternalContext): - internal accessor to m_context. (-[JSContext dealloc]): - desctructor. (-[JSContext notifyException:]): (-[JSContext valueFromNotifyException:]): (-[JSContext boolFromNotifyException:]): - internal method to record an exception was thrown. (-[JSContext beginCallbackWithData:thisValue:argumentCount:arguments:]): (-[JSContext endCallbackWithData:]): - internal methods to push/pop a callback record. (-[JSContext protect:]): (-[JSContext unprotect:]): - internal methods to add a value to a protect set (used to protect the internal property of JSValue). (-[JSContext wrapperForObject:]): - internal method to create a wrapper object. (WeakContextRef::WeakContextRef): (WeakContextRef::~WeakContextRef): (WeakContextRef::get): (WeakContextRef::set): - Helper class to implement a weak reference to a JSContext. * API/JSContextInternal.h: Added. (CallbackData): (WeakContextRef): - see API/JSContext.mm for description of internal methods. * API/JSExport.h: Added. - Provides JSExport protocol & JSExportAs macro. * API/JSValue.mm: Added. (+[JSValue valueWithObject:inContext:]): (+[JSValue valueWithBool:inContext:]): (+[JSValue valueWithDouble:inContext:]): (+[JSValue valueWithInt32:inContext:]): (+[JSValue valueWithUInt32:inContext:]): (+[JSValue valueWithNewObjectInContext:]): (+[JSValue valueWithNewArrayInContext:]): (+[JSValue valueWithNewRegularExpressionFromPattern๐ inContext:]): (+[JSValue valueWithNewErrorFromMessage:inContext:]): (+[JSValue valueWithNullInContext:]): (+[JSValue valueWithUndefinedInContext:]): - Constructors. (-[JSValue toObject]): (-[JSValue toObjectOfClass:]): (-[JSValue toBool]): (-[JSValue toDouble]): (-[JSValue toInt32]): (-[JSValue toUInt32]): (-[JSValue toNumber]): (-[JSValue toString]): (-[JSValue toDate]): (-[JSValue toArray]): (-[JSValue toDictionary]): - Conversion to Objective-C types. (-[JSValue valueForProperty:]): (-[JSValue setValue:forProperty:]): (-[JSValue deleteProperty:]): (-[JSValue hasProperty:]): (-[JSValue defineProperty:descriptor:]): - Property access by property name. (-[JSValue valueAtIndex:]): (-[JSValue setValue:atIndex:]): - Property access by index. (-[JSValue isUndefined]): (-[JSValue isNull]): (-[JSValue isBoolean]): (-[JSValue isNumber]): (-[JSValue isString]): (-[JSValue isObject]): - Test JavaScript type. (-[JSValue isEqualToObject:]): (-[JSValue isEqualWithTypeCoercionToObject:]): (-[JSValue isInstanceOf:]): - ===, ==, instanceof operators. (-[JSValue callWithArguments:]): (-[JSValue constructWithArguments:]): (-[JSValue invokeMethod:withArguments:]): - Call & construct. (-[JSValue context]): - implementation for .context property. (-[JSValue toPoint]): (-[JSValue toRange]): (-[JSValue toRect]): (-[JSValue toSize]): (+[JSValue valueWithPoint:inContext:]): (+[JSValue valueWithRange:inContext:]): (+[JSValue valueWithRect:inContext:]): (+[JSValue valueWithSize:inContext:]): - Support for NS struct types. (-[JSValue objectForKeyedSubscript:]): (-[JSValue objectAtIndexedSubscript:]): (-[JSValue setObject:forKeyedSubscript:]): (-[JSValue setObject:atIndexedSubscript:]): - support for subscript property access. (isDate): (isArray): - internal helper functions to check for instances of JS Date, Array types. (JSContainerConvertor): (Task): (JSContainerConvertor::JSContainerConvertor): (JSContainerConvertor::isWorkListEmpty): (JSContainerConvertor::convert): (JSContainerConvertor::add): (JSContainerConvertor::take): - helper class for tracking state while converting to Array/Dictionary objects. (valueToObjectWithoutCopy): (containerValueToObject): (valueToObject): (valueToNumber): (valueToString): (valueToDate): (valueToArray): (valueToDictionary): - function for converting JavaScript values to Objective-C objects. (ObjcContainerConvertor): (ObjcContainerConvertor::ObjcContainerConvertor): (ObjcContainerConvertor::isWorkListEmpty): (ObjcContainerConvertor::convert): (ObjcContainerConvertor::add): (ObjcContainerConvertor::take): - helper class for tracking state while converting to Array/Dictionary values. (objectToValueWithoutCopy): (objectToValue): (valueInternalValue): - function for converting Objective-C objects to JavaScript values. (+[JSValue valueWithValue:inContext:]): (-[JSValue initWithValue:inContext:]): - internal constructors. (StructTagHandler): (getStructTagHandler): (+[JSValue selectorForStructToValue:]): (+[JSValue selectorForValueToStruct:]): - methods to tracking struct types that support conversion to/from JSValue. (-[JSValue dealloc]): - destructor. (-[JSValue description]): - Objective-C to-NSString conversion. (typeToValueInvocationFor): (valueToTypeInvocationFor): - create invocation objects for conversion to/from JSValue. * API/JSValueInternal.h: Added. - see API/JSValue.mm for description of internal methods. * API/JSVirtualMachine.h: Added. - this Objective-C class is used to reference a JavaScript virtual machine (JSGlobalData). * API/JSVirtualMachine.mm: Added. (-[JSVirtualMachine init]): (-[JSVirtualMachine dealloc]): - constructor & destructor. (getGroupFromVirtualMachine): - internal accessor for m_group property. * API/JSVirtualMachineInternal.h: Added. - see API/JSVirtualMachine.mm for description of internal methods. * API/JSWrapperMap.h: Added. * API/JSWrapperMap.mm: Added. (wrapperClass): - singleton root for detction (& unwrapping) of wrapper objects. (selectorToPropertyName): - default selector to property name conversion. (createObjectWithCustomBrand): - creates a JSObject with a custom NativeBrand (class name). (createRenameMap): - parse @optional properties of a JSExport protocol. (putNonEnumerable): - property put with enumerable=false. (copyMethodsToObject): - iterate methods in a protocol; add functions to a JSObject. (parsePropertyAttributes): - examine protocol property metadata. (makeSetterName): - "foo" -> "setFoo" (copyPrototypeProperties): - create properties on a Protocol object reflecting the instance methods & properties of a protocol. (-[JSObjCClassInfo initWithContext:forClass:superClassInfo:]): (-[JSObjCClassInfo dealloc]): (-[JSObjCClassInfo wrapperForObject:]): (-[JSObjCClassInfo constructor]): - cache the Protocol/Constructor objects for an Objective-C type. (-[JSWrapperMap initWithContext:]): (-[JSWrapperMap dealloc]): - constructor & desctructor. (-[JSWrapperMap classInfoForClass:]): - maps Class -> JSObjCClassInfo. (-[JSWrapperMap wrapperForObject:]): - cretae or retrieve a cached wrapper value for an object. (tryUnwrapObjcObject): - check whether a value is a wrapper object; unwrap if so. * API/JavaScriptCore.h: - Added includes for new API headers. * API/ObjCCallbackFunction.h: Added. - this class is used to wrap Objective-C instance methods, class methods & blocks as JSFunction objects. * API/ObjCCallbackFunction.mm: Added. (CallbackArgument): (CallbackArgument::~CallbackArgument): (CallbackArgumentBoolean): (CallbackArgumentBoolean::set): (CallbackArgumentInteger): (CallbackArgumentInteger::set): (CallbackArgumentDouble): (CallbackArgumentDouble::set): (CallbackArgumentJSValue): (CallbackArgumentJSValue::set): (CallbackArgumentId): (CallbackArgumentId::set): (CallbackArgumentOfClass): (CallbackArgumentOfClass::CallbackArgumentOfClass): (CallbackArgumentOfClass::~CallbackArgumentOfClass): (CallbackArgumentOfClass::set): (CallbackArgumentNSNumber): (CallbackArgumentNSNumber::set): (CallbackArgumentNSString): (CallbackArgumentNSString::set): (CallbackArgumentNSDate): (CallbackArgumentNSDate::set): (CallbackArgumentNSArray): (CallbackArgumentNSArray::set): (CallbackArgumentNSDictionary): (CallbackArgumentNSDictionary::set): (CallbackArgumentStruct): (CallbackArgumentStruct::CallbackArgumentStruct): (CallbackArgumentStruct::~CallbackArgumentStruct): (CallbackArgumentStruct::set): (CallbackArgumentBlockCallback): (CallbackArgumentBlockCallback::CallbackArgumentBlockCallback): (CallbackArgumentBlockCallback::~CallbackArgumentBlockCallback): (CallbackArgumentBlockCallback::set): - decoded arguent type information of a ObjCCallbackFunction. (ArgumentTypeDelegate): (ArgumentTypeDelegate::typeInteger): (ArgumentTypeDelegate::typeDouble): (ArgumentTypeDelegate::typeBool): (ArgumentTypeDelegate::typeVoid): (ArgumentTypeDelegate::typeId): (ArgumentTypeDelegate::typeOfClass): (ArgumentTypeDelegate::typeBlock): (ArgumentTypeDelegate::typeStruct): - delegate for use in conjunction with parseObjCType. (CallbackResult): (CallbackResult::~CallbackResult): (CallbackResultVoid): (CallbackResultVoid::get): (CallbackResultId): (CallbackResultId::get): (CallbackResultNumeric): (CallbackResultNumeric::get): (CallbackResultBoolean): (CallbackResultBoolean::get): (CallbackResultStruct): (CallbackResultStruct::CallbackResultStruct): (CallbackResultStruct::~CallbackResultStruct): (CallbackResultStruct::get): - decoded result type information of a ObjCCallbackFunction. (ResultTypeDelegate): (ResultTypeDelegate::typeInteger): (ResultTypeDelegate::typeDouble): (ResultTypeDelegate::typeBool): (ResultTypeDelegate::typeVoid): (ResultTypeDelegate::typeId): (ResultTypeDelegate::typeOfClass): (ResultTypeDelegate::typeBlock): (ResultTypeDelegate::typeStruct): - delegate for use in conjunction with parseObjCType. (ObjCCallbackFunction): (ObjCCallbackFunction::ObjCCallbackFunction): (ObjCCallbackFunction::~ObjCCallbackFunction): - constructor & destructor. (ObjCCallbackFunction::context): - accessor. (ObjCCallbackFunction::wrappedBlock): - attemmpt to unwrap a block object. (objCCallbackFunctionFinalize): (objCCallbackFunctionCallAsFunction): (objCCallbackFunctionClass): - JSClassRef used to represent ObjCCallbackFunction objects. (ObjCCallbackFunction::call): (blockSignatureContainsClass): - helper function to determine if we're running on a recent Clang. (skipNumber): - helper used in parsing signature strings. (objCCallbackFunctionForInvocation): (objCCallbackFunctionForMethod): (objCCallbackFunctionForBlock): - functions to try to create ObjCCallbackFunction instances for methods/blocks. (tryUnwrapBlock): - attemmpt to unwrap a block object. * API/ObjcRuntimeExtras.h: Added. (protocolImplementsProtocol): (forEachProtocolImplementingProtocol): (forEachMethodInClass): (forEachMethodInProtocol): (forEachPropertyInProtocol): - functions used in reflecting on Objective-C types. (skipPair): - parsing helper used by parseObjCType, scans for matching parentheses. (StringRange): (StringRange::StringRange): (StringRange::~StringRange): (StringRange::operator const char*): (StringRange::get): - Helper class - create a c string copy of a range of an existing string. (parseObjCType): - function to parse Objective-C type strings, makes callbacks to a deleagte. * API/tests/testapi.c: (main): - added call to testObjectiveCAPI (in testapi.m). * API/tests/testapi.m: Added. (+[ParentObject parentTest]): (+[TestObject testObject]): (+[TestObject classTest]): (-[TestObject getString]): (-[TestObject testArgumentTypesWithInt:double:boolean:string:number:array:dictionary:]): (-[TestObject callback:]): (-[TextXYZ test:]): - test object, used in various test vases. (checkResult): - helper function. (blockSignatureContainsClass): - helper function to determine if we're running on a recent Clang. (testObjectiveCAPI): - new test cases. * JavaScriptCore.xcodeproj/project.pbxproj: - added new files. * runtime/JSGlobalData.cpp: (JSC::JSGlobalData::JSGlobalData): * runtime/JSGlobalData.h: (JSGlobalData): - added m_apiData - provide convenient storage for use by the API. * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::JSGlobalObject): * runtime/JSGlobalObject.h: (JSGlobalObject): - added m_apiData - provide convenient storage for use by the API. ../WTF: * wtf/WTFThreadData.cpp: (WTF::WTFThreadData::WTFThreadData): * wtf/WTFThreadData.h: (WTFThreadData): - Added m_apiData - provide convenient storage for use by the API. git-svn-id: http://svn.webkit.org/repository/webkit/trunk@138604 268f45cc-cd09-0410-ab3c-d52691b4dbfc
- Loading branch information...
Showing
with
5,170 additions
and 4 deletions.
- +301 โ0 Source/JavaScriptCore/API/APIJSValue.h
- +8 โ0 Source/JavaScriptCore/API/JSBase.h
- +38 โ0 Source/JavaScriptCore/API/JSBlockAdaptor.h
- +428 โ0 Source/JavaScriptCore/API/JSBlockAdaptor.mm
- +2 โ2 Source/JavaScriptCore/API/JSCallbackObjectFunctions.h
- +116 โ0 Source/JavaScriptCore/API/JSContext.h
- +262 โ0 Source/JavaScriptCore/API/JSContext.mm
- +73 โ0 Source/JavaScriptCore/API/JSContextInternal.h
- +127 โ0 Source/JavaScriptCore/API/JSExport.h
- +1,167 โ0 Source/JavaScriptCore/API/JSValue.mm
- +54 โ0 Source/JavaScriptCore/API/JSValueInternal.h
- +41 โ0 Source/JavaScriptCore/API/JSVirtualMachine.h
- +66 โ0 Source/JavaScriptCore/API/JSVirtualMachine.mm
- +37 โ0 Source/JavaScriptCore/API/JSVirtualMachineInternal.h
- +45 โ0 Source/JavaScriptCore/API/JSWrapperMap.h
- +463 โ0 Source/JavaScriptCore/API/JSWrapperMap.mm
- +9 โ0 Source/JavaScriptCore/API/JavaScriptCore.h
- +35 โ0 Source/JavaScriptCore/API/ObjCCallbackFunction.h
- +677 โ0 Source/JavaScriptCore/API/ObjCCallbackFunction.mm
- +219 โ0 Source/JavaScriptCore/API/ObjcRuntimeExtras.h
- +9 โ1 Source/JavaScriptCore/API/tests/testapi.c
- +477 โ0 Source/JavaScriptCore/API/tests/testapi.m
- +421 โ0 Source/JavaScriptCore/ChangeLog
- +72 โ0 Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
- +1 โ0 Source/JavaScriptCore/runtime/JSGlobalData.cpp
- +2 โ0 Source/JavaScriptCore/runtime/JSGlobalData.h
- +1 โ0 Source/JavaScriptCore/runtime/JSGlobalObject.cpp
- +2 โ0 Source/JavaScriptCore/runtime/JSGlobalObject.h
- +13 โ0 Source/WTF/ChangeLog
- +2 โ1 Source/WTF/wtf/WTFThreadData.cpp
- +2 โ0 Source/WTF/wtf/WTFThreadData.h
301
Source/JavaScriptCore/API/APIJSValue.h
| @@ -0,0 +1,301 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +@class JSContext; | ||
| + | ||
| +// A JSValue is a reference to a value within the JavaScript object space of a | ||
| +// JSVirtualMachine. All instances of JSValue originate from a JSContext, and | ||
| +// hold a weak reference to this JSContext. Where an instance method is invoked | ||
| +// upon a JSValue, and this returns another JSValue, the returned JSValue will | ||
| +// originate from the same JSContext as the JSValue on which the method was | ||
| +// invoked. | ||
| +// | ||
| +// The JSContext is used to manage the life-cycle of the refereced JavaScript | ||
| +// value within the virtual machine. So long as the JSContext is being retained | ||
| +// the JSValue will continue to keep the referencd value within the JavaScript | ||
| +// virtual machine alive. When the JSContext associated with a JSValue is | ||
| +// deallocated the weak context property of the JSValue will become nil. After | ||
| +// this occurs the value within the JavaScript virtual machine is no longer | ||
| +// being maintained by this JSValue, and the JSValue instance may no longer by | ||
| +// used as a reference to this value. For any method invoked on instance of | ||
| +// JSValue with a nil context, the method will return a nil/zero/false result. | ||
| +// | ||
| +// For all methods taking arguments of type id, arguments will be converted | ||
| +// into a JavaScript value according to the conversion specified below. | ||
| +// All JavaScript values are associated with a particular JSVirtualMachine | ||
| +// (the associated JSVirtualMachine is avaible indirectly via the context | ||
| +// property). An instance of JSValue may only be passed as an argument to | ||
| +// methods on instances of JSValue and JSContext that belong to the same | ||
| +// JSVirtualMachine - passing a JSValue to a method on an object originating | ||
| +// from a different JSVirtualMachine will result in an Objective-C exception | ||
| +// being raised. | ||
| +// | ||
| +// Conversion between Objective-C and JavaScript types. | ||
| +// | ||
| +// When converting between JavaScript values and Objective-C objects a copy is | ||
| +// performed. Values of types listed below are copied to the corresponding | ||
| +// types on conversion in each direction. For NSDictionaries, entries in the | ||
| +// dictionary that are keyed by strings are copied onto a JavaScript object. | ||
| +// For dictionaries and arrays, conversion is recursive, with the same object | ||
| +// conversion being applied to all entries in the collection. | ||
| +// | ||
| +// Objective-C type | JavaScript type | ||
| +// --------------------+--------------------- | ||
| +// nil | undefined | ||
| +// NSNull | null | ||
| +// NSString | string | ||
| +// NSNumber | number, boolean | ||
| +// NSDictionary | Object object | ||
| +// NSArray | Array object | ||
| +// NSDate | Date object | ||
| +// NSBlock * | Function object * | ||
| +// id ** | Wrapper object ** | ||
| +// Class *** | Constructor object *** | ||
| +// | ||
| +// * Instances of NSBlock with supported arguments types will be presented to | ||
| +// JavaScript as a callable Function object. For more information on supported | ||
| +// argument types see JSExport.h. If a JavaScript Function originating from an | ||
| +// Objective-C block is converted back to an Objective-C object the block will | ||
| +// be returned. All other JavaScript functions will be converted in the same | ||
| +// manner as a JavaScript object of type Object. | ||
| +// | ||
| +// ** For Objective-C instances that do not derive from the set of types listed | ||
| +// above, a wrapper object to provide a retaining handle to the Objective-C | ||
| +// instance from JavaScript. For more information on these wrapper objects, see | ||
| +// JSExport.h. When a JavaScript wrapper object is converted back to Objective-C | ||
| +// the Objective-C instance being retained by the wrapper is returned. | ||
| +// | ||
| +// *** For Objective-C Class objects a constructor object containing exported | ||
| +// class methods will be returned. See JSExport.h sor more information on | ||
| +// constructor objects. | ||
| +NS_CLASS_AVAILABLE(10_9, NA) | ||
| +@interface JSValue : NSObject | ||
| + | ||
| +// Create a JSValue by converting an Objective-C object. | ||
| ++ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context; | ||
| +// Create a JavaScript value from an Objective-C primitive type. | ||
| ++ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context; | ||
| ++ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context; | ||
| ++ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context; | ||
| ++ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context; | ||
| +// Create a JavaScript value in this context. | ||
| ++ (JSValue *)valueWithNewObjectInContext:(JSContext *)context; | ||
| ++ (JSValue *)valueWithNewArrayInContext:(JSContext *)context; | ||
| ++ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context; | ||
| ++ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context; | ||
| ++ (JSValue *)valueWithNullInContext:(JSContext *)context; | ||
| ++ (JSValue *)valueWithUndefinedInContext:(JSContext *)context; | ||
| + | ||
| +// Convert this value to a corresponding Objective-C object, according to the | ||
| +// conversion specified above. | ||
| +- (id)toObject; | ||
| +// Convert this value to a corresponding Objective-C object, if the result is | ||
| +// not of the specified class then nil will be returned. | ||
| +- (id)toObjectOfClass:(Class)cls; | ||
| +// The value is copied to a boolean according to the conversion specified by the | ||
| +// JavaScript language. | ||
| +- (BOOL)toBool; | ||
| +// The value is copied to a number according to the conversion specified by the | ||
| +// JavaScript language. | ||
| +- (double)toDouble; | ||
| +// The value is copied to an integer according to the conversion specified by | ||
| +// the JavaScript language. | ||
| +- (int32_t)toInt32; | ||
| +// The value is copied to an integer according to the conversion specified by | ||
| +// the JavaScript language. | ||
| +- (uint32_t)toUInt32; | ||
| +// If the value is a boolean, a NSNumber value of @YES or @NO will be returned. | ||
| +// For all other types the value will be copied to a number according to the | ||
| +// conversion specified by the JavaScript language. | ||
| +- (NSNumber *)toNumber; | ||
| +// The value is copied to a string according to the conversion specified by the | ||
| +// JavaScript language. | ||
| +- (NSString *)toString; | ||
| +// The value is converted to a number representing a time interval since 1970, | ||
| +// and a new NSDate instance is returned. | ||
| +- (NSDate *)toDate; | ||
| +// If the value is null or undefined then nil is returned. | ||
| +// If the value is not an object then a JavaScript TypeError will be thrown. | ||
| +// The property "length" is read from the object, converted to an unsigned | ||
| +// integer, and an NSArray of this size is allocated. Properties corresponding | ||
| +// to indicies within the array bounds will be copied to the array, with | ||
| +// Objective-C objects converted to equivalent JSValues as specified. | ||
| +- (NSArray *)toArray; | ||
| +// If the value is null or undefined then nil is returned. | ||
| +// If the value is not an object then a JavaScript TypeError will be thrown. | ||
| +// All enumerable properties of the object are copied to the dictionary, with | ||
| +// Objective-C objects converted to equivalent JSValues as specified. | ||
| +- (NSDictionary *)toDictionary; | ||
| + | ||
| +// Access a property from the value. This method will return the JavaScript value | ||
| +// 'undefined' if the property does not exist. | ||
| +- (JSValue *)valueForProperty:(NSString *)property; | ||
| +// Set a property on the value. | ||
| +- (void)setValue:(id)value forProperty:(NSString *)property; | ||
| +// Delete a property from the value, returns YES if deletion is successful. | ||
| +- (BOOL)deleteProperty:(NSString *)property; | ||
| +// Returns YES if property is present on the value. | ||
| +// This method has the same function as the JavaScript operator "in". | ||
| +- (BOOL)hasProperty:(NSString *)property; | ||
| +// This method may be used to create a data or accessor property on an object; | ||
| +// this method operates in accordance with the Object.defineProperty method in | ||
| +// the JavaScript langauage. | ||
| +- (void)defineProperty:(NSString *)property descriptor:(id)descriptor; | ||
| + | ||
| +// Access an indexed property from the value. This method will return the | ||
| +// JavaScript value 'undefined' if no property exists at that index. | ||
| +- (JSValue *)valueAtIndex:(NSUInteger)index; | ||
| +// Set an indexed property on the value. | ||
| +- (void)setValue:(id)value atIndex:(NSUInteger)index; | ||
| + | ||
| +// All JavaScript values are precisely one of these types. | ||
| +- (BOOL)isUndefined; | ||
| +- (BOOL)isNull; | ||
| +- (BOOL)isBoolean; | ||
| +- (BOOL)isNumber; | ||
| +- (BOOL)isString; | ||
| +- (BOOL)isObject; | ||
| + | ||
| +// This method has the same function as the JavaScript operator "===". | ||
| +- (BOOL)isEqualToObject:(id)value; | ||
| +// This method has the same function as the JavaScript operator "==". | ||
| +- (BOOL)isEqualWithTypeCoercionToObject:(id)value; | ||
| +// This method has the same function as the JavaScript operator "instanceof". | ||
| +- (BOOL)isInstanceOf:(id)value; | ||
| + | ||
| +// Call this value as a function passing the specified "this" value and arguments. | ||
| +- (JSValue *)callWithArguments:(NSArray *)arguments; | ||
| +// Call this value as a constructor passing the specified arguments. | ||
| +- (JSValue *)constructWithArguments:(NSArray *)arguments; | ||
| +// Access the property named "method" from this value; call the value resulting | ||
| +// from the property access as a function, passing this value as the "this" | ||
| +// value, and the specified agruments. | ||
| +- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments; | ||
| + | ||
| +// The JSContext that this value originates from. | ||
| +@property(readonly, weak) JSContext *context; | ||
| + | ||
| +@end | ||
| + | ||
| +// Objective-C methods exported to JavaScript may have argument and/or return | ||
| +// values of struct types if the struct if conversion to and from the struct | ||
| +// is supported by JSValue. Support is provided for any types where JSValue | ||
| +// contains both a class method "valueWith<Type>:inContext:", and and instance | ||
| +// method "to<Type>" - where the string "<Type>" in these selector names match, | ||
| +// with the first argument to the former being of the same struct type as the | ||
| +// return type of the latter. | ||
| +// Support is provided for structs of type CGPoint, NSRange, CGRect and CGSize. | ||
| +@interface JSValue(StructSupport) | ||
| + | ||
| +// This method returns a newly allocated JavaScript object containing properties | ||
| +// named "x" and "y", with values from the CGPoint. | ||
| ++ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context; | ||
| +// This method returns a newly allocated JavaScript object containing properties | ||
| +// named "location" and "length", with values from the NSRange. | ||
| ++ (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context; | ||
| +// This method returns a newly allocated JavaScript object containing properties | ||
| +// named "x", "y", "width", and "height", with values from the CGRect. | ||
| ++ (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context; | ||
| +// This method returns a newly allocated JavaScript object containing properties | ||
| +// named "width" and "height", with values from the CGSize. | ||
| ++ (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context; | ||
| + | ||
| +// Convert a value to type CGPoint by reading properties named "x" and "y" from | ||
| +// this value, and converting the results to double. | ||
| +- (CGPoint)toPoint; | ||
| +// Convert a value to type NSRange by accessing properties named "location" and | ||
| +// "length" from this value converting the results to double. | ||
| +- (NSRange)toRange; | ||
| +// Convert a value to type CGRect by reading properties named "x", "y", "width", | ||
| +// and "height" from this value, and converting the results to double. | ||
| +- (CGRect)toRect; | ||
| +// Convert a value to type CGRange by accessing properties named "width" and | ||
| +// "height" from this value converting the results to double. | ||
| +- (CGSize)toSize; | ||
| + | ||
| +@end | ||
| + | ||
| +// Instances of JSValue implement the following methods in order to enable | ||
| +// support for subscript access by key and index, for example: | ||
| +// | ||
| +// JSValue *objectA, *objectB; | ||
| +// JSValue *v1 = object[@"X"]; // Get value for property "X" from 'object'. | ||
| +// JSValue *v2 = object[42]; // Get value for index 42 from 'object'. | ||
| +// object[@"Y"] = v1; // Assign 'v1' to property "Y" of 'object'. | ||
| +// object[101] = v2; // Assign 'v2' to index 101 of 'object'. | ||
| +// | ||
| +// An object key passed as a subscript will be converted to a JavaScipt value, | ||
| +// and then the value converted to a string used as a property name. | ||
| +@interface JSValue(SubscriptSupport) | ||
| + | ||
| +- (JSValue *)objectForKeyedSubscript:(id)key; | ||
| +- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index; | ||
| +- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key; | ||
| +- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index; | ||
| + | ||
| +@end | ||
| + | ||
| +#ifdef __cplusplus | ||
| +extern "C" { | ||
| +#endif | ||
| + | ||
| +// These keys may assist in creating a property descriptor for use with the | ||
| +// defineProperty method on JSValue. | ||
| +// Property descriptors must fit one of three descriptions: | ||
| +// Data Descriptor: | ||
| +// - A descriptor containing one or both of the keys "value" and "writable", | ||
| +// and optionally containing one or both of the keys "enumerable" and | ||
| +// "configurable". A data descriptor may not contain either the "get" or | ||
| +// "set" key. | ||
| +// A data descriptor may be used to create or modify the attributes of a | ||
| +// data property on an object (replacing any existing accessor property). | ||
| +// Accessor Descriptor: | ||
| +// - A descriptor containing one or both of the keys "get" and "set", and | ||
| +// optionally containing one or both of the keys "enumerable" and | ||
| +// "configurable". An accessor descriptor may not contain either the "value" | ||
| +// or "writable" key. | ||
| +// An accessor descriptor may be used to create or modify the attributes of | ||
| +// an accessor property on an object (replacing any existing data property). | ||
| +// Generic Descriptor: | ||
| +// - A descriptor containing one or both of the keys "enumerable" and | ||
| +// "configurable". A generic descriptor may not contain any of the keys | ||
| +// "value", " writable", "get", or "set". | ||
| +// A generic descriptor may be used to modify the attributes of an existing | ||
| +// data or accessor property, or to create a new data property. | ||
| +JS_EXPORT extern NSString * const JSPropertyDescriptorWritableKey; | ||
| +JS_EXPORT extern NSString * const JSPropertyDescriptorEnumerableKey; | ||
| +JS_EXPORT extern NSString * const JSPropertyDescriptorConfigurableKey; | ||
| +JS_EXPORT extern NSString * const JSPropertyDescriptorValueKey; | ||
| +JS_EXPORT extern NSString * const JSPropertyDescriptorGetKey; | ||
| +JS_EXPORT extern NSString * const JSPropertyDescriptorSetKey; | ||
| + | ||
| +#ifdef __cplusplus | ||
| +} // extern "C" | ||
| +#endif | ||
| + | ||
| +#endif | ||
| + |
8
Source/JavaScriptCore/API/JSBase.h
38
Source/JavaScriptCore/API/JSBlockAdaptor.h
| @@ -0,0 +1,38 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import <JavaScriptCore/JavaScriptCore.h> | ||
| +#import <wtf/RetainPtr.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +@interface JSBlockAdaptor : NSObject | ||
| + | ||
| +- (id)initWithBlockSignatureFromProtocol:(const char *)signature; | ||
| +- (id)blockFromValue:(JSValueRef)argument inContext:(JSContext *)context withException:(JSValueRef *)exception; | ||
| + | ||
| +@end | ||
| + | ||
| +#endif |
428
Source/JavaScriptCore/API/JSBlockAdaptor.mm
| @@ -0,0 +1,428 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#include "config.h" | ||
| +#import "JavaScriptCore.h" | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +#import "APICast.h" | ||
| +#import "APIShims.h" | ||
| +#import "Error.h" | ||
| +#import "JSBlockAdaptor.h" | ||
| +#import "JSContextInternal.h" | ||
| +#import "JSWrapperMap.h" | ||
| +#import "JSValue.h" | ||
| +#import "JSValueInternal.h" | ||
| +#import "ObjcRuntimeExtras.h" | ||
| +#import "wtf/OwnPtr.h" | ||
| + | ||
| +extern "C" { | ||
| +id __NSMakeSpecialForwardingCaptureBlock(const char *signature, void (^handler)(NSInvocation *inv)); | ||
| +}; | ||
| + | ||
| +class BlockArgument { | ||
| +public: | ||
| + virtual ~BlockArgument(); | ||
| + virtual JSValueRef get(NSInvocation*, NSInteger, JSContext*, JSValueRef*) = 0; | ||
| + | ||
| + OwnPtr<BlockArgument> m_next; | ||
| +}; | ||
| + | ||
| +BlockArgument::~BlockArgument() | ||
| +{ | ||
| +} | ||
| + | ||
| +class BlockArgumentBoolean : public BlockArgument { | ||
| +public: | ||
| + JSValueRef get(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef*) | ||
| + { | ||
| + bool value; | ||
| + [invocation getArgument:&value atIndex:argumentNumber]; | ||
| + return JSValueMakeBoolean(contextInternalContext(context), value); | ||
| + } | ||
| +}; | ||
| + | ||
| +template<typename T> | ||
| +class BlockArgumentNumeric : public BlockArgument { | ||
| +public: | ||
| + JSValueRef get(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef*) | ||
| + { | ||
| + T value; | ||
| + [invocation getArgument:&value atIndex:argumentNumber]; | ||
| + return JSValueMakeNumber(contextInternalContext(context), value); | ||
| + } | ||
| +}; | ||
| + | ||
| +class BlockArgumentId : public BlockArgument { | ||
| +public: | ||
| + JSValueRef get(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef*) | ||
| + { | ||
| + id value; | ||
| + [invocation getArgument:&value atIndex:argumentNumber]; | ||
| + return objectToValue(context, value); | ||
| + } | ||
| +}; | ||
| + | ||
| +class BlockArgumentStruct : public BlockArgument { | ||
| +public: | ||
| + BlockArgumentStruct(NSInvocation* conversionInvocation, const char* encodedType) | ||
| + : m_conversionInvocation(conversionInvocation) | ||
| + { | ||
| + [m_conversionInvocation retain]; | ||
| + NSUInteger size, alignment; | ||
| + NSGetSizeAndAlignment(encodedType, &size, &alignment); | ||
| + --alignment; | ||
| + m_allocation = (char*)malloc(size + alignment); | ||
| + m_buffer = reinterpret_cast<char*>((reinterpret_cast<intptr_t>(m_allocation) + alignment) & ~alignment); | ||
| + } | ||
| + | ||
| + virtual ~BlockArgumentStruct() | ||
| + { | ||
| + [m_conversionInvocation release]; | ||
| + free(m_allocation); | ||
| + } | ||
| + | ||
| + virtual JSValueRef get(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef*) | ||
| + { | ||
| + [invocation getArgument:m_allocation atIndex:argumentNumber]; | ||
| + | ||
| + [m_conversionInvocation setArgument:m_allocation atIndex:2]; | ||
| + [m_conversionInvocation setArgument:&context atIndex:3]; | ||
| + [m_conversionInvocation invokeWithTarget:[JSValue class]]; | ||
| + | ||
| + JSValue* value; | ||
| + [m_conversionInvocation getReturnValue:&value]; | ||
| + return valueInternalValue(value); | ||
| + } | ||
| + | ||
| +private: | ||
| + NSInvocation* m_conversionInvocation; | ||
| + char* m_buffer; | ||
| + char* m_allocation; | ||
| +}; | ||
| + | ||
| +class BlockArgumentTypeDelegate { | ||
| +public: | ||
| + typedef BlockArgument* ResultType; | ||
| + | ||
| + template<typename T> | ||
| + static ResultType typeInteger() | ||
| + { | ||
| + return new BlockArgumentNumeric<T>; | ||
| + } | ||
| + | ||
| + template<typename T> | ||
| + static ResultType typeDouble() | ||
| + { | ||
| + return new BlockArgumentNumeric<T>; | ||
| + } | ||
| + | ||
| + static ResultType typeBool() | ||
| + { | ||
| + return new BlockArgumentBoolean; | ||
| + } | ||
| + | ||
| + static ResultType typeVoid() | ||
| + { | ||
| + ASSERT_NOT_REACHED(); | ||
| + return nil; | ||
| + } | ||
| + | ||
| + static ResultType typeId() | ||
| + { | ||
| + return new BlockArgumentId; | ||
| + } | ||
| + | ||
| + static ResultType typeOfClass(const char*, const char*) | ||
| + { | ||
| + return new BlockArgumentId; | ||
| + } | ||
| + | ||
| + static ResultType typeBlock(const char*, const char*) | ||
| + { | ||
| + return nil; | ||
| + } | ||
| + | ||
| + static ResultType typeStruct(const char* begin, const char* end) | ||
| + { | ||
| + StringRange copy(begin, end); | ||
| + if (NSInvocation* invocation = valueToTypeInvocationFor(copy)) | ||
| + return new BlockArgumentStruct(invocation, copy); | ||
| + return nil; | ||
| + } | ||
| +}; | ||
| + | ||
| +class BlockResult { | ||
| +public: | ||
| + virtual ~BlockResult() | ||
| + { | ||
| + } | ||
| + | ||
| + virtual void set(NSInvocation*, JSContext*, JSValueRef, JSValueRef*) = 0; | ||
| +}; | ||
| + | ||
| +class BlockResultVoid : public BlockResult { | ||
| +public: | ||
| + void set(NSInvocation*, JSContext*, JSValueRef, JSValueRef*) | ||
| + { | ||
| + } | ||
| +}; | ||
| + | ||
| +template<typename T> | ||
| +class BlockResultInteger : public BlockResult { | ||
| +public: | ||
| + void set(NSInvocation* invocation, JSContext* context, JSValueRef result, JSValueRef* exception) | ||
| + { | ||
| + T value = (T)JSC::toInt32(JSValueToNumber(contextInternalContext(context), result, exception)); | ||
| + [invocation setReturnValue:&value]; | ||
| + } | ||
| +}; | ||
| + | ||
| +template<typename T> | ||
| +class BlockResultDouble : public BlockResult { | ||
| +public: | ||
| + void set(NSInvocation* invocation, JSContext* context, JSValueRef result, JSValueRef* exception) | ||
| + { | ||
| + T value = (T)JSValueToNumber(contextInternalContext(context), result, exception); | ||
| + [invocation setReturnValue:&value]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class BlockResultBoolean : public BlockResult { | ||
| +public: | ||
| + void set(NSInvocation* invocation, JSContext* context, JSValueRef result, JSValueRef*) | ||
| + { | ||
| + bool value = JSValueToBoolean(contextInternalContext(context), result); | ||
| + [invocation setReturnValue:&value]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class BlockResultStruct : public BlockResult { | ||
| +public: | ||
| + BlockResultStruct(NSInvocation* conversionInvocation, const char* encodedType) | ||
| + : m_conversionInvocation(conversionInvocation) | ||
| + { | ||
| + [m_conversionInvocation retain]; | ||
| + NSUInteger size, alignment; | ||
| + NSGetSizeAndAlignment(encodedType, &size, &alignment); | ||
| + --alignment; | ||
| + m_allocation = (char*)malloc(size + alignment); | ||
| + m_buffer = reinterpret_cast<char*>((reinterpret_cast<intptr_t>(m_allocation) + alignment) & ~alignment); | ||
| + } | ||
| + | ||
| + virtual ~BlockResultStruct() | ||
| + { | ||
| + [m_conversionInvocation release]; | ||
| + free(m_allocation); | ||
| + } | ||
| + | ||
| + void set(NSInvocation* invocation, JSContext* context, JSValueRef result, JSValueRef*) | ||
| + { | ||
| + JSValue* value = [JSValue valueWithValue:result inContext:context]; | ||
| + [m_conversionInvocation invokeWithTarget:value]; | ||
| + [m_conversionInvocation getReturnValue:m_buffer]; | ||
| + [invocation setReturnValue:&value]; | ||
| + } | ||
| + | ||
| +private: | ||
| + NSInvocation* m_conversionInvocation; | ||
| + char* m_buffer; | ||
| + char* m_allocation; | ||
| +}; | ||
| + | ||
| +@implementation JSBlockAdaptor { | ||
| + WTF::RetainPtr<NSString> m_signatureWithOffsets; | ||
| + WTF::RetainPtr<NSString> m_signatureWithoutOffsets; | ||
| + OwnPtr<BlockResult> m_result; | ||
| + OwnPtr<BlockArgument> m_arguments; | ||
| + size_t m_argumentCount; | ||
| +} | ||
| + | ||
| +// Helper function to add offset information back into a block signature. | ||
| +static NSString* buildBlockSignature(NSString* prefix, const char* begin, const char* end, NSUInteger& offset, NSString* postfix) | ||
| +{ | ||
| + StringRange copy(begin, end); | ||
| + NSString* result = [NSString stringWithFormat:@"%@%s%lu%@", prefix, copy.get(), offset, postfix]; | ||
| + NSUInteger size; | ||
| + NSGetSizeAndAlignment(copy, &size, 0); | ||
| + if (size < 4) | ||
| + size = 4; | ||
| + offset += size; | ||
| + return result; | ||
| +} | ||
| + | ||
| +- (id)initWithBlockSignatureFromProtocol:(const char*)encodedType | ||
| +{ | ||
| + [super init]; | ||
| + | ||
| + m_signatureWithoutOffsets = [NSString stringWithUTF8String:encodedType]; | ||
| + m_argumentCount = 0; | ||
| + | ||
| + // Currently only adapting JavaScript functions to blocks returning void. | ||
| + const char* resultTypeBegin = encodedType; | ||
| + if (*encodedType++ != 'v') | ||
| + return self; | ||
| + OwnPtr<BlockResult> result = adoptPtr(new BlockResultVoid); | ||
| + const char* resultTypeEnd = encodedType; | ||
| + | ||
| + if (encodedType[0] != '@' || encodedType[1] != '?') { | ||
| + return self; | ||
| + } | ||
| + encodedType += 2; | ||
| + | ||
| + // The first argument to a block is the block itself. | ||
| + NSString* signature = @"@?0"; | ||
| + NSUInteger offset = sizeof(void*); | ||
| + | ||
| + OwnPtr<BlockArgument> arguments = 0; | ||
| + OwnPtr<BlockArgument>* nextArgument = &arguments; | ||
| + while (*encodedType) { | ||
| + const char* begin = encodedType; | ||
| + OwnPtr<BlockArgument> argument = adoptPtr(parseObjCType<BlockArgumentTypeDelegate>(encodedType)); | ||
| + if (!argument) | ||
| + return self; | ||
| + const char* end = encodedType; | ||
| + | ||
| + // Append the type & stack offset of this argument to the signature string. | ||
| + signature = buildBlockSignature(signature, begin, end, offset, @""); | ||
| + | ||
| + *nextArgument = argument.release(); | ||
| + nextArgument = &(*nextArgument)->m_next; | ||
| + ++m_argumentCount; | ||
| + } | ||
| + | ||
| + // Prefix the signature with the return type & total stackframe size. | ||
| + // (this call will also add the return type size to the offset, | ||
| + // which is a nonsesne, but harmless since this is the last use). | ||
| + signature = buildBlockSignature(@"", resultTypeBegin, resultTypeEnd, offset, signature); | ||
| + | ||
| + m_signatureWithOffsets = signature; | ||
| + m_result = result.release(); | ||
| + m_arguments = arguments.release(); | ||
| + | ||
| + return self; | ||
| +} | ||
| + | ||
| +// The JSBlockAdaptor stores the type string taken from the protocol. This string | ||
| +// does not contain invocation layout information. The signature from the block | ||
| +// does contain the stackframe offsets. Skip these when comparing strings. | ||
| +- (bool)blockMatchesSignature:(id)block | ||
| +{ | ||
| + if (!_Block_has_signature(block)) | ||
| + return false; | ||
| + | ||
| + const char* withoutOffsets= [m_signatureWithoutOffsets UTF8String]; | ||
| + const char* signature = _Block_signature(block); | ||
| + | ||
| + while (true) { | ||
| + while (isdigit(*withoutOffsets)) | ||
| + ++withoutOffsets; | ||
| + while (isdigit(*signature)) | ||
| + ++signature; | ||
| + | ||
| + if (*withoutOffsets != *signature) | ||
| + return false; | ||
| + if (!*withoutOffsets) | ||
| + return true; | ||
| + ++withoutOffsets; | ||
| + ++signature; | ||
| + } | ||
| +} | ||
| + | ||
| +- (id)blockFromValue:(JSValueRef)argument inContext:(JSContext*)context withException:(JSValueRef*)exception | ||
| +{ | ||
| + JSGlobalContextRef ctx = contextInternalContext(context); | ||
| + | ||
| + // Check for null/undefined. | ||
| + if (JSValueIsUndefined(ctx, argument) || JSValueIsNull(ctx, argument)) | ||
| + return nil; | ||
| + | ||
| + JSObjectRef function = 0; | ||
| + if (id object = tryUnwrapObjcObject(ctx, argument)) { | ||
| + // Check if the argument is an Objective-C block. | ||
| + if ([object isKindOfClass:getNSBlockClass()]) { | ||
| + if ([self blockMatchesSignature:object]) | ||
| + return object; | ||
| + } | ||
| + } else if (m_signatureWithOffsets && JSValueIsObject(ctx, argument)) { | ||
| + // Check if the argument is a JavaScript function | ||
| + // (and that were able to create a forwarding block for this signature). | ||
| + JSObjectRef object = JSValueToObject(ctx, argument, exception); | ||
| + if (JSObjectIsFunction(ctx, object)) | ||
| + function = object; | ||
| + } | ||
| + | ||
| + if (!function) { | ||
| + JSC::APIEntryShim entryShim(toJS(ctx)); | ||
| + *exception = toRef(JSC::createTypeError(toJS(ctx), "Invalid argument supplied for Objective-C block")); | ||
| + return nil; | ||
| + } | ||
| + | ||
| + // Captured variables. | ||
| + JSBlockAdaptor* adaptor = self; | ||
| + JSValue* value = [JSValue valueWithValue:function inContext:context]; | ||
| + | ||
| + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=105895 | ||
| + // Currently only supporting void functions. | ||
| + return __NSMakeSpecialForwardingCaptureBlock([m_signatureWithOffsets UTF8String], ^(NSInvocation *invocation){ | ||
| + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=105894 | ||
| + // Rather than requiring that the context be retained, it would probably be more | ||
| + // appropriate to use a new JSContext instance (creating one if necessary). | ||
| + // It would be nice if we could throw a JS exception here, but without a context we have | ||
| + // nothing to work with. | ||
| + JSContext* context = value.context; | ||
| + if (!context) | ||
| + return; | ||
| + JSGlobalContextRef ctx = contextInternalContext(context); | ||
| + | ||
| + JSValueRef args[adaptor->m_argumentCount]; | ||
| + | ||
| + size_t argumentNumber = 0; | ||
| + BlockArgument* arguments = adaptor->m_arguments.get(); | ||
| + for (BlockArgument* argument = arguments; argument; argument = argument->m_next.get()) { | ||
| + JSValueRef exception = 0; | ||
| + args[argumentNumber] = argument->get(invocation, argumentNumber + 1, context, &exception); | ||
| + if (exception) { | ||
| + [context notifyException:exception]; | ||
| + return; | ||
| + } | ||
| + | ||
| + ++argumentNumber; | ||
| + } | ||
| + size_t argumentCount = argumentNumber; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectCallAsFunction(ctx, JSValueToObject(ctx, valueInternalValue(value), 0), 0, argumentCount, args, &exception); | ||
| + if (exception) { | ||
| + [context notifyException:exception]; | ||
| + return; | ||
| + } | ||
| + }); | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +#endif |
4
Source/JavaScriptCore/API/JSCallbackObjectFunctions.h
116
Source/JavaScriptCore/API/JSContext.h
| @@ -0,0 +1,116 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#include <JavaScriptCore/JavaScript.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +@class JSVirtualMachine, JSValue; | ||
| + | ||
| +// An instance of JSContext represents a JavaScript execution environment, all | ||
| +// JavaScript execution takes place within a context. | ||
| +// JSContext is also used to manage the life-cycle of objects within the | ||
| +// JavaScript virtual machine. Every instance of JSValue is associated with a | ||
| +// JSContext via a weak reference. The JSValue will keep the JavaScript value it | ||
| +// references alive so long as the JSContext is also retained. When an instance | ||
| +// of JSContext is deallocated and the weak references to it are cleared the | ||
| +// JSValues that had been associated with this context will be invalidated, and | ||
| +// will cease to keep the values within the JavaScript engine alive. | ||
| +NS_CLASS_AVAILABLE(10_9, NA) | ||
| +@interface JSContext : NSObject | ||
| + | ||
| +// Create a JSContext. | ||
| +- (id)init; | ||
| +// Create a JSContext in the specified virtual machine. | ||
| +- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine; | ||
| + | ||
| +// Evaluate a string of JavaScript code. | ||
| +- (JSValue *)evaluateScript:(NSString *)script; | ||
| + | ||
| +// This method retrieves the global object of the JavaScript execution context. | ||
| +// Instances of JSContext originating from WebKit will return a reference to the | ||
| +// WindowProxy object. | ||
| +- (JSValue *)globalObject; | ||
| + | ||
| +// This method may be called from within an Objective-C block or method invoked | ||
| +// as a callback from JavaScript to retrieve the callback's context. Outside of | ||
| +// a callback from JavaScript this method will return nil. | ||
| ++ (JSContext *)currentContext; | ||
| +// This method may be called from within an Objective-C block or method invoked | ||
| +// as a callback from JavaScript to retrieve the callback's this value. Outside | ||
| +// of a callback from JavaScript this method will return nil. | ||
| ++ (JSValue *)currentThis; | ||
| +// This method may be called from within an Objective-C block or method invoked | ||
| +// as a callback from JavaScript to retrieve the callback's arguments, objects | ||
| +// in the returned array are instances of JSValue. Outside of a callback from | ||
| +// JavaScript this method will return nil. | ||
| ++ (NSArray *)currentArguments; | ||
| + | ||
| +// The "exception" property may be used to throw an exception to JavaScript. | ||
| +// Before a callback is made from JavaScript to an Objective-C block or method, | ||
| +// the prior value of the exception property will be preserved and the property | ||
| +// will be set to nil. After the callback has completed the new value of the | ||
| +// exception property will be read, and prior value restored. If the new value | ||
| +// of exception is not nil, the callback will result in that value being thrown. | ||
| +// This property may also be used to check for uncaught exceptions arising from | ||
| +// API function calls (since the default behaviour of "exceptionHandler" is to | ||
| +// assign ant uncaught exception to this property). | ||
| +// If a JSValue orginating from a different JSVirtualMachine this this context | ||
| +// is assigned to this property, an Objective-C exception will be raised. | ||
| +@property(retain) JSValue *exception; | ||
| + | ||
| +// If a call to an API function results in an uncaught JavaScript exception, the | ||
| +// "exceptionHandler" block will be invoked. The default implementation for the | ||
| +// exception handler will store the exception to the exception property on | ||
| +// context. As a consequence the default behaviour is for unhandled exceptions | ||
| +// occuring within a callback from JavaScript to be rethrown upon return. | ||
| +// Setting this value to nil will result in all uncaught exceptions thrown from | ||
| +// the API being silently consumed. | ||
| +@property(copy) void(^exceptionHandler)(JSContext* context, JSValue *exception); | ||
| + | ||
| +// All instances of JSContext are associated with a single JSVirtualMachine. The | ||
| +// virtual machine provides an "object space" or set of execution resources. | ||
| +@property(readonly, retain) JSVirtualMachine *virtualMachine; | ||
| + | ||
| +@end | ||
| + | ||
| +// Instances of JSContext implement the following methods in order to enable | ||
| +// support for subscript access by key and index, for example: | ||
| +// | ||
| +// JSContext *context; | ||
| +// JSValue *v = context[@"X"]; // Get value for "X" from the global object. | ||
| +// context[@"Y"] = v; // Assign 'v' to "Y" on the global object. | ||
| +// | ||
| +// An object key passed as a subscript will be converted to a JavaScipt value, | ||
| +// and then the value converted to a string used to resolve a property of the | ||
| +// global object. | ||
| +@interface JSContext(SubscriptSupport) | ||
| + | ||
| +- (JSValue *)objectForKeyedSubscript:(id)key; | ||
| +- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key; | ||
| + | ||
| +@end | ||
| + | ||
| +#endif |
262
Source/JavaScriptCore/API/JSContext.mm
| @@ -0,0 +1,262 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#include "config.h" | ||
| + | ||
| +#import "APICast.h" | ||
| +#import "JSContextInternal.h" | ||
| +#import "JSGlobalObject.h" | ||
| +#import "JSValueInternal.h" | ||
| +#import "JSVirtualMachineInternal.h" | ||
| +#import "JSWrapperMap.h" | ||
| +#import "JavaScriptCore.h" | ||
| +#import "ObjcRuntimeExtras.h" | ||
| +#import <wtf/HashSet.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +typedef WTF::HashMap<JSValueRef, size_t> ProtectMap; | ||
| + | ||
| +@implementation JSContext { | ||
| + JSVirtualMachine *m_virtualMachine; | ||
| + JSGlobalContextRef m_context; | ||
| + JSWrapperMap *m_wrapperMap; | ||
| + ProtectMap m_protected; | ||
| +} | ||
| + | ||
| +@synthesize exception; | ||
| +@synthesize exceptionHandler; | ||
| + | ||
| +- (id)init | ||
| +{ | ||
| + return [self initWithVirtualMachine:[[[JSVirtualMachine alloc] init] autorelease]]; | ||
| +} | ||
| + | ||
| +- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine | ||
| +{ | ||
| + [super init]; | ||
| + | ||
| + m_virtualMachine = [virtualMachine retain]; | ||
| + m_context = JSGlobalContextCreateInGroup(getGroupFromVirtualMachine(virtualMachine), 0); | ||
| + m_wrapperMap = [[JSWrapperMap alloc] initWithContext:self]; | ||
| + | ||
| + self.exception = nil; | ||
| + self.exceptionHandler = ^(JSContext* context, JSValue *exceptionValue) { | ||
| + context.exception = exceptionValue; | ||
| + }; | ||
| + | ||
| + toJS(m_context)->lexicalGlobalObject()->m_apiData = self; | ||
| + return self; | ||
| +} | ||
| + | ||
| +- (JSValue *)evaluateScript:(NSString *)script | ||
| +{ | ||
| + JSValueRef exceptionValue = 0; | ||
| + JSStringRef scriptJS = JSStringCreateWithCFString((CFStringRef)script); | ||
| + JSValueRef result = JSEvaluateScript(m_context, scriptJS, 0, 0, 0, &exceptionValue); | ||
| + JSStringRelease(scriptJS); | ||
| + | ||
| + if (exceptionValue) | ||
| + return [self valueFromNotifyException:exceptionValue]; | ||
| + | ||
| + return [JSValue valueWithValue:result inContext:self]; | ||
| +} | ||
| + | ||
| +- (JSValue *)globalObject | ||
| +{ | ||
| + return [JSValue valueWithValue:JSContextGetGlobalObject(m_context) inContext:self]; | ||
| +} | ||
| + | ||
| ++ (JSContext *)currentContext | ||
| +{ | ||
| + WTFThreadData& threadData = wtfThreadData(); | ||
| + CallbackData *entry = (CallbackData *)threadData.m_apiData; | ||
| + return entry ? entry->context : nil; | ||
| +} | ||
| + | ||
| ++ (JSValue *)currentThis | ||
| +{ | ||
| + WTFThreadData& threadData = wtfThreadData(); | ||
| + CallbackData *entry = (CallbackData *)threadData.m_apiData; | ||
| + | ||
| + if (!entry->currentThis) | ||
| + entry->currentThis = [[JSValue alloc] initWithValue:entry->thisValue inContext:[JSContext currentContext]]; | ||
| + | ||
| + return entry->currentThis; | ||
| +} | ||
| + | ||
| ++ (NSArray *)currentArguments | ||
| +{ | ||
| + WTFThreadData& threadData = wtfThreadData(); | ||
| + CallbackData *entry = (CallbackData *)threadData.m_apiData; | ||
| + | ||
| + if (!entry->currentArguments) { | ||
| + JSContext *context = [JSContext currentContext]; | ||
| + size_t count = entry->argumentCount; | ||
| + JSValue * args[count]; | ||
| + for (size_t i =0; i < count; ++i) | ||
| + args[i] = [JSValue valueWithValue:entry->arguments[i] inContext:context]; | ||
| + entry->currentArguments = [[NSArray alloc] initWithObjects:args count:count]; | ||
| + } | ||
| + | ||
| + return entry->currentArguments; | ||
| +} | ||
| + | ||
| +- (JSVirtualMachine*)virtualMachine | ||
| +{ | ||
| + return m_virtualMachine; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +@implementation JSContext(SubscriptSupport) | ||
| + | ||
| +- (JSValue *)objectForKeyedSubscript:(id)key | ||
| +{ | ||
| + return [self globalObject][key]; | ||
| +} | ||
| + | ||
| +- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key | ||
| +{ | ||
| + [self globalObject][key] = object; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +@implementation JSContext(Internal) | ||
| + | ||
| +JSGlobalContextRef contextInternalContext(JSContext* context) | ||
| +{ | ||
| + return context->m_context; | ||
| +} | ||
| + | ||
| +- (void)dealloc | ||
| +{ | ||
| + toJS(m_context)->lexicalGlobalObject()->m_apiData = 0; | ||
| + | ||
| + ProtectMap::iterator iterator = m_protected.begin(); | ||
| + ProtectMap::iterator end = m_protected.end(); | ||
| + for (; iterator != end; ++iterator) | ||
| + JSValueUnprotect(m_context, iterator->key); | ||
| + | ||
| + [m_wrapperMap release]; | ||
| + JSGlobalContextRelease(m_context); | ||
| + [m_virtualMachine release]; | ||
| + [super dealloc]; | ||
| +} | ||
| + | ||
| +- (void)notifyException:(JSValueRef)exceptionValue | ||
| +{ | ||
| + self.exceptionHandler(self, [JSValue valueWithValue:exceptionValue inContext:self]); | ||
| +} | ||
| + | ||
| +- (JSValue *)valueFromNotifyException:(JSValueRef)exceptionValue | ||
| +{ | ||
| + [self notifyException:exceptionValue]; | ||
| + return [JSValue valueWithUndefinedInContext:self]; | ||
| +} | ||
| + | ||
| +- (BOOL)boolFromNotifyException:(JSValueRef)exceptionValue | ||
| +{ | ||
| + [self notifyException:exceptionValue]; | ||
| + return NO; | ||
| +} | ||
| + | ||
| +- (void)beginCallbackWithData:(CallbackData *)callbackData thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments | ||
| +{ | ||
| + WTFThreadData& threadData = wtfThreadData(); | ||
| + [self retain]; | ||
| + CallbackData *prevStack = (CallbackData *)threadData.m_apiData; | ||
| + *callbackData = (CallbackData){ prevStack, self, [self.exception retain], thisValue, nil, argumentCount, arguments, nil }; | ||
| + threadData.m_apiData = callbackData; | ||
| + self.exception = nil; | ||
| +} | ||
| + | ||
| +- (void)endCallbackWithData:(CallbackData *)callbackData | ||
| +{ | ||
| + WTFThreadData& threadData = wtfThreadData(); | ||
| + self.exception = callbackData->preservedException; | ||
| + [callbackData->preservedException release]; | ||
| + [callbackData->currentThis release]; | ||
| + [callbackData->currentArguments release]; | ||
| + threadData.m_apiData = callbackData->next; | ||
| + [self release]; | ||
| +} | ||
| + | ||
| +- (void)protect:(JSValueRef)value | ||
| +{ | ||
| + // Lock access to m_protected | ||
| + JSC::JSLockHolder lock(toJS(m_context)); | ||
| + | ||
| + ProtectMap::AddResult result = m_protected.add(value, 1); | ||
| + if (result.isNewEntry) | ||
| + JSValueProtect(m_context, value); | ||
| + else | ||
| + ++result.iterator->value; | ||
| +} | ||
| + | ||
| +- (void)unprotect:(JSValueRef)value | ||
| +{ | ||
| + // Lock access to m_protected | ||
| + JSC::JSLockHolder lock(toJS(m_context)); | ||
| + | ||
| + ProtectMap::iterator iterator = m_protected.find(value); | ||
| + if (iterator->value == 1) { | ||
| + m_protected.remove(value); | ||
| + JSValueUnprotect(m_context, value); | ||
| + } else | ||
| + --iterator->value; | ||
| +} | ||
| + | ||
| +- (JSValue *)wrapperForObject:(id)object | ||
| +{ | ||
| + // Lock access to m_wrapperMap | ||
| + JSC::JSLockHolder lock(toJS(m_context)); | ||
| + return [m_wrapperMap wrapperForObject:object]; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +WeakContextRef::WeakContextRef(JSContext *context) | ||
| +{ | ||
| + objc_initWeak(&m_weakContext, context); | ||
| +} | ||
| + | ||
| +WeakContextRef::~WeakContextRef() | ||
| +{ | ||
| + objc_destroyWeak(&m_weakContext); | ||
| +} | ||
| + | ||
| +JSContext * WeakContextRef::get() | ||
| +{ | ||
| + return objc_loadWeak(&m_weakContext); | ||
| +} | ||
| + | ||
| +void WeakContextRef::set(JSContext *context) | ||
| +{ | ||
| + objc_storeWeak(&m_weakContext, context); | ||
| +} | ||
| + | ||
| +#endif |
73
Source/JavaScriptCore/API/JSContextInternal.h
| @@ -0,0 +1,73 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import <JavaScriptCore/JavaScriptCore.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +#import "JSContext.h" | ||
| + | ||
| +struct CallbackData { | ||
| + CallbackData *next; | ||
| + JSContext *context; | ||
| + JSValue *preservedException; | ||
| + JSValueRef thisValue; | ||
| + JSValue *currentThis; | ||
| + size_t argumentCount; | ||
| + const JSValueRef *arguments; | ||
| + NSArray *currentArguments; | ||
| +}; | ||
| + | ||
| +class WeakContextRef { | ||
| +public: | ||
| + WeakContextRef(JSContext* = nil); | ||
| + ~WeakContextRef(); | ||
| + | ||
| + JSContext * get(); | ||
| + void set(JSContext*); | ||
| + | ||
| +private: | ||
| + JSContext *m_weakContext; | ||
| +}; | ||
| + | ||
| +@interface JSContext(Internal) | ||
| + | ||
| +JSGlobalContextRef contextInternalContext(JSContext *); | ||
| + | ||
| +- (void)notifyException:(JSValueRef)exception; | ||
| +- (JSValue *)valueFromNotifyException:(JSValueRef)exception; | ||
| +- (BOOL)boolFromNotifyException:(JSValueRef)exception; | ||
| + | ||
| +- (void)beginCallbackWithData:(CallbackData *)callbackData thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments; | ||
| +- (void)endCallbackWithData:(CallbackData *)callbackData; | ||
| + | ||
| +- (void)protect:(JSValueRef)value; | ||
| +- (void)unprotect:(JSValueRef)value; | ||
| + | ||
| +- (JSValue *)wrapperForObject:(id)object; | ||
| + | ||
| +@end | ||
| + | ||
| +#endif |
127
Source/JavaScriptCore/API/JSExport.h
| @@ -0,0 +1,127 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import <JavaScriptCore/JavaScriptCore.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +// When an JavaScript value is created from an instance of an Objective-C class | ||
| +// for which no copying conversion is specified a JavaScript wrapper object will | ||
| +// be created. | ||
| +// | ||
| +// In JavaScript inheritance is supported via a chain of prototype objects, and | ||
| +// for each Objective-C class (and per JSContext) an object appropriate for use | ||
| +// as a prototype will be provided. For the class NSObject the prototype object | ||
| +// will be the JavaScript context's Object Prototype. For all other Objective-C | ||
| +// classes a Prototype object will be created. The Prototype object for a given | ||
| +// Objective-C class will have its internal [Prototype] property set to point to | ||
| +// the Prototype object of the Objective-C class's superclass. As such the | ||
| +// prototype chain for a JavaScript wrapper object will reflect the wrapped | ||
| +// Objective-C type's inheritance hierarchy. | ||
| +// | ||
| +// In addition to the Prototype object a JavaScript Constructor object will also | ||
| +// be produced for each Objective-C class. The Constructor object has a property | ||
| +// named 'prototype' that references the Prototype object, and the Prototype | ||
| +// object has a property named 'constructor' that references the Constructor. | ||
| +// The Constructor object is not callable. | ||
| +// | ||
| +// By default no methods or properties of the Objective-C class will be exposed | ||
| +// to JavaScript, however methods and properties may explicitly be exported. | ||
| +// For each protocol that a class conforms to, if the protocol incorporates the | ||
| +// protocol JSExport, then the protocol will be interpreted as a list of methods | ||
| +// and properties to be exported to JavaScript. | ||
| +// | ||
| +// For each instance method being exported, a corresponding JavaScript function | ||
| +// will be assigned as a property of the Prototype object, for each Objective-C | ||
| +// property being exported a JavaScript accessor property will be created on the | ||
| +// Prototype, and for each class method exported a JavaScript function will be | ||
| +// created on the Constructor object. For example: | ||
| +// | ||
| +// @protocol MyClassJavaScriptMethods <JSExport> | ||
| +// - (void)foo; | ||
| +// @end | ||
| +// | ||
| +// @interface MyClass : NSObject <MyClassJavaScriptMethods> | ||
| +// - (void)foo; | ||
| +// - (void)bar; | ||
| +// @end | ||
| +// | ||
| +// If an instance of MyClass is converted to a JavaScript value, the resulting | ||
| +// wrapper object will (via its prototype) export the method "foo" to JavaScript, | ||
| +// since the class conforms to the MyClassJavaScriptExports protocol, and this | ||
| +// protocol incorporates JSExport. "bar" will not be exported. | ||
| +// | ||
| +// Properties, arguments, and return values of the following types are | ||
| +// supported: | ||
| +// | ||
| +// Primitive numbers: signed values of up to 32-bits are converted in a manner | ||
| +// consistent with valueWithInt32/toInt32, unsigned values of up to 32-bits | ||
| +// are converted in a manner consistent with valueWithUInt32/toUInt32, all | ||
| +// other numeric values are converted consistently with valueWithDouble/ | ||
| +// toDouble. | ||
| +// BOOL: values are converted consistently with valueWithBool/toBool. | ||
| +// id: values are converted consistently with valueWithObject/toObject. | ||
| +// <Objective-C Class>: - where the type is a pointer to a specified Objective-C | ||
| +// class, conversion is consistent with valueWithObjectOfClass/toObject. | ||
| +// struct types: C struct types are supported, where JSValue provides support | ||
| +// for the given type. Support is built in for CGPoint, NSRange, CGRect, and | ||
| +// CGSize; see JSValueStructSupport.h for information on how to add support | ||
| +// for additional types. | ||
| +// block types: In addition to support provided by valueWithObject/toObject for | ||
| +// block types, if a JavaScript Function is passed as an argument, where the | ||
| +// type required is a block with a void return value (and where the block's | ||
| +// arguments are all of supported types), then a special adaptor block | ||
| +// will be created, allowing the the JavaScript function to be used in the | ||
| +// place of a block. | ||
| +// | ||
| +// For any interface that conforms to JSExport the normal copying conversion for | ||
| +// built in types will be inhibited - so, for example, if an instance that | ||
| +// derives from NSString by conforms to JSExport is passed to valueWithObject:, | ||
| +// a wrapper object for the Objective-C object will be returned rather than a | ||
| +// JavaScript string primitive. | ||
| +@protocol JSExport | ||
| +@end | ||
| + | ||
| +// When a selector that takes one or more arguments is converted to a JavaScript | ||
| +// property name, by default a property name will be generated by performing the | ||
| +// following conversion: | ||
| +// - All semicolons are removed from the selector | ||
| +// - Any lowercase letter that had followed a semicolon will be capitalized. | ||
| +// Under the default conversion a selector "doFoo:withBar:" will be exported as | ||
| +// "doFooWithBar". The default conversion may be overriden using the JSExportAs | ||
| +// macro, for example to export a method "doFoo:withBar:" as "doFoo": | ||
| +// | ||
| +// @protocol MyClassJavaScriptMethods <JSExport> | ||
| +// JSExportAs(doFoo, | ||
| +// - (void)doFoo:(id)foo withBar:(id)bar | ||
| +// ); | ||
| +// @end | ||
| +// | ||
| +// Note that the JSExport macro may only be applied to a selector that takes one | ||
| +// or more argument. | ||
| +#define JSExportAs(PropertyName, Selector) \ | ||
| + @optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector | ||
| + | ||
| +#endif |
1,167
Source/JavaScriptCore/API/JSValue.mm
| @@ -0,0 +1,1167 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#include "config.h" | ||
| +#import "APICast.h" | ||
| +#import "APIShims.h" | ||
| +#import "DateInstance.h" | ||
| +#import "Error.h" | ||
| +#import "JavaScriptCore.h" | ||
| +#import "JSContextInternal.h" | ||
| +#import "JSVirtualMachineInternal.h" | ||
| +#import "JSValueInternal.h" | ||
| +#import "JSWrapperMap.h" | ||
| +#import "ObjcRuntimeExtras.h" | ||
| +#import "JSValue.h" | ||
| +#import "wtf/HashMap.h" | ||
| +#import "wtf/HashSet.h" | ||
| +#import "wtf/Vector.h" | ||
| +#import <wtf/TCSpinLock.h> | ||
| +#import "wtf/text/WTFString.h" | ||
| +#import <wtf/text/StringHash.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +NSString * const JSPropertyDescriptorWritableKey = @"writable"; | ||
| +NSString * const JSPropertyDescriptorEnumerableKey = @"enumerable"; | ||
| +NSString * const JSPropertyDescriptorConfigurableKey = @"configurable"; | ||
| +NSString * const JSPropertyDescriptorValueKey = @"value"; | ||
| +NSString * const JSPropertyDescriptorGetKey = @"get"; | ||
| +NSString * const JSPropertyDescriptorSetKey = @"set"; | ||
| + | ||
| +@implementation JSValue { | ||
| + JSValueRef m_value; | ||
| + JSContext *m_weakContext; | ||
| +} | ||
| + | ||
| ++ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:objectToValue(context, value) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:JSValueMakeBoolean(contextInternalContext(context), value) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:JSValueMakeNumber(contextInternalContext(context), value) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:JSValueMakeNumber(contextInternalContext(context), value) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:JSValueMakeNumber(contextInternalContext(context), value) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithNewObjectInContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:JSObjectMake(contextInternalContext(context), 0, 0) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithNewArrayInContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:JSObjectMakeArray(contextInternalContext(context), 0, NULL, 0) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context | ||
| +{ | ||
| + JSStringRef patternString = JSStringCreateWithCFString((CFStringRef)pattern); | ||
| + JSStringRef flagsString = JSStringCreateWithCFString((CFStringRef)flags); | ||
| + JSValueRef arguments[2] = { JSValueMakeString(contextInternalContext(context), patternString), JSValueMakeString(contextInternalContext(context), flagsString) }; | ||
| + JSStringRelease(patternString); | ||
| + JSStringRelease(flagsString); | ||
| + | ||
| + return [JSValue valueWithValue:JSObjectMakeRegExp(contextInternalContext(context), 2, arguments, 0) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context | ||
| +{ | ||
| + JSStringRef string = JSStringCreateWithCFString((CFStringRef)message); | ||
| + JSValueRef argument = JSValueMakeString(contextInternalContext(context), string); | ||
| + JSStringRelease(string); | ||
| + | ||
| + return [JSValue valueWithValue:JSObjectMakeError(contextInternalContext(context), 1, &argument, 0) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithNullInContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:JSValueMakeNull(contextInternalContext(context)) inContext:context]; | ||
| +} | ||
| ++ (JSValue *)valueWithUndefinedInContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithValue:JSValueMakeUndefined(contextInternalContext(context)) inContext:context]; | ||
| +} | ||
| + | ||
| +- (id)toObject | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + return valueToObject(context, m_value); | ||
| +} | ||
| +- (id)toObjectOfClass:(Class)cls | ||
| +{ | ||
| + id result = [self toObject]; | ||
| + return [result isKindOfClass:cls] ? result : nil; | ||
| +} | ||
| + | ||
| +- (BOOL)toBool | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + return (context && JSValueToBoolean(contextInternalContext(context), m_value)) ? YES : NO; | ||
| +} | ||
| +- (double)toDouble | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return std::numeric_limits<double>::quiet_NaN(); | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + double result = JSValueToNumber(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) { | ||
| + [context notifyException:exception]; | ||
| + return std::numeric_limits<double>::quiet_NaN(); | ||
| + } | ||
| + | ||
| + return result; | ||
| +} | ||
| +- (int32_t)toInt32 | ||
| +{ | ||
| + return JSC::toInt32([self toDouble]); | ||
| +} | ||
| +- (uint32_t)toUInt32 | ||
| +{ | ||
| + return JSC::toUInt32([self toDouble]); | ||
| +} | ||
| +- (NSNumber *)toNumber | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + id result = valueToNumber(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + [context notifyException:exception]; | ||
| + return result; | ||
| +} | ||
| +- (NSString *)toString | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + id result = valueToString(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + [context notifyException:exception]; | ||
| + return result; | ||
| +} | ||
| +- (NSDate *)toDate | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + id result = valueToDate(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + [context notifyException:exception]; | ||
| + return result; | ||
| +} | ||
| +- (NSArray *)toArray | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + id result = valueToArray(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + [context notifyException:exception]; | ||
| + return result; | ||
| +} | ||
| +- (NSDictionary *)toDictionary | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + id result = valueToDictionary(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + [context notifyException:exception]; | ||
| + return result; | ||
| +} | ||
| + | ||
| +- (JSValue *)valueForProperty:(NSString *)propertyName | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName); | ||
| + JSValueRef result = JSObjectGetProperty(contextInternalContext(context), object, name, &exception); | ||
| + JSStringRelease(name); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + return [JSValue valueWithValue:result inContext:context]; | ||
| +} | ||
| +- (void)setValue:(id)value forProperty:(NSString *)propertyName | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) { | ||
| + [context notifyException:exception]; | ||
| + return; | ||
| + } | ||
| + | ||
| + JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName); | ||
| + JSObjectSetProperty(contextInternalContext(context), object, name, objectToValue(context, value), 0, &exception); | ||
| + JSStringRelease(name); | ||
| + if (exception) { | ||
| + [context notifyException:exception]; | ||
| + return; | ||
| + } | ||
| +} | ||
| +- (BOOL)deleteProperty:(NSString *)propertyName | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return NO; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + return [context boolFromNotifyException:exception]; | ||
| + | ||
| + JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName); | ||
| + BOOL result = JSObjectDeleteProperty(contextInternalContext(context), object, name, &exception) ? YES : NO; | ||
| + JSStringRelease(name); | ||
| + if (exception) | ||
| + return [context boolFromNotifyException:exception]; | ||
| + | ||
| + return result; | ||
| +} | ||
| +- (BOOL)hasProperty:(NSString *)propertyName | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return NO; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + return [context boolFromNotifyException:exception]; | ||
| + | ||
| + JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName); | ||
| + BOOL result = JSObjectHasProperty(contextInternalContext(context), object, name) ? YES : NO; | ||
| + JSStringRelease(name); | ||
| + return result; | ||
| +} | ||
| +- (void)defineProperty:(NSString *)property descriptor:(id)descriptor | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return; | ||
| + [[context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, property, descriptor ]]; | ||
| +} | ||
| + | ||
| +- (JSValue *)valueAtIndex:(NSUInteger)index | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + if (index != (unsigned)index) | ||
| + [self valueForProperty:[[JSValue valueWithDouble:index inContext:context] toString]]; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + JSValueRef result = JSObjectGetPropertyAtIndex(contextInternalContext(context), object, (unsigned)index, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + return [JSValue valueWithValue:result inContext:context]; | ||
| +} | ||
| +- (void)setValue:(id)value atIndex:(NSUInteger)index | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return; | ||
| + | ||
| + if (index != (unsigned)index) | ||
| + return [self setValue:value forProperty:[[JSValue valueWithDouble:index inContext:context] toString]]; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) { | ||
| + [context notifyException:exception]; | ||
| + return; | ||
| + } | ||
| + | ||
| + JSObjectSetPropertyAtIndex(contextInternalContext(context), object, (unsigned)index, objectToValue(context, value), &exception); | ||
| + if (exception) { | ||
| + [context notifyException:exception]; | ||
| + return; | ||
| + } | ||
| +} | ||
| + | ||
| +- (BOOL)isUndefined | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + return (context && JSValueIsUndefined(contextInternalContext(context), m_value)) ? YES : NO; | ||
| +} | ||
| +- (BOOL)isNull | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + return (context && JSValueIsNull(contextInternalContext(context), m_value)) ? YES : NO; | ||
| +} | ||
| +- (BOOL)isBoolean | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + return (context && JSValueIsBoolean(contextInternalContext(context), m_value)) ? YES : NO; | ||
| +} | ||
| +- (BOOL)isNumber | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + return (context && JSValueIsNumber(contextInternalContext(context), m_value)) ? YES : NO; | ||
| +} | ||
| +- (BOOL)isString | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + return (context && JSValueIsString(contextInternalContext(context), m_value)) ? YES : NO; | ||
| +} | ||
| +- (BOOL)isObject | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + return (context && JSValueIsObject(contextInternalContext(context), m_value)) ? YES : NO; | ||
| +} | ||
| + | ||
| +- (BOOL)isEqualToObject:(id)value | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return NO; | ||
| + | ||
| + return JSValueIsStrictEqual(contextInternalContext(context), m_value, objectToValue(context, value)) ? YES : NO; | ||
| +} | ||
| +- (BOOL)isEqualWithTypeCoercionToObject:(id)value | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return NO; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + BOOL result = JSValueIsEqual(contextInternalContext(context), m_value, objectToValue(context, value), &exception) ? YES : NO; | ||
| + if (exception) | ||
| + return [context boolFromNotifyException:exception]; | ||
| + | ||
| + return result; | ||
| +} | ||
| +- (BOOL)isInstanceOf:(id)value | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return NO; | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef constructor = JSValueToObject(contextInternalContext(context), objectToValue(context, value), &exception); | ||
| + if (exception) | ||
| + return [context boolFromNotifyException:exception]; | ||
| + | ||
| + BOOL result = JSValueIsInstanceOfConstructor(contextInternalContext(context), m_value, constructor, &exception) ? YES : NO; | ||
| + if (exception) | ||
| + return [context boolFromNotifyException:exception]; | ||
| + | ||
| + return result; | ||
| +} | ||
| + | ||
| +- (JSValue *)callWithArguments:(NSArray *)args | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + NSUInteger argumentCount = [args count]; | ||
| + JSValueRef arguments[argumentCount]; | ||
| + for (unsigned i = 0; i < argumentCount; ++i) | ||
| + arguments[i] = objectToValue(context, [args objectAtIndex:i]); | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + JSValueRef result = JSObjectCallAsFunction(contextInternalContext(context), object, 0, argumentCount, arguments, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + return [JSValue valueWithValue:result inContext:context]; | ||
| +} | ||
| +- (JSValue *)constructWithArguments:(NSArray *)args | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + NSUInteger argumentCount = [args count]; | ||
| + JSValueRef arguments[argumentCount]; | ||
| + for (unsigned i = 0; i < argumentCount; ++i) | ||
| + arguments[i] = objectToValue(context, [args objectAtIndex:i]); | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + JSObjectRef result = JSObjectCallAsConstructor(contextInternalContext(context), object, argumentCount, arguments, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + return [JSValue valueWithValue:result inContext:context]; | ||
| +} | ||
| +- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + NSUInteger argumentCount = [arguments count]; | ||
| + JSValueRef args[argumentCount]; | ||
| + for (unsigned i = 0; i < argumentCount; ++i) | ||
| + args[i] = objectToValue(context, [arguments objectAtIndex:i]); | ||
| + | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef thisObject = JSValueToObject(contextInternalContext(context), m_value, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + JSStringRef name = JSStringCreateWithCFString((CFStringRef)method); | ||
| + JSValueRef func = JSObjectGetProperty(contextInternalContext(context), thisObject, name, &exception); | ||
| + JSStringRelease(name); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + JSObjectRef object = JSValueToObject(contextInternalContext(context), func, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + JSValueRef result = JSObjectCallAsFunction(contextInternalContext(context), object, thisObject, argumentCount, args, &exception); | ||
| + if (exception) | ||
| + return [context valueFromNotifyException:exception]; | ||
| + | ||
| + return [JSValue valueWithValue:result inContext:context]; | ||
| +} | ||
| + | ||
| +- (JSContext *)context | ||
| +{ | ||
| + return objc_loadWeak(&m_weakContext); | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +@implementation JSValue(StructSupport) | ||
| + | ||
| +- (CGPoint)toPoint | ||
| +{ | ||
| + return (CGPoint){ | ||
| + [self[@"x"] toDouble], | ||
| + [self[@"y"] toDouble] | ||
| + }; | ||
| +} | ||
| + | ||
| +- (NSRange)toRange | ||
| +{ | ||
| + return (NSRange){ | ||
| + (NSUInteger)[self[@"location"] toDouble], | ||
| + (NSUInteger)[self[@"length"] toDouble] | ||
| + }; | ||
| +} | ||
| + | ||
| +- (CGRect)toRect | ||
| +{ | ||
| + return (CGRect){ | ||
| + [self toPoint], | ||
| + [self toSize] | ||
| + }; | ||
| +} | ||
| + | ||
| +- (CGSize)toSize | ||
| +{ | ||
| + return (CGSize){ | ||
| + [self[@"width"] toDouble], | ||
| + [self[@"height"] toDouble] | ||
| + }; | ||
| +} | ||
| + | ||
| ++ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithObject:@{ | ||
| + @"x":[NSNumber numberWithFloat:point.x], | ||
| + @"y":[NSNumber numberWithFloat:point.y] | ||
| + } inContext:context]; | ||
| +} | ||
| + | ||
| ++ (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithObject:@{ | ||
| + @"location":[NSNumber numberWithFloat:range.location], | ||
| + @"length":[NSNumber numberWithFloat:range.length] | ||
| + } inContext:context]; | ||
| +} | ||
| + | ||
| ++ (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithObject:@{ | ||
| + @"x":[NSNumber numberWithFloat:rect.origin.x], | ||
| + @"y":[NSNumber numberWithFloat:rect.origin.y], | ||
| + @"width":[NSNumber numberWithFloat:rect.size.width], | ||
| + @"height":[NSNumber numberWithFloat:rect.size.height] | ||
| + } inContext:context]; | ||
| +} | ||
| + | ||
| ++ (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context | ||
| +{ | ||
| + return [JSValue valueWithObject:@{ | ||
| + @"width":[NSNumber numberWithFloat:size.width], | ||
| + @"height":[NSNumber numberWithFloat:size.height] | ||
| + } inContext:context]; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +@implementation JSValue(SubscriptSupport) | ||
| + | ||
| +- (JSValue *)objectForKeyedSubscript:(id)key | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + if (![(NSObject*)key isKindOfClass:[NSString class]]) { | ||
| + key = [[JSValue valueWithObject:key inContext:context] toString]; | ||
| + if (!key) | ||
| + return [JSValue valueWithUndefinedInContext:context]; | ||
| + } | ||
| + | ||
| + return [self valueForProperty:(NSString *)key]; | ||
| +} | ||
| + | ||
| +- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index | ||
| +{ | ||
| + return [self valueAtIndex:index]; | ||
| +} | ||
| + | ||
| +- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return; | ||
| + | ||
| + if (![(NSObject*)key isKindOfClass:[NSString class]]) { | ||
| + key = [[JSValue valueWithObject:key inContext:context] toString]; | ||
| + if (!key) | ||
| + return; | ||
| + } | ||
| + | ||
| + [self setValue:object forProperty:(NSString *)key]; | ||
| +} | ||
| + | ||
| +- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index | ||
| +{ | ||
| + [self setValue:object atIndex:index]; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +inline bool isDate(JSObjectRef object, JSGlobalContextRef context) | ||
| +{ | ||
| + JSC::APIEntryShim entryShim(toJS(context)); | ||
| + return toJS(object)->inherits(&JSC::DateInstance::s_info); | ||
| +} | ||
| + | ||
| +inline bool isArray(JSObjectRef object, JSGlobalContextRef context) | ||
| +{ | ||
| + JSC::APIEntryShim entryShim(toJS(context)); | ||
| + return toJS(object)->inherits(&JSC::JSArray::s_info); | ||
| +} | ||
| + | ||
| +@implementation JSValue(Internal) | ||
| + | ||
| +enum ConversionType { | ||
| + ContainerNone, | ||
| + ContainerArray, | ||
| + ContainerDictionary | ||
| +}; | ||
| + | ||
| +class JSContainerConvertor { | ||
| +public: | ||
| + struct Task { | ||
| + JSValueRef js; | ||
| + id objc; | ||
| + ConversionType type; | ||
| + }; | ||
| + | ||
| + JSContainerConvertor(JSGlobalContextRef context) | ||
| + : m_context(context) | ||
| + { | ||
| + } | ||
| + | ||
| + id convert(JSValueRef property); | ||
| + void add(Task task); | ||
| + Task take(); | ||
| + bool isWorkListEmpty() { return !m_worklist.size(); } | ||
| + | ||
| +private: | ||
| + JSGlobalContextRef m_context; | ||
| + WTF::HashMap<JSValueRef, id> m_objectMap; | ||
| + WTF::Vector<Task> m_worklist; | ||
| +}; | ||
| + | ||
| +inline id JSContainerConvertor::convert(JSValueRef value) | ||
| +{ | ||
| + WTF::HashMap<JSValueRef, id>::iterator iter = m_objectMap.find(value); | ||
| + if (iter != m_objectMap.end()) | ||
| + return iter->value; | ||
| + | ||
| + Task result = valueToObjectWithoutCopy(m_context, value); | ||
| + if (result.js) | ||
| + add(result); | ||
| + return result.objc; | ||
| +} | ||
| + | ||
| +void JSContainerConvertor::add(Task task) | ||
| +{ | ||
| + m_objectMap.add(task.js, task.objc); | ||
| + if (task.type != ContainerNone) | ||
| + m_worklist.append(task); | ||
| +} | ||
| + | ||
| +JSContainerConvertor::Task JSContainerConvertor::take() | ||
| +{ | ||
| + ASSERT(!isWorkListEmpty()); | ||
| + Task last = m_worklist.last(); | ||
| + m_worklist.removeLast(); | ||
| + return last; | ||
| +} | ||
| + | ||
| +static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef context, JSValueRef value) | ||
| +{ | ||
| + if (!JSValueIsObject(context, value)) { | ||
| + id primitive; | ||
| + if (JSValueIsBoolean(context, value)) | ||
| + primitive = JSValueToBoolean(context, value) ? @YES : @NO; | ||
| + else if (JSValueIsNumber(context, value)) { | ||
| + // Normalize the number, so it will unique correctly in the hash map - | ||
| + // it's nicer not to leak this internal implementation detail! | ||
| + value = JSValueMakeNumber(context, JSValueToNumber(context, value, 0)); | ||
| + primitive = [NSNumber numberWithDouble:JSValueToNumber(context, value, 0)]; | ||
| + } else if (JSValueIsString(context, value)) { | ||
| + // Would be nice to unique strings, too. | ||
| + JSStringRef jsstring = JSValueToStringCopy(context, value, 0); | ||
| + NSString * stringNS = (NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring); | ||
| + JSStringRelease(jsstring); | ||
| + primitive = [stringNS autorelease]; | ||
| + } else if (JSValueIsNull(context, value)) | ||
| + primitive = [NSNull null]; | ||
| + else { | ||
| + ASSERT(JSValueIsUndefined(context, value)); | ||
| + primitive = nil; | ||
| + } | ||
| + return (JSContainerConvertor::Task){ value, primitive, ContainerNone }; | ||
| + } | ||
| + | ||
| + JSObjectRef object = JSValueToObject(context, value, 0); | ||
| + | ||
| + if (id wrapped = tryUnwrapObjcObject(context, object)) | ||
| + return (JSContainerConvertor::Task){ object, wrapped, ContainerNone }; | ||
| + | ||
| + if (isDate(object, context)) | ||
| + return (JSContainerConvertor::Task){ object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0)], ContainerNone }; | ||
| + | ||
| + if (isArray(object, context)) | ||
| + return (JSContainerConvertor::Task){ object, [NSMutableArray array], ContainerArray }; | ||
| + | ||
| + return (JSContainerConvertor::Task){ object, [NSMutableDictionary dictionary], ContainerDictionary }; | ||
| +} | ||
| + | ||
| +static id containerValueToObject(JSGlobalContextRef context, JSContainerConvertor::Task task) | ||
| +{ | ||
| + ASSERT(task.type != ContainerNone); | ||
| + JSContainerConvertor convertor(context); | ||
| + convertor.add(task); | ||
| + ASSERT(!convertor.isWorkListEmpty()); | ||
| + | ||
| + do { | ||
| + JSContainerConvertor::Task current = task = convertor.take(); | ||
| + ASSERT(JSValueIsObject(context, current.js)); | ||
| + JSObjectRef js = JSValueToObject(context, current.js, 0); | ||
| + | ||
| + if (current.type == ContainerArray) { | ||
| + ASSERT([current.objc isKindOfClass:[NSMutableArray class]]); | ||
| + NSMutableArray *array = (NSMutableArray *)current.objc; | ||
| + | ||
| + JSStringRef lengthString = JSStringCreateWithUTF8CString("length"); | ||
| + unsigned length = JSC::toUInt32(JSValueToNumber(context, JSObjectGetProperty(context, js, lengthString, 0), 0)); | ||
| + JSStringRelease(lengthString); | ||
| + | ||
| + for (unsigned i = 0; i < length; ++i) { | ||
| + id objc = convertor.convert(JSObjectGetPropertyAtIndex(context, js, i, 0)); | ||
| + [array addObject:objc ? objc : [NSNull null]]; | ||
| + } | ||
| + } else { | ||
| + ASSERT([current.objc isKindOfClass:[NSMutableDictionary class]]); | ||
| + NSMutableDictionary *dictionary = (NSMutableDictionary *)current.objc; | ||
| + | ||
| + JSPropertyNameArrayRef propertyNameArray = JSObjectCopyPropertyNames(context, js); | ||
| + size_t length = JSPropertyNameArrayGetCount(propertyNameArray); | ||
| + | ||
| + for (size_t i = 0; i < length; ++i) { | ||
| + JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNameArray, i); | ||
| + if (id objc = convertor.convert(JSObjectGetProperty(context, js, propertyName, 0))) | ||
| + dictionary[[(NSString *)JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]] = objc; | ||
| + } | ||
| + | ||
| + JSPropertyNameArrayRelease(propertyNameArray); | ||
| + } | ||
| + | ||
| + } while (!convertor.isWorkListEmpty()); | ||
| + | ||
| + return task.objc; | ||
| +} | ||
| + | ||
| +id valueToObject(JSContext *context, JSValueRef value) | ||
| +{ | ||
| + JSContainerConvertor::Task result = valueToObjectWithoutCopy(contextInternalContext(context), value); | ||
| + if (result.type == ContainerNone) | ||
| + return result.objc; | ||
| + return containerValueToObject(contextInternalContext(context), result); | ||
| +} | ||
| + | ||
| +id valueToNumber(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception) | ||
| +{ | ||
| + ASSERT(!*exception); | ||
| + if (id wrapped = tryUnwrapObjcObject(context, value)) { | ||
| + if ([wrapped isKindOfClass:[NSNumber class]]) | ||
| + return wrapped; | ||
| + } | ||
| + | ||
| + if (JSValueIsBoolean(context, value)) | ||
| + return JSValueToBoolean(context, value) ? @YES : @NO; | ||
| + | ||
| + double result = JSValueToNumber(context, value, exception); | ||
| + return [NSNumber numberWithDouble:*exception ? std::numeric_limits<double>::quiet_NaN() : result]; | ||
| +} | ||
| +id valueToString(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception) | ||
| +{ | ||
| + ASSERT(!*exception); | ||
| + if (id wrapped = tryUnwrapObjcObject(context, value)) { | ||
| + if ([wrapped isKindOfClass:[NSString class]]) | ||
| + return wrapped; | ||
| + } | ||
| + | ||
| + JSStringRef jsstring = JSValueToStringCopy(context, value, exception); | ||
| + if (*exception) { | ||
| + ASSERT(!jsstring); | ||
| + return nil; | ||
| + } | ||
| + | ||
| + NSString* stringNS = [(NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring) autorelease]; | ||
| + JSStringRelease(jsstring); | ||
| + return stringNS; | ||
| +} | ||
| +id valueToDate(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception) | ||
| +{ | ||
| + ASSERT(!*exception); | ||
| + if (id wrapped = tryUnwrapObjcObject(context, value)) { | ||
| + if ([wrapped isKindOfClass:[NSDate class]]) | ||
| + return wrapped; | ||
| + } | ||
| + | ||
| + double result = JSValueToNumber(context, value, exception); | ||
| + return *exception ? nil : [NSDate dateWithTimeIntervalSince1970:result]; | ||
| +} | ||
| +id valueToArray(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception) | ||
| +{ | ||
| + ASSERT(!*exception); | ||
| + if (id wrapped = tryUnwrapObjcObject(context, value)) { | ||
| + if ([wrapped isKindOfClass:[NSArray class]]) | ||
| + return wrapped; | ||
| + } | ||
| + | ||
| + if (JSValueIsObject(context, value)) | ||
| + return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableArray array], ContainerArray}); | ||
| + | ||
| + if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value))) | ||
| + *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSArray")); | ||
| + return nil; | ||
| +} | ||
| +id valueToDictionary(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception) | ||
| +{ | ||
| + ASSERT(!*exception); | ||
| + if (id wrapped = tryUnwrapObjcObject(context, value)) { | ||
| + if ([wrapped isKindOfClass:[NSDictionary class]]) | ||
| + return wrapped; | ||
| + } | ||
| + | ||
| + if (JSValueIsObject(context, value)) | ||
| + return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableDictionary dictionary], ContainerDictionary}); | ||
| + | ||
| + if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value))) | ||
| + *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSDictionary")); | ||
| + return nil; | ||
| +} | ||
| + | ||
| +class ObjcContainerConvertor { | ||
| +public: | ||
| + struct Task { | ||
| + id objc; | ||
| + JSValueRef js; | ||
| + ConversionType type; | ||
| + }; | ||
| + | ||
| + ObjcContainerConvertor(JSContext *context) | ||
| + : m_context(context) | ||
| + { | ||
| + } | ||
| + | ||
| + JSValueRef convert(id object); | ||
| + void add(Task task); | ||
| + Task take(); | ||
| + bool isWorkListEmpty() { return !m_worklist.size(); } | ||
| + | ||
| +private: | ||
| + JSContext *m_context; | ||
| + WTF::HashMap<id, JSValueRef> m_objectMap; | ||
| + WTF::Vector<Task> m_worklist; | ||
| +}; | ||
| + | ||
| +JSValueRef ObjcContainerConvertor::convert(id object) | ||
| +{ | ||
| + ASSERT(object); | ||
| + | ||
| + auto it = m_objectMap.find(object); | ||
| + if (it != m_objectMap.end()) | ||
| + return it->value; | ||
| + | ||
| + ObjcContainerConvertor::Task task = objectToValueWithoutCopy(m_context, object); | ||
| + add(task); | ||
| + return task.js; | ||
| +} | ||
| + | ||
| +void ObjcContainerConvertor::add(ObjcContainerConvertor::Task task) | ||
| +{ | ||
| + m_objectMap.add(task.objc, task.js); | ||
| + if (task.type != ContainerNone) | ||
| + m_worklist.append(task); | ||
| +} | ||
| + | ||
| +ObjcContainerConvertor::Task ObjcContainerConvertor::take() | ||
| +{ | ||
| + ASSERT(!isWorkListEmpty()); | ||
| + Task last = m_worklist.last(); | ||
| + m_worklist.removeLast(); | ||
| + return last; | ||
| +} | ||
| + | ||
| +static ObjcContainerConvertor::Task objectToValueWithoutCopy(JSContext *context, id object) | ||
| +{ | ||
| + if (!object) | ||
| + return (ObjcContainerConvertor::Task){ object, JSValueMakeUndefined(contextInternalContext(context)), ContainerNone }; | ||
| + | ||
| + if (!class_conformsToProtocol(object_getClass(object), getJSExportProtocol())) { | ||
| + if ([object isKindOfClass:[NSArray class]]) | ||
| + return (ObjcContainerConvertor::Task){ object, JSObjectMakeArray(contextInternalContext(context), 0, NULL, 0), ContainerArray }; | ||
| + | ||
| + if ([object isKindOfClass:[NSDictionary class]]) | ||
| + return (ObjcContainerConvertor::Task){ object, JSObjectMake(contextInternalContext(context), 0, 0), ContainerDictionary }; | ||
| + | ||
| + if ([object isKindOfClass:[NSNull class]]) | ||
| + return (ObjcContainerConvertor::Task){ object, JSValueMakeNull(contextInternalContext(context)), ContainerNone }; | ||
| + | ||
| + if ([object isKindOfClass:[JSValue class]]) | ||
| + return (ObjcContainerConvertor::Task){ object, ((JSValue *)object)->m_value, ContainerNone }; | ||
| + | ||
| + if ([object isKindOfClass:[NSArray class]]) | ||
| + return (ObjcContainerConvertor::Task){ object, JSObjectMakeArray(contextInternalContext(context), 0, NULL, 0), ContainerArray }; | ||
| + | ||
| + if ([object isKindOfClass:[NSDictionary class]]) | ||
| + return (ObjcContainerConvertor::Task){ object, JSObjectMake(contextInternalContext(context), 0, 0), ContainerDictionary }; | ||
| + | ||
| + if ([object isKindOfClass:[NSString class]]) { | ||
| + JSStringRef string = JSStringCreateWithCFString((CFStringRef)object); | ||
| + JSValueRef js = JSValueMakeString(contextInternalContext(context), string); | ||
| + JSStringRelease(string); | ||
| + return (ObjcContainerConvertor::Task){ object, js, ContainerNone }; | ||
| + } | ||
| + | ||
| + if ([object isKindOfClass:[NSNumber class]]) { | ||
| + id literalTrue = @YES; | ||
| + if (object == literalTrue) | ||
| + return (ObjcContainerConvertor::Task){ object, JSValueMakeBoolean(contextInternalContext(context), true), ContainerNone }; | ||
| + id literalFalse = @NO; | ||
| + if (object == literalFalse) | ||
| + return (ObjcContainerConvertor::Task){ object, JSValueMakeBoolean(contextInternalContext(context), false), ContainerNone }; | ||
| + return (ObjcContainerConvertor::Task){ object, JSValueMakeNumber(contextInternalContext(context), [(NSNumber*)object doubleValue]), ContainerNone }; | ||
| + } | ||
| + | ||
| + if ([object isKindOfClass:[NSDate class]]) { | ||
| + JSValueRef argument = JSValueMakeNumber(contextInternalContext(context), [(NSDate *)object timeIntervalSince1970]); | ||
| + JSObjectRef result = JSObjectMakeDate(contextInternalContext(context), 1, &argument, 0); | ||
| + return (ObjcContainerConvertor::Task){ object, result, ContainerNone }; | ||
| + } | ||
| + } | ||
| + | ||
| + return (ObjcContainerConvertor::Task){ object, valueInternalValue([context wrapperForObject:object]), ContainerNone }; | ||
| +} | ||
| + | ||
| +JSValueRef objectToValue(JSContext *context, id object) | ||
| +{ | ||
| + ObjcContainerConvertor::Task task = objectToValueWithoutCopy(context, object); | ||
| + if (task.type == ContainerNone) | ||
| + return task.js; | ||
| + | ||
| + ObjcContainerConvertor convertor(context); | ||
| + convertor.add(task); | ||
| + ASSERT(!convertor.isWorkListEmpty()); | ||
| + | ||
| + do { | ||
| + ObjcContainerConvertor::Task current = convertor.take(); | ||
| + ASSERT(JSValueIsObject(contextInternalContext(context), current.js)); | ||
| + JSObjectRef js = JSValueToObject(contextInternalContext(context), current.js, 0); | ||
| + | ||
| + if (current.type == ContainerArray) { | ||
| + ASSERT([current.objc isKindOfClass:[NSArray class]]); | ||
| + NSArray * array = (NSArray *)current.objc; | ||
| + NSUInteger count = [array count]; | ||
| + for (NSUInteger index = 0; index < count; ++index) | ||
| + JSObjectSetPropertyAtIndex(contextInternalContext(context), js, index, convertor.convert([array objectAtIndex:index]), 0); | ||
| + } else { | ||
| + ASSERT(current.type == ContainerDictionary); | ||
| + ASSERT([current.objc isKindOfClass:[NSDictionary class]]); | ||
| + NSDictionary* dictionary = (NSDictionary*)current.objc; | ||
| + NSEnumerator* enumerator = [dictionary keyEnumerator]; | ||
| + while (id key = [enumerator nextObject]) { | ||
| + if ([key isKindOfClass:[NSString class]]) { | ||
| + JSStringRef propertyName = JSStringCreateWithCFString((CFStringRef)key); | ||
| + JSObjectSetProperty(contextInternalContext(context), js, propertyName, convertor.convert([dictionary objectForKey:key]), 0, 0); | ||
| + JSStringRelease(propertyName); | ||
| + } | ||
| + } | ||
| + } | ||
| + | ||
| + } while (!convertor.isWorkListEmpty()); | ||
| + | ||
| + return task.js; | ||
| +} | ||
| + | ||
| +JSValueRef valueInternalValue(JSValue * value) | ||
| +{ | ||
| + return value->m_value; | ||
| +} | ||
| + | ||
| ++ (JSValue *)valueWithValue:(JSValueRef)value inContext:(JSContext*)context | ||
| +{ | ||
| + return [[[JSValue alloc] initWithValue:value inContext:context] autorelease]; | ||
| +} | ||
| + | ||
| +- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext*)context | ||
| +{ | ||
| + ASSERT(value); | ||
| + | ||
| + objc_initWeak(&m_weakContext, context); | ||
| + | ||
| + [super init]; | ||
| + [context protect:value]; | ||
| + m_value = value; | ||
| + return self; | ||
| +} | ||
| + | ||
| +struct StructTagHandler { | ||
| + SEL typeToValueSEL; | ||
| + SEL valueToTypeSEL; | ||
| +}; | ||
| + | ||
| +static StructTagHandler* getStructTagHandler(const char* encodedType) | ||
| +{ | ||
| + static SpinLock getStructTagHandlerLock = SPINLOCK_INITIALIZER; | ||
| + SpinLockHolder lockHolder(&getStructTagHandlerLock); | ||
| + | ||
| + typedef WTF::HashMap<RefPtr<StringImpl>, StructTagHandler> StructHandlers; | ||
| + static StructHandlers *m_structHandlers = 0; | ||
| + | ||
| + if (!m_structHandlers) { | ||
| + m_structHandlers = new StructHandlers(); | ||
| + | ||
| + // Step 1: find all valueWith<Foo>:inContext: class methods in JSValue. | ||
| + size_t valueWithMinLen = strlen("valueWithX:inContext:"); | ||
| + forEachMethodInClass(object_getClass([JSValue class]), ^(Method method){ | ||
| + SEL sel = method_getName(method); | ||
| + const char* name = sel_getName(sel); | ||
| + size_t len = strlen(name); | ||
| + // Check for valueWith<Foo>:context: | ||
| + if (len < valueWithMinLen || strncmp(name, "valueWith", 9) || strncmp(name + len - 11, ":inContext:", 11)) | ||
| + return; | ||
| + // Check for [ id, SEL, <type>, <contextType> ] | ||
| + if (method_getNumberOfArguments(method) != 4) | ||
| + return; | ||
| + char idType[3]; | ||
| + // Check 2nd argument type is "@" | ||
| + char* secondType = method_copyArgumentType(method, 3); | ||
| + if (strcmp(secondType, "@")) { | ||
| + free(secondType); | ||
| + return; | ||
| + } | ||
| + free(secondType); | ||
| + // Check result type is also "@" | ||
| + method_getReturnType(method, idType, 3); | ||
| + if (strcmp(idType, "@")) | ||
| + return; | ||
| + char* type = method_copyArgumentType(method, 2); | ||
| + m_structHandlers->add(WTF::String(type).impl(), (StructTagHandler){ sel, 0 }); | ||
| + free(type); | ||
| + }); | ||
| + | ||
| + // Step 2: find all to<Foo> instance methods in JSValue. | ||
| + size_t minLenValue = strlen("toX"); | ||
| + forEachMethodInClass([JSValue class], ^(Method method){ | ||
| + SEL sel = method_getName(method); | ||
| + const char* name = sel_getName(sel); | ||
| + size_t len = strlen(name); | ||
| + // Check for to<Foo> | ||
| + if (len < minLenValue || strncmp(name, "to", 2)) | ||
| + return; | ||
| + // Check for [ id, SEL ] | ||
| + if (method_getNumberOfArguments(method) != 2) | ||
| + return; | ||
| + // Try to find a matching valueWith<Foo>:context: method. | ||
| + char* type = method_copyReturnType(method); | ||
| + | ||
| + StructHandlers::iterator iter = m_structHandlers->find(WTF::String(type).impl()); | ||
| + free(type); | ||
| + if (iter == m_structHandlers->end()) | ||
| + return; | ||
| + StructTagHandler& handler = iter->value; | ||
| + | ||
| + // check that strlen(<foo>) == strlen(<Foo>) | ||
| + const char* valueWithName = sel_getName(handler.typeToValueSEL); | ||
| + size_t valueWithLen = strlen(valueWithName); | ||
| + if (valueWithLen - valueWithMinLen != len - minLenValue) | ||
| + return; | ||
| + // Check that <Foo> == <Foo> | ||
| + if (strncmp(valueWithName + 9, name + 2, len - minLenValue - 1)) | ||
| + return; | ||
| + handler.valueToTypeSEL = sel; | ||
| + }); | ||
| + | ||
| + // Step 3: clean up - remove entries where we found prospective valueWith<Foo>:inContext: conversions, but no matching to<Foo> methods. | ||
| + typedef WTF::HashSet<RefPtr<WTF::StringImpl> > RemoveSet; | ||
| + RemoveSet removeSet; | ||
| + for (StructHandlers::iterator iter = m_structHandlers->begin(); iter != m_structHandlers->end(); ++iter) { | ||
| + StructTagHandler& handler = iter->value; | ||
| + if (!handler.valueToTypeSEL) | ||
| + removeSet.add(iter->key); | ||
| + } | ||
| + | ||
| + for (RemoveSet::iterator iter = removeSet.begin(); iter != removeSet.end(); ++iter) | ||
| + m_structHandlers->remove(*iter); | ||
| + } | ||
| + | ||
| + StructHandlers::iterator iter = m_structHandlers->find(WTF::String(encodedType).impl()); | ||
| + if (iter == m_structHandlers->end()) | ||
| + return 0; | ||
| + return &iter->value; | ||
| +} | ||
| + | ||
| ++ (SEL)selectorForStructToValue:(const char *)structTag | ||
| +{ | ||
| + StructTagHandler* handler = getStructTagHandler(structTag); | ||
| + return handler ? handler->typeToValueSEL : nil; | ||
| +} | ||
| + | ||
| ++ (SEL)selectorForValueToStruct:(const char *)structTag | ||
| +{ | ||
| + StructTagHandler* handler = getStructTagHandler(structTag); | ||
| + return handler ? handler->valueToTypeSEL : nil; | ||
| +} | ||
| + | ||
| +- (void)dealloc | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (context) | ||
| + [context unprotect:m_value]; | ||
| + objc_destroyWeak(&m_weakContext); | ||
| + [super dealloc]; | ||
| +} | ||
| + | ||
| +- (NSString *)description | ||
| +{ | ||
| + JSContext *context = [self context]; | ||
| + if (!context) | ||
| + return nil; | ||
| + | ||
| + if (id wrapped = tryUnwrapObjcObject(contextInternalContext(context), m_value)) | ||
| + return [wrapped description]; | ||
| + return [self toString]; | ||
| +} | ||
| + | ||
| +NSInvocation* typeToValueInvocationFor(const char* encodedType) | ||
| +{ | ||
| + SEL selector = [JSValue selectorForStructToValue:encodedType]; | ||
| + if (!selector) | ||
| + return 0; | ||
| + | ||
| + const char* methodTypes = method_getTypeEncoding(class_getClassMethod([JSValue class], selector)); | ||
| + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]]; | ||
| + [invocation setSelector:selector]; | ||
| + return invocation; | ||
| +} | ||
| + | ||
| +NSInvocation* valueToTypeInvocationFor(const char* encodedType) | ||
| +{ | ||
| + SEL selector = [JSValue selectorForValueToStruct:encodedType]; | ||
| + if (!selector) | ||
| + return 0; | ||
| + | ||
| + const char* methodTypes = method_getTypeEncoding(class_getInstanceMethod([JSValue class], selector)); | ||
| + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]]; | ||
| + [invocation setSelector:selector]; | ||
| + return invocation; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +#endif |
54
Source/JavaScriptCore/API/JSValueInternal.h
| @@ -0,0 +1,54 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import <JavaScriptCore/JavaScriptCore.h> | ||
| +#import "APIJSValue.h" | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +@interface JSValue(Internal) | ||
| + | ||
| +JSValueRef valueInternalValue(JSValue*); | ||
| + | ||
| ++ (JSValue *)valueWithValue:(JSValueRef)value inContext:(JSContext *)context; | ||
| +- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context; | ||
| + | ||
| +JSValueRef objectToValue(JSContext *, id); | ||
| +id valueToObject(JSContext *, JSValueRef); | ||
| +id valueToNumber(JSGlobalContextRef, JSValueRef, JSValueRef* exception); | ||
| +id valueToString(JSGlobalContextRef, JSValueRef, JSValueRef* exception); | ||
| +id valueToDate(JSGlobalContextRef, JSValueRef, JSValueRef* exception); | ||
| +id valueToArray(JSGlobalContextRef, JSValueRef, JSValueRef* exception); | ||
| +id valueToDictionary(JSGlobalContextRef, JSValueRef, JSValueRef* exception); | ||
| + | ||
| ++ (SEL)selectorForStructToValue:(const char *)structTag; | ||
| ++ (SEL)selectorForValueToStruct:(const char *)structTag; | ||
| + | ||
| +@end | ||
| + | ||
| +NSInvocation* typeToValueInvocationFor(const char* encodedType); | ||
| +NSInvocation* valueToTypeInvocationFor(const char* encodedType); | ||
| + | ||
| +#endif |
41
Source/JavaScriptCore/API/JSVirtualMachine.h
| @@ -0,0 +1,41 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import <JavaScriptCore/JavaScriptCore.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +// An instance of JSVirtualMachine represents a single JavaScript "object space" | ||
| +// or set of execution resources. Thread safety is supported by locking the | ||
| +// virtual machine, with concurrent JavaScript execution supported by allocating | ||
| +// separate instances of JSVirtualMachine. | ||
| +NS_CLASS_AVAILABLE(10_9, NA) | ||
| +@interface JSVirtualMachine : NSObject | ||
| + | ||
| +- (id)init; | ||
| + | ||
| +@end | ||
| + | ||
| +#endif |
66
Source/JavaScriptCore/API/JSVirtualMachine.mm
| @@ -0,0 +1,66 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#include "config.h" | ||
| + | ||
| +#import "JavaScriptCore.h" | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +#import "APICast.h" | ||
| +#import "JSVirtualMachineInternal.h" | ||
| + | ||
| +@implementation JSVirtualMachine { | ||
| + JSContextGroupRef m_group; | ||
| +} | ||
| + | ||
| +- (id)init | ||
| +{ | ||
| + [super init]; | ||
| + m_group = JSContextGroupCreate(); | ||
| + toJS(m_group)->m_apiData = self; | ||
| + return self; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +@implementation JSVirtualMachine(Internal) | ||
| + | ||
| +- (void)dealloc | ||
| +{ | ||
| + toJS(m_group)->m_apiData = 0; | ||
| + JSContextGroupRelease(m_group); | ||
| + [super dealloc]; | ||
| +} | ||
| + | ||
| +JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine) | ||
| +{ | ||
| + return virtualMachine->m_group; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +#endif | ||
| + |
37
Source/JavaScriptCore/API/JSVirtualMachineInternal.h
| @@ -0,0 +1,37 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import "JSVirtualMachine.h" | ||
| +#import <JavaScriptCore/JavaScriptCore.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +@interface JSVirtualMachine(Internal) | ||
| + | ||
| +JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine*); | ||
| + | ||
| +@end | ||
| + | ||
| +#endif |
45
Source/JavaScriptCore/API/JSWrapperMap.h
| @@ -0,0 +1,45 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import <JavaScriptCore/JavaScriptCore.h> | ||
| +#import <JSValueInternal.h> | ||
| +#import <objc/objc-runtime.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +@interface JSWrapperMap : NSObject | ||
| + | ||
| +- (id)initWithContext:(JSContext *)context; | ||
| + | ||
| +- (JSValue *)wrapperForObject:(id)object; | ||
| + | ||
| +@end | ||
| + | ||
| +id tryUnwrapObjcObject(JSGlobalContextRef, JSValueRef); | ||
| + | ||
| +Protocol* getJSExportProtocol(); | ||
| +Class getNSBlockClass(); | ||
| + | ||
| +#endif |
463
Source/JavaScriptCore/API/JSWrapperMap.mm
| @@ -0,0 +1,463 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#include "config.h" | ||
| +#import "JavaScriptCore.h" | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +#import "JSContextInternal.h" | ||
| +#import "JSWrapperMap.h" | ||
| +#import "ObjCCallbackFunction.h" | ||
| +#import "ObjcRuntimeExtras.h" | ||
| +#import <wtf/TCSpinLock.h> | ||
| +#import "wtf/Vector.h" | ||
| + | ||
| +static void wrapperFinalize(JSObjectRef object) | ||
| +{ | ||
| + [(id)JSObjectGetPrivate(object) release]; | ||
| +} | ||
| + | ||
| +// All wrapper objects and constructor objects derive from this type, so we can detect & unwrap Objective-C instances/Classes. | ||
| +static JSClassRef wrapperClass() | ||
| +{ | ||
| + static SpinLock initLock = SPINLOCK_INITIALIZER; | ||
| + SpinLockHolder lockHolder(&initLock); | ||
| + | ||
| + static JSClassRef classRef = 0; | ||
| + | ||
| + if (!classRef) { | ||
| + JSClassDefinition definition; | ||
| + definition = kJSClassDefinitionEmpty; | ||
| + definition.className = "objc_class"; | ||
| + definition.finalize = wrapperFinalize; | ||
| + classRef = JSClassCreate(&definition); | ||
| + } | ||
| + | ||
| + return classRef; | ||
| +} | ||
| + | ||
| +// Default conversion of selectors to property names. | ||
| +// All semicolons are removed, lowercase letters following a semicolon are capitalized. | ||
| +static NSString* selectorToPropertyName(const char* start) | ||
| +{ | ||
| + // Use 'index' to check for colons, if there are non, this is easy! | ||
| + const char* firstColon = index(start, ':'); | ||
| + if (!firstColon) | ||
| + return [NSString stringWithUTF8String:start]; | ||
| + | ||
| + // 'header' is the length of string up to the first colon. | ||
| + size_t header = firstColon - start; | ||
| + // The new string needs to be long enough to hold 'header', plus the remainder of the string, excluding | ||
| + // at least one ':', but including a '\0'. (This is conservative if there are more than one ':'). | ||
| + char* buffer = (char*)malloc(header + strlen(firstColon + 1) + 1); | ||
| + // Copy 'header' characters, set output to point to the end of this & input to point past the first ':'. | ||
| + memcpy(buffer, start, header); | ||
| + char* output = buffer + header; | ||
| + const char* input = start + header + 1; | ||
| + | ||
| + // On entry to the loop, we have already skipped over a ':' from the input. | ||
| + while (true) { | ||
| + char c; | ||
| + // Skip over any additional ':'s. We'll leave c holding the next character after the | ||
| + // last ':', and input pointing past c. | ||
| + while ((c = *(input++)) == ':'); | ||
| + // Copy the character, converting to upper case if necessary. | ||
| + // If the character we copy is '\0', then we're done! | ||
| + if (!(*(output++) = toupper(c))) | ||
| + goto done; | ||
| + // Loop over characters other than ':'. | ||
| + while ((c = *(input++)) != ':') { | ||
| + // Copy the character. | ||
| + // If the character we copy is '\0', then we're done! | ||
| + if (!(*(output++) = c)) | ||
| + goto done; | ||
| + } | ||
| + // If we get here, we've consumed a ':' - wash, rinse, repeat. | ||
| + } | ||
| +done: | ||
| + NSString* result = [NSString stringWithUTF8String:buffer]; | ||
| + free(buffer); | ||
| + return result; | ||
| +} | ||
| + | ||
| +// Make an object that is in all ways are completely vanilla JavaScript object, | ||
| +// other than that it has a native brand set that will be displayed by the default | ||
| +// Object.prototype.toString comversion. | ||
| +static JSValue* createObjectWithCustomBrand(JSContext* context, NSString* brand, JSClassRef parentClass = 0, void* privateData = 0) | ||
| +{ | ||
| + JSClassDefinition definition; | ||
| + definition = kJSClassDefinitionEmpty; | ||
| + definition.className = [brand UTF8String]; | ||
| + definition.parentClass = parentClass; | ||
| + JSClassRef classRef = JSClassCreate(&definition); | ||
| + JSObjectRef result = JSObjectMake(contextInternalContext(context), classRef, privateData); | ||
| + JSClassRelease(classRef); | ||
| + return [[JSValue alloc] initWithValue:result inContext:context]; | ||
| +} | ||
| + | ||
| +// Look for @optional properties in the prototype containing a selector to property | ||
| +// name mapping, separated by a __JS_EXPORT_AS__ delimiter. | ||
| +static NSMutableDictionary* createRenameMap(Protocol* protocol, BOOL isInstanceMethod) | ||
| +{ | ||
| + NSMutableDictionary* renameMap = [[NSMutableDictionary alloc] init]; | ||
| + | ||
| + forEachMethodInProtocol(protocol, NO, isInstanceMethod, ^(SEL sel, const char*){ | ||
| + NSString* rename = @(sel_getName(sel)); | ||
| + NSRange range = [rename rangeOfString:@"__JS_EXPORT_AS__"]; | ||
| + if (range.location == NSNotFound) | ||
| + return; | ||
| + NSString* selector = [rename substringToIndex:range.location]; | ||
| + NSUInteger begin = range.location + range.length; | ||
| + NSUInteger length = [rename length] - begin - 1; | ||
| + NSString* name = [rename substringWithRange:(NSRange){ begin, length }]; | ||
| + renameMap[selector] = name; | ||
| + }); | ||
| + | ||
| + return renameMap; | ||
| +} | ||
| + | ||
| +inline void putNonEnumerable(JSValue* base, NSString* propertyName, JSValue* value) | ||
| +{ | ||
| + [base defineProperty:propertyName descriptor:@{ | ||
| + JSPropertyDescriptorValueKey: value, | ||
| + JSPropertyDescriptorWritableKey: @YES, | ||
| + JSPropertyDescriptorEnumerableKey: @NO, | ||
| + JSPropertyDescriptorConfigurableKey: @YES | ||
| + }]; | ||
| +} | ||
| + | ||
| +// This method will iterate over the set of required methods in the protocol, and: | ||
| +// * Determine a property name (either via a renameMap or default conversion). | ||
| +// * If an accessorMap is provided, and conatins a this name, store the method in the map. | ||
| +// * Otherwise, if the object doesn't already conatin a property with name, create it. | ||
| +static void copyMethodsToObject(JSContext* context, Class objcClass, Protocol* protocol, BOOL isInstanceMethod, JSValue* object, NSMutableDictionary* accessorMethods = nil) | ||
| +{ | ||
| + NSMutableDictionary* renameMap = createRenameMap(protocol, isInstanceMethod); | ||
| + | ||
| + forEachMethodInProtocol(protocol, YES, isInstanceMethod, ^(SEL sel, const char* types){ | ||
| + const char* nameCStr = sel_getName(sel); | ||
| + NSString* name = @(nameCStr); | ||
| + if (accessorMethods && accessorMethods[name]) { | ||
| + JSObjectRef method = objCCallbackFunctionForMethod(context, objcClass, protocol, isInstanceMethod, sel, types); | ||
| + if (!method) | ||
| + return; | ||
| + accessorMethods[name] = [JSValue valueWithValue:method inContext:context]; | ||
| + } else { | ||
| + name = renameMap[name]; | ||
| + if (!name) | ||
| + name = selectorToPropertyName(nameCStr); | ||
| + if ([object hasProperty:name]) | ||
| + return; | ||
| + JSObjectRef method = objCCallbackFunctionForMethod(context, objcClass, protocol, isInstanceMethod, sel, types); | ||
| + if (method) | ||
| + putNonEnumerable(object, name, [JSValue valueWithValue:method inContext:context]); | ||
| + } | ||
| + }); | ||
| + | ||
| + [renameMap release]; | ||
| +} | ||
| + | ||
| +static bool parsePropertyAttributes(objc_property_t property, char*& getterName, char*& setterName) | ||
| +{ | ||
| + bool readonly = false; | ||
| + unsigned attributeCount; | ||
| + objc_property_attribute_t* attributes = property_copyAttributeList(property, &attributeCount); | ||
| + if (attributeCount) { | ||
| + for (unsigned i = 0; i < attributeCount; ++i) { | ||
| + switch (*(attributes[i].name)) { | ||
| + case 'G': | ||
| + getterName = strdup(attributes[i].value); | ||
| + break; | ||
| + case 'S': | ||
| + setterName = strdup(attributes[i].value); | ||
| + break; | ||
| + case 'R': | ||
| + readonly = true; | ||
| + break; | ||
| + default: | ||
| + break; | ||
| + } | ||
| + } | ||
| + free(attributes); | ||
| + } | ||
| + return readonly; | ||
| +} | ||
| + | ||
| +static char* makeSetterName(const char* name) | ||
| +{ | ||
| + size_t nameLength = strlen(name); | ||
| + char* setterName = (char*)malloc(nameLength + 5); // "set" Name ":\0" | ||
| + setterName[0] = 's'; | ||
| + setterName[1] = 'e'; | ||
| + setterName[2] = 't'; | ||
| + setterName[3] = toupper(*name); | ||
| + memcpy(setterName + 4, name + 1, nameLength - 1); | ||
| + setterName[nameLength + 3] = ':'; | ||
| + setterName[nameLength + 4] = '\0'; | ||
| + return setterName; | ||
| +} | ||
| + | ||
| +static void copyPrototypeProperties(JSContext* context, Class objcClass, Protocol* protocol, JSValue* prototypeValue) | ||
| +{ | ||
| + // First gather propreties into this list, then handle the methods (capturing the accessor methods). | ||
| + struct Property { | ||
| + const char* name; | ||
| + char* getterName; | ||
| + char* setterName; | ||
| + }; | ||
| + __block WTF::Vector<Property> propertyList; | ||
| + | ||
| + // Map recording the methods used as getters/setters. | ||
| + NSMutableDictionary *accessorMethods = [NSMutableDictionary dictionary]; | ||
| + | ||
| + // Useful value. | ||
| + JSValue* undefined = [JSValue valueWithUndefinedInContext:context]; | ||
| + | ||
| + forEachPropertyInProtocol(protocol, ^(objc_property_t property){ | ||
| + char* getterName = 0; | ||
| + char* setterName = 0; | ||
| + bool readonly = parsePropertyAttributes(property, getterName, setterName); | ||
| + const char* name = property_getName(property); | ||
| + | ||
| + // Add the names of the getter & setter methods to | ||
| + if (!getterName) | ||
| + getterName = strdup(name); | ||
| + accessorMethods[@(getterName)] = undefined; | ||
| + if (!readonly) { | ||
| + if (!setterName) | ||
| + setterName = makeSetterName(name); | ||
| + accessorMethods[@(setterName)] = undefined; | ||
| + } | ||
| + | ||
| + // Add the properties to a list. | ||
| + propertyList.append((Property){ name, getterName, setterName }); | ||
| + }); | ||
| + | ||
| + // Copy methods to the prototype, capturing accessors in the accessorMethods map. | ||
| + copyMethodsToObject(context, objcClass, protocol, YES, prototypeValue, accessorMethods); | ||
| + | ||
| + // Iterate the propertyList & generate accessor properties. | ||
| + for (size_t i = 0; i < propertyList.size(); ++i) { | ||
| + Property& property = propertyList[i]; | ||
| + | ||
| + JSValue* getter = accessorMethods[@(property.getterName)]; | ||
| + free(property.getterName); | ||
| + ASSERT(![getter isUndefined]); | ||
| + | ||
| + JSValue* setter = undefined; | ||
| + if (property.setterName) { | ||
| + setter = accessorMethods[@(property.setterName)]; | ||
| + free(property.setterName); | ||
| + ASSERT(![setter isUndefined]); | ||
| + } | ||
| + | ||
| + [prototypeValue defineProperty:@(property.name) descriptor:@{ | ||
| + JSPropertyDescriptorGetKey: getter, | ||
| + JSPropertyDescriptorSetKey: setter, | ||
| + JSPropertyDescriptorEnumerableKey: @NO, | ||
| + JSPropertyDescriptorConfigurableKey: @YES | ||
| + }]; | ||
| + } | ||
| +} | ||
| + | ||
| +@interface JSObjCClassInfo : NSObject { | ||
| + JSContext* m_context; | ||
| + Class m_class; | ||
| + bool m_block; | ||
| + JSClassRef m_classRef; | ||
| + JSValue* m_prototype; | ||
| + JSValue* m_constructor; | ||
| +} | ||
| + | ||
| +- (id)initWithContext:(JSContext*)context forClass:(Class)cls superClassInfo:(JSObjCClassInfo*)superClassInfo; | ||
| +- (JSValue *)wrapperForObject:(id)object; | ||
| +- (JSValue *)constructor; | ||
| + | ||
| +@end | ||
| + | ||
| +@implementation JSObjCClassInfo | ||
| + | ||
| +- (id)initWithContext:(JSContext*)context forClass:(Class)cls superClassInfo:(JSObjCClassInfo*)superClassInfo | ||
| +{ | ||
| + [super init]; | ||
| + | ||
| + const char* className = class_getName(cls); | ||
| + m_context = context; | ||
| + m_class = cls; | ||
| + m_block = [cls isSubclassOfClass:getNSBlockClass()]; | ||
| + JSClassDefinition definition; | ||
| + definition = kJSClassDefinitionEmpty; | ||
| + definition.className = className; | ||
| + definition.parentClass = wrapperClass(); | ||
| + m_classRef = JSClassCreate(&definition); | ||
| + | ||
| + ASSERT((cls == [NSObject class]) == !superClassInfo); | ||
| + if (!superClassInfo) { | ||
| + m_constructor = [context[@"Object"] retain]; | ||
| + m_prototype = [m_constructor[@"prototype"] retain]; | ||
| + } else { | ||
| + // Create the prototype/constructor pair. | ||
| + m_prototype = createObjectWithCustomBrand(context, [NSString stringWithFormat:@"%sPrototype", className]); | ||
| + m_constructor = createObjectWithCustomBrand(context, [NSString stringWithFormat:@"%sConstructor", className], wrapperClass(), [cls retain]); | ||
| + putNonEnumerable(m_prototype, @"constructor", m_constructor); | ||
| + putNonEnumerable(m_constructor, @"prototype", m_prototype); | ||
| + | ||
| + Protocol* exportProtocol = getJSExportProtocol(); | ||
| + forEachProtocolImplementingProtocol(cls, exportProtocol, ^(Protocol* protocol){ | ||
| + copyPrototypeProperties(context, cls, protocol, m_prototype); | ||
| + copyMethodsToObject(context, cls, protocol, NO, m_constructor); | ||
| + }); | ||
| + | ||
| + // Set [Prototype]. | ||
| + m_prototype[@"__proto__"] = superClassInfo->m_prototype; | ||
| + } | ||
| + | ||
| + return self; | ||
| +} | ||
| + | ||
| +- (void)dealloc | ||
| +{ | ||
| + JSClassRelease(m_classRef); | ||
| + [m_prototype release]; | ||
| + [m_constructor release]; | ||
| + [super dealloc]; | ||
| +} | ||
| + | ||
| +- (JSValue *)wrapperForObject:(id)object | ||
| +{ | ||
| + ASSERT([object isKindOfClass:m_class]); | ||
| + ASSERT(m_block == [object isKindOfClass:getNSBlockClass()]); | ||
| + if (m_block) { | ||
| + if (JSObjectRef method = objCCallbackFunctionForBlock(m_context, object)) | ||
| + return [JSValue valueWithValue:method inContext:m_context]; | ||
| + } | ||
| + | ||
| + JSValueRef prototypeValue = valueInternalValue(m_prototype); | ||
| + ASSERT(JSValueIsObject(contextInternalContext(m_context), prototypeValue)); | ||
| + JSObjectRef prototype = JSValueToObject(contextInternalContext(m_context), prototypeValue, 0); | ||
| + | ||
| + JSObjectRef wrapper = JSObjectMake(contextInternalContext(m_context), m_classRef, [object retain]); | ||
| + JSObjectSetPrototype(contextInternalContext(m_context), wrapper, prototype); | ||
| + return [JSValue valueWithValue:wrapper inContext:m_context]; | ||
| +} | ||
| + | ||
| +- (JSValue *)constructor | ||
| +{ | ||
| + return m_constructor; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +@implementation JSWrapperMap { | ||
| + JSContext *m_context; | ||
| + NSMutableDictionary* m_classMap; | ||
| +} | ||
| + | ||
| +- (id)initWithContext:(JSContext*)context | ||
| +{ | ||
| + [super init]; | ||
| + m_context = context; | ||
| + m_classMap = [[NSMutableDictionary alloc] init]; | ||
| + return self; | ||
| +} | ||
| + | ||
| +- (void)dealloc | ||
| +{ | ||
| + [m_classMap release]; | ||
| + [super dealloc]; | ||
| +} | ||
| + | ||
| +- (JSObjCClassInfo*)classInfoForClass:(Class)cls | ||
| +{ | ||
| + if (!cls) | ||
| + return nil; | ||
| + | ||
| + // Check if we've already created a JSObjCClassInfo for this Class. | ||
| + if (JSObjCClassInfo* classInfo = (JSObjCClassInfo*)m_classMap[cls]) | ||
| + return classInfo; | ||
| + | ||
| + // Skip internal classes begining with '_' - just copy link to the parent class's info. | ||
| + if ('_' == *class_getName(cls)) | ||
| + return m_classMap[cls] = [self classInfoForClass:class_getSuperclass(cls)]; | ||
| + | ||
| + return m_classMap[cls] = [[[JSObjCClassInfo alloc] initWithContext:m_context forClass:cls superClassInfo:[self classInfoForClass:class_getSuperclass(cls)]] autorelease]; | ||
| +} | ||
| + | ||
| +- (JSValue *)wrapperForObject:(id)object | ||
| +{ | ||
| + JSValue* wrapper = objc_getAssociatedObject(object, m_context); | ||
| + if (wrapper && wrapper.context) | ||
| + return wrapper; | ||
| + | ||
| + if (class_isMetaClass(object_getClass(object))) | ||
| + wrapper = [[self classInfoForClass:(Class)object] constructor]; | ||
| + else { | ||
| + JSObjCClassInfo* classInfo = [self classInfoForClass:[object class]]; | ||
| + wrapper = [classInfo wrapperForObject:object]; | ||
| + } | ||
| + | ||
| + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=105891 | ||
| + // This general approach to wrapper caching is pretty effective, but there are a couple of problems: | ||
| + // (1) For immortal objects JSValues will effectively leaj and this results in error output being logged - we should avoid adding associated objects to immortal objects. | ||
| + // (2) A long lived object may rack up many JSValues. When the contexts are released these will unproctect the associated JavaScript objects, | ||
| + // but still, would probably nicer if we made it so that only one associated object was required, broadcasting object dealloc. | ||
| + objc_setAssociatedObject(object, m_context, wrapper, OBJC_ASSOCIATION_RETAIN); | ||
| + return wrapper; | ||
| +} | ||
| + | ||
| +@end | ||
| + | ||
| +id tryUnwrapObjcObject(JSGlobalContextRef context, JSValueRef value) | ||
| +{ | ||
| + if (!JSValueIsObject(context, value)) | ||
| + return nil; | ||
| + JSValueRef exception = 0; | ||
| + JSObjectRef object = JSValueToObject(context, value, &exception); | ||
| + //ASSERT(!exception); | ||
| + if (JSValueIsObjectOfClass(context, object, wrapperClass())) | ||
| + return (id)JSObjectGetPrivate(object); | ||
| + if (id target = tryUnwrapBlock(context, object)) | ||
| + return target; | ||
| + return nil; | ||
| +} | ||
| + | ||
| +Protocol* getJSExportProtocol() | ||
| +{ | ||
| + static Protocol* protocol = 0; | ||
| + if (!protocol) | ||
| + protocol = objc_getProtocol("JSExport"); | ||
| + return protocol; | ||
| +} | ||
| + | ||
| +Class getNSBlockClass() | ||
| +{ | ||
| + static Class cls = 0; | ||
| + if (!cls) | ||
| + cls = objc_getClass("NSBlock"); | ||
| + return cls; | ||
| +} | ||
| + | ||
| +#endif |
9
Source/JavaScriptCore/API/JavaScriptCore.h
35
Source/JavaScriptCore/API/ObjCCallbackFunction.h
| @@ -0,0 +1,35 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import <JavaScriptCore/JavaScriptCore.h> | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +JSObjectRef objCCallbackFunctionForMethod(JSContext*, Class, Protocol*, BOOL isInstanceMethod, SEL, const char* types); | ||
| +JSObjectRef objCCallbackFunctionForBlock(JSContext*, id); | ||
| + | ||
| +id tryUnwrapBlock(JSGlobalContextRef, JSObjectRef); | ||
| + | ||
| +#endif |
677
Source/JavaScriptCore/API/ObjCCallbackFunction.mm
| @@ -0,0 +1,677 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#include "config.h" | ||
| +#import "JavaScriptCore.h" | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +#import "APICast.h" | ||
| +#import "APIShims.h" | ||
| +#import "Error.h" | ||
| +#import "JSBlockAdaptor.h" | ||
| +#import "JSContextInternal.h" | ||
| +#import "JSWrapperMap.h" | ||
| +#import "JSValueInternal.h" | ||
| +#import "ObjCCallbackFunction.h" | ||
| +#import "ObjcRuntimeExtras.h" | ||
| +#import "objc/runtime.h" | ||
| +#import "wtf/RetainPtr.h" | ||
| + | ||
| +class CallbackArgument { | ||
| +public: | ||
| + virtual ~CallbackArgument(); | ||
| + virtual void set(NSInvocation*, NSInteger, JSContext*, JSValueRef, JSValueRef*) = 0; | ||
| + | ||
| + OwnPtr<CallbackArgument> m_next; | ||
| +}; | ||
| + | ||
| +CallbackArgument::~CallbackArgument() | ||
| +{ | ||
| +} | ||
| + | ||
| +class CallbackArgumentBoolean : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef*) | ||
| + { | ||
| + bool value = JSValueToBoolean(contextInternalContext(context), argument); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +template<typename T> | ||
| +class CallbackArgumentInteger : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + T value = (T)JSC::toInt32(JSValueToNumber(contextInternalContext(context), argument, exception)); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +template<typename T> | ||
| +class CallbackArgumentDouble : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + T value = (T)JSValueToNumber(contextInternalContext(context), argument, exception); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackArgumentJSValue : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef*) | ||
| + { | ||
| + JSValue* value = [JSValue valueWithValue:argument inContext:context]; | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackArgumentId : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef*) | ||
| + { | ||
| + id value = valueToObject(context, argument); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackArgumentOfClass : public CallbackArgument { | ||
| +public: | ||
| + CallbackArgumentOfClass(Class cls) | ||
| + : CallbackArgument() | ||
| + , m_class(cls) | ||
| + { | ||
| + [m_class retain]; | ||
| + } | ||
| + | ||
| + ~CallbackArgumentOfClass() | ||
| + { | ||
| + [m_class release]; | ||
| + } | ||
| + | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + JSGlobalContextRef ctx = contextInternalContext(context); | ||
| + | ||
| + id object = tryUnwrapObjcObject(ctx, argument); | ||
| + if (object && [object isKindOfClass:m_class]) { | ||
| + [invocation setArgument:&object atIndex:argumentNumber]; | ||
| + return; | ||
| + } | ||
| + | ||
| + if (JSValueIsNull(ctx, argument) || JSValueIsUndefined(ctx, argument)) { | ||
| + object = nil; | ||
| + [invocation setArgument:&object atIndex:argumentNumber]; | ||
| + return; | ||
| + } | ||
| + | ||
| + *exception = toRef(JSC::createTypeError(toJS(ctx), "Argument does not match Objective-C Class")); | ||
| + } | ||
| + | ||
| +private: | ||
| + Class m_class; | ||
| +}; | ||
| + | ||
| +class CallbackArgumentNSNumber : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + id value = valueToNumber(contextInternalContext(context), argument, exception); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackArgumentNSString : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + id value = valueToString(contextInternalContext(context), argument, exception); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackArgumentNSDate : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + id value = valueToDate(contextInternalContext(context), argument, exception); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackArgumentNSArray : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + id value = valueToArray(contextInternalContext(context), argument, exception); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackArgumentNSDictionary : public CallbackArgument { | ||
| +public: | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + id value = valueToDictionary(contextInternalContext(context), argument, exception); | ||
| + [invocation setArgument:&value atIndex:argumentNumber]; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackArgumentStruct : public CallbackArgument { | ||
| +public: | ||
| + CallbackArgumentStruct(NSInvocation* conversionInvocation, const char* encodedType) | ||
| + : m_conversionInvocation(conversionInvocation) | ||
| + { | ||
| + [m_conversionInvocation retain]; | ||
| + NSUInteger size, alignment; | ||
| + NSGetSizeAndAlignment(encodedType, &size, &alignment); | ||
| + --alignment; | ||
| + m_allocation = (char*)malloc(size + alignment); | ||
| + m_buffer = reinterpret_cast<char*>((reinterpret_cast<intptr_t>(m_allocation) + alignment) & ~alignment); | ||
| + } | ||
| + | ||
| + virtual ~CallbackArgumentStruct() | ||
| + { | ||
| + [m_conversionInvocation release]; | ||
| + free(m_allocation); | ||
| + } | ||
| + | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef*) | ||
| + { | ||
| + JSValue* value = [JSValue valueWithValue:argument inContext:context]; | ||
| + [m_conversionInvocation invokeWithTarget:value]; | ||
| + [m_conversionInvocation getReturnValue:m_buffer]; | ||
| + [invocation setArgument:m_buffer atIndex:argumentNumber]; | ||
| + } | ||
| + | ||
| +private: | ||
| + NSInvocation* m_conversionInvocation; | ||
| + char* m_buffer; | ||
| + char* m_allocation; | ||
| +}; | ||
| + | ||
| +class CallbackArgumentBlockCallback : public CallbackArgument { | ||
| +public: | ||
| + CallbackArgumentBlockCallback(JSBlockAdaptor* adaptor) | ||
| + : m_adaptor(adaptor) | ||
| + { | ||
| + } | ||
| + | ||
| + virtual ~CallbackArgumentBlockCallback() | ||
| + { | ||
| + [m_adaptor release]; | ||
| + } | ||
| + | ||
| + void set(NSInvocation* invocation, NSInteger argumentNumber, JSContext* context, JSValueRef argument, JSValueRef* exception) | ||
| + { | ||
| + id block = [m_adaptor blockFromValue:argument inContext:context withException:exception]; | ||
| + [invocation setArgument:&block atIndex:argumentNumber]; | ||
| + } | ||
| + | ||
| +private: | ||
| + JSBlockAdaptor* m_adaptor; | ||
| +}; | ||
| + | ||
| +class ArgumentTypeDelegate { | ||
| +public: | ||
| + typedef CallbackArgument* ResultType; | ||
| + | ||
| + template<typename T> | ||
| + static ResultType typeInteger() | ||
| + { | ||
| + return new CallbackArgumentInteger<T>; | ||
| + } | ||
| + | ||
| + template<typename T> | ||
| + static ResultType typeDouble() | ||
| + { | ||
| + return new CallbackArgumentDouble<T>; | ||
| + } | ||
| + | ||
| + static ResultType typeBool() | ||
| + { | ||
| + return new CallbackArgumentBoolean; | ||
| + } | ||
| + | ||
| + static ResultType typeVoid() | ||
| + { | ||
| + ASSERT_NOT_REACHED(); | ||
| + return nil; | ||
| + } | ||
| + | ||
| + static ResultType typeId() | ||
| + { | ||
| + return new CallbackArgumentId; | ||
| + } | ||
| + | ||
| + static ResultType typeOfClass(const char* begin, const char* end) | ||
| + { | ||
| + StringRange copy(begin, end); | ||
| + Class cls = objc_getClass(copy); | ||
| + if (!cls) | ||
| + return nil; | ||
| + | ||
| + if (cls == [JSValue class]) | ||
| + return new CallbackArgumentJSValue; | ||
| + if (cls == [NSString class]) | ||
| + return new CallbackArgumentNSString; | ||
| + if (cls == [NSNumber class]) | ||
| + return new CallbackArgumentNSNumber; | ||
| + if (cls == [NSDate class]) | ||
| + return new CallbackArgumentNSDate; | ||
| + if (cls == [NSArray class]) | ||
| + return new CallbackArgumentNSArray; | ||
| + if (cls == [NSDictionary class]) | ||
| + return new CallbackArgumentNSDictionary; | ||
| + | ||
| + return new CallbackArgumentOfClass(cls); | ||
| + } | ||
| + | ||
| + static ResultType typeBlock(const char* begin, const char* end) | ||
| + { | ||
| + StringRange copy(begin, end); | ||
| + return new CallbackArgumentBlockCallback([[JSBlockAdaptor alloc] initWithBlockSignatureFromProtocol:copy]); | ||
| + } | ||
| + | ||
| + static ResultType typeStruct(const char* begin, const char* end) | ||
| + { | ||
| + StringRange copy(begin, end); | ||
| + if (NSInvocation* invocation = valueToTypeInvocationFor(copy)) | ||
| + return new CallbackArgumentStruct(invocation, copy); | ||
| + return nil; | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackResult { | ||
| +public: | ||
| + virtual ~CallbackResult() | ||
| + { | ||
| + } | ||
| + | ||
| + virtual JSValueRef get(NSInvocation*, JSContext*, JSValueRef*) = 0; | ||
| +}; | ||
| + | ||
| +class CallbackResultVoid : public CallbackResult { | ||
| +public: | ||
| + virtual JSValueRef get(NSInvocation*, JSContext* context, JSValueRef*) | ||
| + { | ||
| + return JSValueMakeUndefined(contextInternalContext(context)); | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackResultId : public CallbackResult { | ||
| +public: | ||
| + virtual JSValueRef get(NSInvocation* invocation, JSContext* context, JSValueRef*) | ||
| + { | ||
| + id value; | ||
| + [invocation getReturnValue:&value]; | ||
| + return objectToValue(context, value); | ||
| + } | ||
| +}; | ||
| + | ||
| +template<typename T> | ||
| +class CallbackResultNumeric : public CallbackResult { | ||
| +public: | ||
| + virtual JSValueRef get(NSInvocation* invocation, JSContext* context, JSValueRef*) | ||
| + { | ||
| + T value; | ||
| + [invocation getReturnValue:&value]; | ||
| + return JSValueMakeNumber(contextInternalContext(context), (double)value); | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackResultBoolean : public CallbackResult { | ||
| +public: | ||
| + virtual JSValueRef get(NSInvocation* invocation, JSContext* context, JSValueRef*) | ||
| + { | ||
| + bool value; | ||
| + [invocation getReturnValue:&value]; | ||
| + return JSValueMakeBoolean(contextInternalContext(context), value); | ||
| + } | ||
| +}; | ||
| + | ||
| +class CallbackResultStruct : public CallbackResult { | ||
| +public: | ||
| + CallbackResultStruct(NSInvocation* conversionInvocation, const char* encodedType) | ||
| + : m_conversionInvocation(conversionInvocation) | ||
| + { | ||
| + [m_conversionInvocation retain]; | ||
| + NSUInteger size, alignment; | ||
| + NSGetSizeAndAlignment(encodedType, &size, &alignment); | ||
| + --alignment; | ||
| + m_allocation = (char*)malloc(size + alignment); | ||
| + m_buffer = reinterpret_cast<char*>((reinterpret_cast<intptr_t>(m_allocation) + alignment) & ~alignment); | ||
| + } | ||
| + | ||
| + virtual ~CallbackResultStruct() | ||
| + { | ||
| + [m_conversionInvocation release]; | ||
| + free(m_allocation); | ||
| + } | ||
| + | ||
| + virtual JSValueRef get(NSInvocation* invocation, JSContext* context, JSValueRef*) | ||
| + { | ||
| + [invocation getReturnValue:m_allocation]; | ||
| + | ||
| + [m_conversionInvocation setArgument:m_allocation atIndex:2]; | ||
| + [m_conversionInvocation setArgument:&context atIndex:3]; | ||
| + [m_conversionInvocation invokeWithTarget:[JSValue class]]; | ||
| + | ||
| + JSValue* value; | ||
| + [m_conversionInvocation getReturnValue:&value]; | ||
| + return valueInternalValue(value); | ||
| + } | ||
| + | ||
| +private: | ||
| + NSInvocation* m_conversionInvocation; | ||
| + char* m_buffer; | ||
| + char* m_allocation; | ||
| +}; | ||
| + | ||
| +class ResultTypeDelegate { | ||
| +public: | ||
| + typedef CallbackResult* ResultType; | ||
| + | ||
| + template<typename T> | ||
| + static ResultType typeInteger() | ||
| + { | ||
| + return new CallbackResultNumeric<T>; | ||
| + } | ||
| + | ||
| + template<typename T> | ||
| + static ResultType typeDouble() | ||
| + { | ||
| + return new CallbackResultNumeric<T>; | ||
| + } | ||
| + | ||
| + static ResultType typeBool() | ||
| + { | ||
| + return new CallbackResultBoolean; | ||
| + } | ||
| + | ||
| + static ResultType typeVoid() | ||
| + { | ||
| + return new CallbackResultVoid; | ||
| + } | ||
| + | ||
| + static ResultType typeId() | ||
| + { | ||
| + return new CallbackResultId(); | ||
| + } | ||
| + | ||
| + static ResultType typeOfClass(const char*, const char*) | ||
| + { | ||
| + return new CallbackResultId(); | ||
| + } | ||
| + | ||
| + static ResultType typeBlock(const char*, const char*) | ||
| + { | ||
| + return new CallbackResultId(); | ||
| + } | ||
| + | ||
| + static ResultType typeStruct(const char* begin, const char* end) | ||
| + { | ||
| + StringRange copy(begin, end); | ||
| + if (NSInvocation* invocation = typeToValueInvocationFor(copy)) | ||
| + return new CallbackResultStruct(invocation, copy); | ||
| + return nil; | ||
| + } | ||
| +}; | ||
| + | ||
| +enum CallbackType { | ||
| + CallbackInstanceMethod, | ||
| + CallbackClassMethod, | ||
| + CallbackBlock | ||
| +}; | ||
| + | ||
| +class ObjCCallbackFunction { | ||
| +public: | ||
| + ObjCCallbackFunction(JSContext *context, NSInvocation* invocation, CallbackType type, Class instanceClass, PassOwnPtr<CallbackArgument> arguments, PassOwnPtr<CallbackResult> result) | ||
| + : m_context(context) | ||
| + , m_type(type) | ||
| + , m_instanceClass([instanceClass retain]) | ||
| + , m_invocation(invocation) | ||
| + , m_arguments(arguments) | ||
| + , m_result(result) | ||
| + { | ||
| + ASSERT(type != CallbackInstanceMethod || instanceClass); | ||
| + } | ||
| + | ||
| + ~ObjCCallbackFunction() | ||
| + { | ||
| + [m_instanceClass release]; | ||
| + } | ||
| + | ||
| + JSValueRef call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); | ||
| + | ||
| + JSContext* context() | ||
| + { | ||
| + return m_context.get(); | ||
| + } | ||
| + | ||
| + id wrappedBlock() | ||
| + { | ||
| + return m_type == CallbackBlock ? [m_invocation target] : nil; | ||
| + } | ||
| + | ||
| +private: | ||
| + WeakContextRef m_context; | ||
| + CallbackType m_type; | ||
| + Class m_instanceClass; | ||
| + WTF::RetainPtr<NSInvocation> m_invocation; | ||
| + OwnPtr<CallbackArgument> m_arguments; | ||
| + OwnPtr<CallbackResult> m_result; | ||
| +}; | ||
| + | ||
| +static void objCCallbackFunctionFinalize(JSObjectRef object) | ||
| +{ | ||
| + delete (ObjCCallbackFunction*)JSObjectGetPrivate(object); | ||
| +} | ||
| + | ||
| +static JSValueRef objCCallbackFunctionCallAsFunction(JSContextRef callerContext, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) | ||
| +{ | ||
| + // Retake the API lock - we need this for a few reasons: | ||
| + // (1) We don't want to support the C-API's confusing drops-locks-once policy - should only drop locks if we can do so recursivey. | ||
| + // (2) We're caling some JSC internals that require us to be on the 'inside' - e.g. createTypeError. | ||
| + // (3) We need to be locked (per context would be fine) against conflicting usgae of the ObjCCallbackFunction's NSInvocation. | ||
| + JSC::APIEntryShim entryShim(toJS(callerContext)); | ||
| + | ||
| + ObjCCallbackFunction* callback = (ObjCCallbackFunction*)JSObjectGetPrivate(function); | ||
| + JSContext *context = callback->context(); | ||
| + if (!context) { | ||
| + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=105894 | ||
| + // Rather than requiring that the context be retained, it would probably be more | ||
| + // appropriate to use a new JSContext instance (creating one if necessary). | ||
| + *exception = toRef(JSC::createTypeError(toJS(callerContext), "Objective-C callback function context released")); | ||
| + return JSValueMakeUndefined(callerContext); | ||
| + } | ||
| + | ||
| + CallbackData callbackData; | ||
| + JSValueRef result; | ||
| + @autoreleasepool { | ||
| + [context beginCallbackWithData:&callbackData thisValue:thisObject argumentCount:argumentCount arguments:arguments]; | ||
| + result = callback->call(context, thisObject, argumentCount, arguments, exception); | ||
| + if (context.exception) | ||
| + *exception = valueInternalValue(context.exception); | ||
| + [context endCallbackWithData:&callbackData]; | ||
| + } | ||
| + return result; | ||
| +} | ||
| + | ||
| +static JSClassRef objCCallbackFunctionClass() | ||
| +{ | ||
| + static SpinLock initLock = SPINLOCK_INITIALIZER; | ||
| + SpinLockHolder lockHolder(&initLock); | ||
| + | ||
| + static JSClassRef classRef = 0; | ||
| + | ||
| + if (!classRef) { | ||
| + JSClassDefinition definition; | ||
| + definition = kJSClassDefinitionEmpty; | ||
| + definition.className = "Function"; | ||
| + definition.finalize = objCCallbackFunctionFinalize; | ||
| + definition.callAsFunction = objCCallbackFunctionCallAsFunction; | ||
| + classRef = JSClassCreate(&definition); | ||
| + } | ||
| + | ||
| + return classRef; | ||
| +} | ||
| + | ||
| +JSValueRef ObjCCallbackFunction::call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) | ||
| +{ | ||
| + JSGlobalContextRef ctx = contextInternalContext(context); | ||
| + | ||
| + size_t firstArgument; | ||
| + switch (m_type) { | ||
| + case CallbackInstanceMethod: { | ||
| + id target = tryUnwrapObjcObject(ctx, thisObject); | ||
| + if (!target || ![target isKindOfClass:m_instanceClass]) { | ||
| + *exception = toRef(JSC::createTypeError(toJS(ctx), "self type check failed for Objective-C instance method")); | ||
| + return JSValueMakeUndefined(ctx); | ||
| + } | ||
| + [m_invocation setTarget:target]; | ||
| + } | ||
| + case CallbackClassMethod: | ||
| + firstArgument = 2; | ||
| + break; | ||
| + case CallbackBlock: | ||
| + firstArgument = 1; | ||
| + } | ||
| + | ||
| + size_t argumentNumber = 0; | ||
| + for (CallbackArgument* argument = m_arguments.get(); argument; argument = argument->m_next.get()) { | ||
| + JSValueRef value = argumentNumber < argumentCount ? arguments[argumentNumber] : JSValueMakeUndefined(ctx); | ||
| + argument->set(m_invocation.get(), argumentNumber + firstArgument, context, value, exception); | ||
| + if (*exception) | ||
| + return JSValueMakeUndefined(ctx); | ||
| + ++argumentNumber; | ||
| + } | ||
| + | ||
| + [m_invocation invoke]; | ||
| + | ||
| + return m_result->get(m_invocation.get(), context, exception); | ||
| +} | ||
| + | ||
| +static bool blockSignatureContainsClass() | ||
| +{ | ||
| + enum CheckResult { | ||
| + CheckResultFalse, | ||
| + CheckResultTrue, | ||
| + CheckResultUnknown | ||
| + }; | ||
| + static enum CheckResult check = CheckResultUnknown; | ||
| + if (check == CheckResultUnknown) { | ||
| + id block = ^(NSString *string){ return string; }; | ||
| + check = _Block_has_signature(block) && strstr(_Block_signature(block), "NSString") ? CheckResultTrue : CheckResultFalse; | ||
| + } | ||
| + return (bool)check; | ||
| +} | ||
| + | ||
| +inline bool skipNumber(const char*& position) | ||
| +{ | ||
| + if (!isdigit(*position)) | ||
| + return false; | ||
| + while(isdigit(*++position)); | ||
| + return true; | ||
| +} | ||
| + | ||
| +static JSObjectRef objCCallbackFunctionForInvocation(JSContext* context, NSInvocation* invocation, CallbackType type, Class instanceClass, const char* signatureWithObjcClasses) | ||
| +{ | ||
| + const char* position = signatureWithObjcClasses; | ||
| + | ||
| + OwnPtr<CallbackResult> result = adoptPtr(parseObjCType<ResultTypeDelegate>(position)); | ||
| + if (!result || !skipNumber(position)) | ||
| + return nil; | ||
| + | ||
| + switch (type) { | ||
| + case CallbackInstanceMethod: | ||
| + case CallbackClassMethod: | ||
| + if ('@' != *(position++) || !skipNumber(position) || ':' != *(position++) || !skipNumber(position)) | ||
| + return nil; | ||
| + break; | ||
| + case CallbackBlock: | ||
| + if (('@' != *(position++)) || ('?' != *(position++)) || !skipNumber(position) || (!blockSignatureContainsClass() && strchr(position, '@'))) | ||
| + return nil; | ||
| + break; | ||
| + } | ||
| + | ||
| + OwnPtr<CallbackArgument> arguments = 0; | ||
| + OwnPtr<CallbackArgument>* nextArgument = &arguments; | ||
| + unsigned argumentCount = 0; | ||
| + while (*position) { | ||
| + OwnPtr<CallbackArgument> argument = adoptPtr(parseObjCType<ArgumentTypeDelegate>(position)); | ||
| + if (!argument || !skipNumber(position)) | ||
| + return nil; | ||
| + | ||
| + *nextArgument = argument.release(); | ||
| + nextArgument = &(*nextArgument)->m_next; | ||
| + ++argumentCount; | ||
| + } | ||
| + | ||
| + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=105892 | ||
| + // The result should be a regular host function, rather than a callable C API object. | ||
| + // Patch in the right length & [Prototype] values for now, but should fix this. | ||
| + // Function.prototype.toString currently fails, since this is not yet a subclass of functon, but call & apply do work. | ||
| + JSObjectRef functionObject = JSObjectMake(contextInternalContext(context), objCCallbackFunctionClass(), new ObjCCallbackFunction(context, invocation, type, instanceClass, arguments.release(), result.release())); | ||
| + JSValue* value = [JSValue valueWithValue:functionObject inContext:context]; | ||
| + value[@"length"] = @(argumentCount); | ||
| + value[@"__proto__"] = context[@"Function"][@"prototype"]; | ||
| + value[@"toString"] = [context evaluateScript:@"(function(){ return '" | ||
| + "function <Objective-C>() {" "\\n" | ||
| + " [native code]" "\\n" | ||
| + "}" "\\n" | ||
| + "'})"]; | ||
| + return functionObject; | ||
| +} | ||
| + | ||
| +JSObjectRef objCCallbackFunctionForMethod(JSContext* context, Class cls, Protocol* protocol, BOOL isInstanceMethod, SEL sel, const char* types) | ||
| +{ | ||
| + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:types]]; | ||
| + [invocation setSelector:sel]; | ||
| + if (!isInstanceMethod) | ||
| + [invocation setTarget:cls]; | ||
| + return objCCallbackFunctionForInvocation(context, invocation, isInstanceMethod ? CallbackInstanceMethod : CallbackClassMethod, isInstanceMethod ? cls : nil, _protocol_getMethodTypeEncoding(protocol, sel, YES, isInstanceMethod)); | ||
| +} | ||
| + | ||
| +JSObjectRef objCCallbackFunctionForBlock(JSContext* context, id target) | ||
| +{ | ||
| + if (!_Block_has_signature(target)) | ||
| + return 0; | ||
| + const char* signature = _Block_signature(target); | ||
| + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:signature]]; | ||
| + [invocation setTarget:target]; | ||
| + return objCCallbackFunctionForInvocation(context, invocation, CallbackBlock, nil, signature); | ||
| +} | ||
| + | ||
| +id tryUnwrapBlock(JSGlobalContextRef context, JSObjectRef object) | ||
| +{ | ||
| + if (!JSValueIsObjectOfClass(context, object, objCCallbackFunctionClass())) | ||
| + return nil; | ||
| + return ((ObjCCallbackFunction*)JSObjectGetPrivate(object))->wrappedBlock(); | ||
| +} | ||
| + | ||
| +#endif |
219
Source/JavaScriptCore/API/ObjcRuntimeExtras.h
| @@ -0,0 +1,219 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import <objc/runtime.h> | ||
| +#import <wtf/HashSet.h> | ||
| +#import <wtf/Vector.h> | ||
| + | ||
| +inline bool protocolImplementsProtocol(Protocol* candidate, Protocol* target) | ||
| +{ | ||
| + unsigned protocolProtocolsCount; | ||
| + Protocol** protocolProtocols = protocol_copyProtocolList(candidate, &protocolProtocolsCount); | ||
| + for (unsigned i = 0; i < protocolProtocolsCount; ++i) { | ||
| + if (protocol_isEqual(protocolProtocols[i], target)) { | ||
| + free(protocolProtocols); | ||
| + return true; | ||
| + } | ||
| + } | ||
| + free(protocolProtocols); | ||
| + return false; | ||
| +} | ||
| + | ||
| +inline void forEachProtocolImplementingProtocol(Class cls, Protocol* target, void (^callback)(Protocol*)) | ||
| +{ | ||
| + ASSERT(cls); | ||
| + ASSERT(target); | ||
| + | ||
| + WTF::Vector<Protocol*> worklist; | ||
| + WTF::HashSet<void*> visited; | ||
| + | ||
| + // Initially fill the worklist with the Class's protocols. | ||
| + unsigned protocolsCount; | ||
| + Protocol** protocols = class_copyProtocolList(cls, &protocolsCount); | ||
| + if (protocolsCount) { | ||
| + worklist.append(protocols, protocolsCount); | ||
| + free(protocols); | ||
| + } | ||
| + | ||
| + while (!worklist.isEmpty()) { | ||
| + Protocol* protocol = worklist.last(); | ||
| + worklist.removeLast(); | ||
| + | ||
| + // Are we encountering this Protocol for the first time? | ||
| + if (!visited.add(protocol).isNewEntry) | ||
| + continue; | ||
| + | ||
| + // If it implements the protocol, make the callback. | ||
| + if (protocolImplementsProtocol(protocol, target)) | ||
| + callback(protocol); | ||
| + | ||
| + // Add incorporated protocols to the worklist. | ||
| + protocols = protocol_copyProtocolList(protocol, &protocolsCount); | ||
| + if (protocolsCount) { | ||
| + worklist.append(protocols, protocolsCount); | ||
| + free(protocols); | ||
| + } | ||
| + } | ||
| +} | ||
| + | ||
| +inline void forEachMethodInClass(Class cls, void (^callback)(Method method)) | ||
| +{ | ||
| + unsigned count; | ||
| + Method* methods = class_copyMethodList(cls, &count); | ||
| + if (count) { | ||
| + for (unsigned i = 0; i < count; ++i) | ||
| + callback(methods[i]); | ||
| + free(methods); | ||
| + } | ||
| +} | ||
| + | ||
| +inline void forEachMethodInProtocol(Protocol* protocol, BOOL isRequiredMethod, BOOL isInstanceMethod, void (^callback)(SEL, const char*)) | ||
| +{ | ||
| + unsigned count; | ||
| + struct objc_method_description* methods = protocol_copyMethodDescriptionList(protocol, isRequiredMethod, isInstanceMethod, &count); | ||
| + if (count) { | ||
| + for (unsigned i = 0; i < count; ++i) | ||
| + callback(methods[i].name, methods[i].types); | ||
| + free(methods); | ||
| + } | ||
| +} | ||
| + | ||
| +inline void forEachPropertyInProtocol(Protocol* protocol, void (^callback)(objc_property_t)) | ||
| +{ | ||
| + unsigned count; | ||
| + objc_property_t* properties = protocol_copyPropertyList(protocol, &count); | ||
| + if (count) { | ||
| + for (unsigned i = 0; i < count; ++i) | ||
| + callback(properties[i]); | ||
| + free(properties); | ||
| + } | ||
| +} | ||
| + | ||
| +template<char open, char close> | ||
| +void skipPair(const char*& position) | ||
| +{ | ||
| + NSUInteger count = 1; | ||
| + do { | ||
| + char c = *(position++); | ||
| + if (!c) | ||
| + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Malformed type encoding" userInfo:nil]; | ||
| + if (c == open) | ||
| + ++count; | ||
| + else if (c == close) | ||
| + --count; | ||
| + } while (count); | ||
| +} | ||
| + | ||
| +class StringRange { | ||
| +public: | ||
| + StringRange(const char* begin, const char* end) : m_ptr(strndup(begin, end - begin)) { } | ||
| + ~StringRange() { free(m_ptr); } | ||
| + operator const char*() { return m_ptr; } | ||
| + const char* get() { return m_ptr; } | ||
| + | ||
| +private: | ||
| + char* m_ptr; | ||
| +}; | ||
| + | ||
| +template<typename DelegateType> | ||
| +typename DelegateType::ResultType parseObjCType(const char*& position) | ||
| +{ | ||
| + ASSERT(*position); | ||
| + | ||
| + switch (*(position++)) { | ||
| + case 'c': | ||
| + return DelegateType::template typeInteger<char>(); | ||
| + case 'i': | ||
| + return DelegateType::template typeInteger<int>(); | ||
| + case 's': | ||
| + return DelegateType::template typeInteger<short>(); | ||
| + case 'l': | ||
| + return DelegateType::template typeInteger<long>(); | ||
| + case 'q': | ||
| + return DelegateType::template typeDouble<unsigned long long>(); | ||
| + case 'C': | ||
| + return DelegateType::template typeInteger<unsigned char>(); | ||
| + case 'I': | ||
| + return DelegateType::template typeInteger<unsigned>(); | ||
| + case 'S': | ||
| + return DelegateType::template typeInteger<unsigned short>(); | ||
| + case 'L': | ||
| + return DelegateType::template typeInteger<unsigned long>(); | ||
| + case 'Q': | ||
| + return DelegateType::template typeDouble<unsigned long long>(); | ||
| + case 'f': | ||
| + return DelegateType::template typeDouble<float>(); | ||
| + case 'd': | ||
| + return DelegateType::template typeDouble<double>(); | ||
| + case 'B': | ||
| + return DelegateType::typeBool(); | ||
| + case 'v': | ||
| + return DelegateType::typeVoid(); | ||
| + | ||
| + case '@': { // An object (whether statically typed or typed id) | ||
| + if (position[0] == '?' && position[1] == '<') { | ||
| + position += 2; | ||
| + const char* begin = position; | ||
| + skipPair<'<','>'>(position); | ||
| + return DelegateType::typeBlock(begin, position - 1); | ||
| + } | ||
| + | ||
| + if (*position == '"') { | ||
| + const char* begin = ++position; | ||
| + position = index(position, '"'); | ||
| + return DelegateType::typeOfClass(begin, position++); | ||
| + } | ||
| + | ||
| + return DelegateType::typeId(); | ||
| + } | ||
| + | ||
| + case '{': { // {name=type...} A structure | ||
| + const char* begin = position - 1; | ||
| + skipPair<'{','}'>(position); | ||
| + return DelegateType::typeStruct(begin, position); | ||
| + } | ||
| + | ||
| + // NOT supporting C strings, arrays, pointers, unions, bitfields, function pointers. | ||
| + case '*': // A character string (char *) | ||
| + case '[': // [array type] An array | ||
| + case '(': // (name=type...) A union | ||
| + case 'b': // bnum A bit field of num bits | ||
| + case '^': // ^type A pointer to type | ||
| + case '?': // An unknown type (among other things, this code is used for function pointers) | ||
| + // NOT supporting Objective-C Class, SEL | ||
| + case '#': // A class object (Class) | ||
| + case ':': // A method selector (SEL) | ||
| + default: | ||
| + return nil; | ||
| + } | ||
| +} | ||
| + | ||
| +extern "C" { | ||
| + const char *_protocol_getMethodTypeEncoding(Protocol *, SEL, BOOL isRequiredMethod, BOOL isInstanceMethod); | ||
| + id objc_initWeak(id *, id); | ||
| + void objc_destroyWeak(id *); | ||
| + bool _Block_has_signature(void *); | ||
| + const char * _Block_signature(void *); | ||
| +} |
10
Source/JavaScriptCore/API/tests/testapi.c
477
Source/JavaScriptCore/API/tests/testapi.m
| @@ -0,0 +1,477 @@ | ||
| +/* | ||
| + * Copyright (C) 2013 Apple Inc. All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions | ||
| + * are met: | ||
| + * 1. Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * 2. Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | ||
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| + | ||
| +#import "JavaScriptCore.h" | ||
| + | ||
| +bool _Block_has_signature(void *); | ||
| +const char * _Block_signature(void *); | ||
| + | ||
| +extern int failed; | ||
| +void testObjectiveCAPI(void); | ||
| + | ||
| +#if JS_OBJC_API_ENABLED | ||
| + | ||
| +@protocol ParentObject <JSExport> | ||
| +@end | ||
| + | ||
| +@interface ParentObject : NSObject<ParentObject> | ||
| ++ (NSString *)parentTest; | ||
| +@end | ||
| + | ||
| +@implementation ParentObject | ||
| ++ (NSString *)parentTest | ||
| +{ | ||
| + return [self description]; | ||
| +} | ||
| +@end | ||
| + | ||
| +@protocol TestObject <JSExport> | ||
| +@property int variable; | ||
| +@property (readonly) int six; | ||
| +@property CGPoint point; | ||
| ++ (NSString *)classTest; | ||
| ++ (NSString *)parentTest; | ||
| +- (NSString *)getString; | ||
| +JSExportAs(testArgumentTypes, | ||
| +- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString*)s number:(NSNumber*)n array:(NSArray*)a dictionary:(NSDictionary*)o | ||
| +); | ||
| +- (void)callback:(void(^)(int))block; | ||
| +@end | ||
| + | ||
| +@interface TestObject : ParentObject <TestObject> | ||
| +@property int six; | ||
| ++ (id)testObject; | ||
| +@end | ||
| + | ||
| +@implementation TestObject | ||
| +@synthesize variable; | ||
| +@synthesize six; | ||
| +@synthesize point; | ||
| ++ (id)testObject | ||
| +{ | ||
| + return [[[TestObject alloc] init] autorelease]; | ||
| +} | ||
| ++ (NSString *)classTest | ||
| +{ | ||
| + return @"classTest - okay"; | ||
| +} | ||
| +- (NSString *)getString | ||
| +{ | ||
| + return @"42"; | ||
| +} | ||
| +- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString*)s number:(NSNumber*)n array:(NSArray*)a dictionary:(NSDictionary*)o | ||
| +{ | ||
| + return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]]; | ||
| +} | ||
| +- (void)callback:(void(^)(int))block | ||
| +{ | ||
| + block(42); | ||
| +} | ||
| +@end | ||
| + | ||
| +bool testXYZTested = false; | ||
| + | ||
| +@protocol TextXYZ <JSExport> | ||
| +@property int x; | ||
| +@property (readonly) int y; | ||
| +- (void)test:(NSString *)message; | ||
| +@end | ||
| + | ||
| +@interface TextXYZ : NSObject <TextXYZ> | ||
| +@property int x; | ||
| +@property int y; | ||
| +@property int z; | ||
| +@end | ||
| + | ||
| +@implementation TextXYZ | ||
| +@synthesize x; | ||
| +@synthesize y; | ||
| +@synthesize z; | ||
| +- (void)test:(NSString *)message | ||
| +{ | ||
| + testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5; | ||
| +} | ||
| +@end | ||
| + | ||
| +static void checkResult(NSString *description, bool passed) | ||
| +{ | ||
| + NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED"); | ||
| + if (!passed) | ||
| + failed = 1; | ||
| +} | ||
| + | ||
| +static bool blockSignatureContainsClass() | ||
| +{ | ||
| + enum CheckResult { | ||
| + CheckResultFalse, | ||
| + CheckResultTrue, | ||
| + CheckResultUnknown | ||
| + }; | ||
| + static enum CheckResult check = CheckResultUnknown; | ||
| + if (check == CheckResultUnknown) { | ||
| + id block = ^(NSString *string){ return string; }; | ||
| + check = _Block_has_signature(block) && strstr(_Block_signature(block), "NSString") ? CheckResultTrue : CheckResultFalse; | ||
| + } | ||
| + return (bool)check; | ||
| +} | ||
| + | ||
| +void testObjectiveCAPI() | ||
| +{ | ||
| + NSLog(@"Testing Objective-C API"); | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + JSValue *result = [context evaluateScript:@"2 + 2"]; | ||
| + checkResult(@"2 + 2", [result toInt32] == 4); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]]; | ||
| + checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"message"] = @"Hello"; | ||
| + JSValue *result = [context evaluateScript:@"message + ', World!'"]; | ||
| + checkResult(@"Hello, World!", [[result toString] isEqual:@"Hello, World!"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + JSValue *result = [context evaluateScript:@"({ x:42 })"]; | ||
| + id obj = [result toObject]; | ||
| + checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]); | ||
| + id num = (NSDictionary*)obj[@"x"]; | ||
| + checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + __block int result; | ||
| + context[@"blockCallback"] = ^(int value){ | ||
| + result = value; | ||
| + }; | ||
| + [context evaluateScript:@"blockCallback(42)"]; | ||
| + checkResult(@"blockCallback", result == 42); | ||
| + } | ||
| + | ||
| + if (blockSignatureContainsClass()) { | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + __block bool result = false; | ||
| + context[@"blockCallback"] = ^(NSString *value){ | ||
| + result = [@"42" isEqualToString:value] == YES; | ||
| + }; | ||
| + [context evaluateScript:@"blockCallback(42)"]; | ||
| + checkResult(@"blockCallback(NSString *)", result); | ||
| + } | ||
| + } else | ||
| + NSLog(@"Skipping 'blockCallback(NSString *)' test case"); | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + checkResult(@"!context.exception", !context.exception); | ||
| + [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | ||
| + checkResult(@"context.exception", context.exception); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + __block bool caught = false; | ||
| + context.exceptionHandler = ^(JSContext *context, JSValue *exception) { | ||
| + (void)context; | ||
| + (void)exception; | ||
| + caught = true; | ||
| + }; | ||
| + [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | ||
| + checkResult(@"JSContext.exceptionHandler", caught); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"callback"] = ^{ | ||
| + JSContext *context = [JSContext currentContext]; | ||
| + context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context]; | ||
| + }; | ||
| + JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"]; | ||
| + checkResult(@"Explicit throw in callback - was caught by JavaScript", [[result toString] isEqual:@"Caught exception"]); | ||
| + checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"callback"] = ^{ | ||
| + JSContext *context = [JSContext currentContext]; | ||
| + [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | ||
| + }; | ||
| + JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"]; | ||
| + checkResult(@"Implicit throw in callback - was caught by JavaScript", [[result toString] isEqual:@"Caught exception"]); | ||
| + checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + [context evaluateScript: | ||
| + @"function sum(array) { \ | ||
| + var result = 0; \ | ||
| + for (var i in array) \ | ||
| + result += array[i]; \ | ||
| + return result; \ | ||
| + }"]; | ||
| + JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context]; | ||
| + JSValue *sumFunction = context[@"sum"]; | ||
| + JSValue *result = [sumFunction callWithArguments:@[ array ]]; | ||
| + checkResult(@"sum([13, 2, 7])", [result toInt32] == 22); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + JSValue *mulAddFunction = [context evaluateScript: | ||
| + @"(function(array, object) { \ | ||
| + var result = []; \ | ||
| + for (var i in array) \ | ||
| + result.push(array[i] * object.x + object.y); \ | ||
| + return result; \ | ||
| + })"]; | ||
| + JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]]; | ||
| + checkResult(@"mulAddFunction", [[result toString] isEqual:@"43,44,46"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + JSValue *object = [JSValue valueWithNewObjectInContext:context]; | ||
| + | ||
| + object[@"point"] = @{ @"x":@1, @"y":@2 }; | ||
| + object[@"point"][@"x"] = @3; | ||
| + CGPoint point = [object[@"point"] toPoint]; | ||
| + checkResult(@"toPoint", point.x == 3 && point.y == 2); | ||
| + | ||
| + object[@{ @"toString":^{ return @"foo"; } }] = @"bar"; | ||
| + checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]); | ||
| + | ||
| + object[[@"foobar" substringToIndex:3]] = @"bar"; | ||
| + checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TextXYZ *testXYZ = [[TextXYZ alloc] init]; | ||
| + context[@"testXYZ"] = testXYZ; | ||
| + testXYZ.x = 3; | ||
| + testXYZ.y = 4; | ||
| + testXYZ.z = 5; | ||
| + [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"]; | ||
| + [context evaluateScript:@"testXYZ.test('test')"]; | ||
| + checkResult(@"TextXYZ - testXYZTested", testXYZTested); | ||
| + JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"]; | ||
| + checkResult(@"TextXYZ - result", [[result toString] isEqual:@"13,4,undefined"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{ | ||
| + JSPropertyDescriptorGetKey:^{ | ||
| + return [JSContext currentThis][@"x"]; | ||
| + } | ||
| + }]; | ||
| + JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context]; | ||
| + int result = [object [@"getterProperty"] toInt32]; | ||
| + checkResult(@"getterProperty", result == 101); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"concatenate"] = ^{ | ||
| + NSArray *arguments = [JSContext currentArguments]; | ||
| + if (![arguments count]) | ||
| + return @""; | ||
| + NSString *message = [arguments[0] description]; | ||
| + for (NSUInteger index = 1; index < [arguments count]; ++index) | ||
| + message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]]; | ||
| + return message; | ||
| + }; | ||
| + JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"]; | ||
| + checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"foo"] = @YES; | ||
| + JSValue *result = [context evaluateScript:@"typeof foo"]; | ||
| + checkResult(@"@YES is boolean", [[result toString] isEqual:@"boolean"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + JSValue *result = [context evaluateScript:@"String(testObject)"]; | ||
| + checkResult(@"String(testObject)", [[result toString] isEqual:@"[object TestObject]"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"]; | ||
| + checkResult(@"String(testObject.__proto__)", [[result toString] isEqual:@"[object TestObjectPrototype]"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"TestObject"] = [TestObject class]; | ||
| + JSValue *result = [context evaluateScript:@"String(TestObject)"]; | ||
| + checkResult(@"String(TestObject)", [[result toString] isEqual:@"[object TestObjectConstructor]"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context]; | ||
| + checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"TestObject"] = [TestObject class]; | ||
| + JSValue *result = [context evaluateScript:@"TestObject.parentTest()"]; | ||
| + checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObjectA"] = testObject; | ||
| + context[@"testObjectB"] = testObject; | ||
| + JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"]; | ||
| + checkResult(@"testObjectA == testObjectB", [result toBool]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + testObject.point = (CGPoint){3,4}; | ||
| + JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"]; | ||
| + checkResult(@"testObject.point - result", [[result toString] isEqual:@"{\"x\":3,\"y\":4}"]); | ||
| + checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + testObject.six = 6; | ||
| + context[@"testObject"] = testObject; | ||
| + context[@"mul"] = ^(int x, int y){ return x * y; }; | ||
| + JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"]; | ||
| + checkResult(@"mul(testObject.six, 7)", [result toInt32] == 42); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + context[@"testObject"][@"variable"] = @4; | ||
| + [context evaluateScript:@"++testObject.variable"]; | ||
| + checkResult(@"++testObject.variable", testObject.variable == 5); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"point"] = @{ @"x":@6, @"y":@7 }; | ||
| + JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"]; | ||
| + checkResult(@"point.x + ',' + point.y", [[result toString] isEqual:@"6,7"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + context[@"point"] = @{ @"x":@6, @"y":@7 }; | ||
| + JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"]; | ||
| + checkResult(@"point.x + ',' + point.y", [[result toString] isEqual:@"6,7"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + JSValue *result = [context evaluateScript:@"testObject.getString()"]; | ||
| + checkResult(@"testObject.getString()", [result toInt32] == 42); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"]; | ||
| + checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"]; | ||
| + checkResult(@"testObject.getString.call(testObject)", [result toInt32] == 42); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + checkResult(@"testObject.getString.call({}) pre", !context.exception); | ||
| + [context evaluateScript:@"testObject.getString.call({})"]; | ||
| + checkResult(@"testObject.getString.call({}) post", context.exception); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context = [[[JSContext alloc] init] autorelease]; | ||
| + TestObject* testObject = [TestObject testObject]; | ||
| + context[@"testObject"] = testObject; | ||
| + JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"]; | ||
| + checkResult(@"testObject.callback", [result toInt32] == 42); | ||
| + } | ||
| + | ||
| + @autoreleasepool { | ||
| + JSContext *context1 = [[JSContext alloc] init]; | ||
| + JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine]; | ||
| + JSValue *value = [JSValue valueWithDouble:42 inContext:context2]; | ||
| + checkResult(@"value.context == context2", value.context == context2); | ||
| + context1[@"passValueBetweenContexts"] = value; | ||
| + JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"]; | ||
| + checkResult(@"result.context == context1", result.context == context1); | ||
| + checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]); | ||
| + [context1 release]; | ||
| + [context2 release]; | ||
| + } | ||
| +} | ||
| + | ||
| +#else | ||
| + | ||
| +void testObjectiveCAPI() | ||
| +{ | ||
| +} | ||
| + | ||
| +#endif |
421
Source/JavaScriptCore/ChangeLog
72
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
1
Source/JavaScriptCore/runtime/JSGlobalData.cpp
2
Source/JavaScriptCore/runtime/JSGlobalData.h
1
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
2
Source/JavaScriptCore/runtime/JSGlobalObject.h
13
Source/WTF/ChangeLog
3
Source/WTF/wtf/WTFThreadData.cpp
2
Source/WTF/wtf/WTFThreadData.h
0 comments on commit
fc38188