Permalink
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...
1 parent 3e7471a commit fc3818879aa31b219fb99880378ef947bbc1acc1 barraclough@apple.com committed Jan 2, 2013
Showing with 5,170 additions and 4 deletions.
  1. +301 โˆ’0 Source/JavaScriptCore/API/APIJSValue.h
  2. +8 โˆ’0 Source/JavaScriptCore/API/JSBase.h
  3. +38 โˆ’0 Source/JavaScriptCore/API/JSBlockAdaptor.h
  4. +428 โˆ’0 Source/JavaScriptCore/API/JSBlockAdaptor.mm
  5. +2 โˆ’2 Source/JavaScriptCore/API/JSCallbackObjectFunctions.h
  6. +116 โˆ’0 Source/JavaScriptCore/API/JSContext.h
  7. +262 โˆ’0 Source/JavaScriptCore/API/JSContext.mm
  8. +73 โˆ’0 Source/JavaScriptCore/API/JSContextInternal.h
  9. +127 โˆ’0 Source/JavaScriptCore/API/JSExport.h
  10. +1,167 โˆ’0 Source/JavaScriptCore/API/JSValue.mm
  11. +54 โˆ’0 Source/JavaScriptCore/API/JSValueInternal.h
  12. +41 โˆ’0 Source/JavaScriptCore/API/JSVirtualMachine.h
  13. +66 โˆ’0 Source/JavaScriptCore/API/JSVirtualMachine.mm
  14. +37 โˆ’0 Source/JavaScriptCore/API/JSVirtualMachineInternal.h
  15. +45 โˆ’0 Source/JavaScriptCore/API/JSWrapperMap.h
  16. +463 โˆ’0 Source/JavaScriptCore/API/JSWrapperMap.mm
  17. +9 โˆ’0 Source/JavaScriptCore/API/JavaScriptCore.h
  18. +35 โˆ’0 Source/JavaScriptCore/API/ObjCCallbackFunction.h
  19. +677 โˆ’0 Source/JavaScriptCore/API/ObjCCallbackFunction.mm
  20. +219 โˆ’0 Source/JavaScriptCore/API/ObjcRuntimeExtras.h
  21. +9 โˆ’1 Source/JavaScriptCore/API/tests/testapi.c
  22. +477 โˆ’0 Source/JavaScriptCore/API/tests/testapi.m
  23. +421 โˆ’0 Source/JavaScriptCore/ChangeLog
  24. +72 โˆ’0 Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
  25. +1 โˆ’0 Source/JavaScriptCore/runtime/JSGlobalData.cpp
  26. +2 โˆ’0 Source/JavaScriptCore/runtime/JSGlobalData.h
  27. +1 โˆ’0 Source/JavaScriptCore/runtime/JSGlobalObject.cpp
  28. +2 โˆ’0 Source/JavaScriptCore/runtime/JSGlobalObject.h
  29. +13 โˆ’0 Source/WTF/ChangeLog
  30. +2 โˆ’1 Source/WTF/wtf/WTFThreadData.cpp
  31. +2 โˆ’0 Source/WTF/wtf/WTFThreadData.h
View
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
+
View
8 Source/JavaScriptCore/API/JSBase.h
@@ -30,6 +30,10 @@
#include <stdbool.h>
#endif
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+
/* JavaScript engine interface */
/*! @typedef JSContextGroupRef A group that associates JavaScript contexts with one another. Contexts in the same group may share and exchange JavaScript objects. */
@@ -135,4 +139,8 @@ JS_EXPORT void JSGarbageCollect(JSContextRef ctx);
}
#endif
+/* Enable the Objective-C API for platforms with a modern runtime. */
+#undef JS_OBJC_API_ENABLED
+#define JS_OBJC_API_ENABLED (defined(__OBJC__) && defined(__clang__) && defined(__APPLE__) && (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) && !defined(__i386__))
+
#endif /* JSBase_h */
View
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
View
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
View
4 Source/JavaScriptCore/API/JSCallbackObjectFunctions.h
@@ -508,10 +508,10 @@ void* JSCallbackObject<Parent>::getPrivate()
template <class Parent>
bool JSCallbackObject<Parent>::inherits(JSClassRef c) const
{
- for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass)
+ for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
if (jsClass == c)
return true;
-
+ }
return false;
}
View
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
View
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
View
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
View
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
View
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
View
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
View
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
View
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
+
View
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
View
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
View
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
View
9 Source/JavaScriptCore/API/JavaScriptCore.h
@@ -29,4 +29,13 @@
#include <JavaScriptCore/JavaScript.h>
#include <JavaScriptCore/JSStringRefCF.h>
+#if JS_OBJC_API_ENABLED
+
+#import "JSContext.h"
+#import "APIJSValue.h"
+#import "JSVirtualMachine.h"
+#import "JSExport.h"
+
+#endif
+
#endif /* JavaScriptCore_h */
View
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
View
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
View
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 *);
+}
View
10 Source/JavaScriptCore/API/tests/testapi.c
@@ -48,8 +48,12 @@ static double nan(const char*)
#endif
+#if PLATFORM(MAC)
+void testObjectiveCAPI(void);
+#endif
+
static JSGlobalContextRef context;
-static int failed;
+int failed;
static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
{
if (JSValueToBoolean(context, value) != expectedValue) {
@@ -1040,6 +1044,10 @@ int main(int argc, char* argv[])
::SetErrorMode(0);
#endif
+#if PLATFORM(MAC)
+ testObjectiveCAPI();
+#endif
+
const char *scriptPath = "testapi.js";
if (argc > 1) {
scriptPath = argv[1];
View
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
View
421 Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,424 @@
+2012-12-31 Gavin Barraclough <barraclough@apple.com>
+
+ Objective-C API for JavaScriptCore
+ https://bugs.webkit.org/show_bug.cgi?id=105889
+
+ Reviewed by Filip Pizlo.
+
+ 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:flags: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.
+
2012-12-27 Csaba Osztrogonรกc <ossy@webkit.org>
One more unreviwed holiday MIPS and SH4 buildfixes after r138516.
View
72 Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -540,6 +540,7 @@
86CC85A30EE79B7400288682 /* JITCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CC85A20EE79B7400288682 /* JITCall.cpp */; };
86CC85C40EE7A89400288682 /* JITPropertyAccess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CC85C30EE7A89400288682 /* JITPropertyAccess.cpp */; };
86CCEFDE0F413F8900FD7F9E /* JITCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CCEFDD0F413F8900FD7F9E /* JITCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 86D2221A167EF9440024C804 /* testapi.m in Sources */ = {isa = PBXBuildFile; fileRef = 86D22219167EF9440024C804 /* testapi.m */; };
86D3B2C310156BDE002865E7 /* ARMAssembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86D3B2BF10156BDE002865E7 /* ARMAssembler.cpp */; };
86D3B2C410156BDE002865E7 /* ARMAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C010156BDE002865E7 /* ARMAssembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
86D3B2C510156BDE002865E7 /* AssemblerBufferWithConstantPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C110156BDE002865E7 /* AssemblerBufferWithConstantPool.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -548,6 +549,18 @@
86D3B3C410159D7F002865E7 /* RepatchBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B3C210159D7F002865E7 /* RepatchBuffer.h */; settings = {ATTRIBUTES = (Private, ); }; };
86DB64640F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86DB64630F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp */; };
86E116B10FE75AC800B512BC /* CodeLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E116B00FE75AC800B512BC /* CodeLocation.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 86E3C612167BABD7006D760A /* APIJSValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C606167BAB87006D760A /* APIJSValue.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 86E3C613167BABD7006D760A /* JSContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C607167BAB87006D760A /* JSContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 86E3C614167BABD7006D760A /* JSExport.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60A167BAB87006D760A /* JSExport.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 86E3C615167BABD7006D760A /* JSVirtualMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60F167BAB87006D760A /* JSVirtualMachine.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 86E3C616167BABEE006D760A /* JSContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86E3C608167BAB87006D760A /* JSContext.mm */; };
+ 86E3C617167BABEE006D760A /* JSContextInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C609167BAB87006D760A /* JSContextInternal.h */; };
+ 86E3C618167BABEE006D760A /* JSWrapperMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86E3C60B167BAB87006D760A /* JSWrapperMap.mm */; };
+ 86E3C619167BABEE006D760A /* JSWrapperMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60C167BAB87006D760A /* JSWrapperMap.h */; };
+ 86E3C61A167BABEE006D760A /* JSValue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86E3C60D167BAB87006D760A /* JSValue.mm */; };
+ 86E3C61B167BABEE006D760A /* JSValueInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C60E167BAB87006D760A /* JSValueInternal.h */; };
+ 86E3C61C167BABEE006D760A /* JSVirtualMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86E3C610167BAB87006D760A /* JSVirtualMachine.mm */; };
+ 86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E3C611167BAB87006D760A /* JSVirtualMachineInternal.h */; };
86E85539111B9968001AF51E /* JSStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E85538111B9968001AF51E /* JSStringBuilder.h */; };
86EBF2FF1560F06A008E9222 /* NameConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86EBF2F91560F036008E9222 /* NameConstructor.cpp */; };
86EBF3001560F06A008E9222 /* NameConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EBF2FA1560F036008E9222 /* NameConstructor.h */; };
@@ -569,6 +582,11 @@
86EC9DD31328DF82002B2AD7 /* DFGSpeculativeJIT.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EC9DC31328DF82002B2AD7 /* DFGSpeculativeJIT.h */; };
86ECA3EA132DEF1C002B2AD7 /* DFGNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ECA3E9132DEF1C002B2AD7 /* DFGNode.h */; };
86ECA3FA132DF25A002B2AD7 /* DFGScoreBoard.h in Headers */ = {isa = PBXBuildFile; fileRef = 86ECA3F9132DF25A002B2AD7 /* DFGScoreBoard.h */; };
+ 86F3EEBB168CDE930077B92A /* JSBlockAdaptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB7168CCF750077B92A /* JSBlockAdaptor.h */; };
+ 86F3EEBC168CDE930077B92A /* JSBlockAdaptor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86F3EEB8168CCF750077B92A /* JSBlockAdaptor.mm */; };
+ 86F3EEBD168CDE930077B92A /* ObjCCallbackFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB9168CCF750077B92A /* ObjCCallbackFunction.h */; };
+ 86F3EEBE168CDE930077B92A /* ObjCCallbackFunction.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86F3EEBA168CCF750077B92A /* ObjCCallbackFunction.mm */; };
+ 86F3EEBF168CDE930077B92A /* ObjcRuntimeExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 86F3EEB616855A5B0077B92A /* ObjcRuntimeExtras.h */; };
86FA9E91142BBB2E001773B7 /* JSBoundFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86FA9E8F142BBB2D001773B7 /* JSBoundFunction.cpp */; };
86FA9E92142BBB2E001773B7 /* JSBoundFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */; };
90213E3D123A40C200D422F3 /* MemoryStatistics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 90213E3B123A40C200D422F3 /* MemoryStatistics.cpp */; };
@@ -1378,6 +1396,7 @@
86CC85A20EE79B7400288682 /* JITCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITCall.cpp; sourceTree = "<group>"; };
86CC85C30EE7A89400288682 /* JITPropertyAccess.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITPropertyAccess.cpp; sourceTree = "<group>"; };
86CCEFDD0F413F8900FD7F9E /* JITCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITCode.h; sourceTree = "<group>"; };
+ 86D22219167EF9440024C804 /* testapi.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = testapi.m; path = API/tests/testapi.m; sourceTree = "<group>"; };
86D3B2BF10156BDE002865E7 /* ARMAssembler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ARMAssembler.cpp; sourceTree = "<group>"; };
86D3B2C010156BDE002865E7 /* ARMAssembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARMAssembler.h; sourceTree = "<group>"; };
86D3B2C110156BDE002865E7 /* AssemblerBufferWithConstantPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AssemblerBufferWithConstantPool.h; sourceTree = "<group>"; };
@@ -1386,6 +1405,18 @@
86D3B3C210159D7F002865E7 /* RepatchBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RepatchBuffer.h; sourceTree = "<group>"; };
86DB64630F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutableAllocatorFixedVMPool.cpp; sourceTree = "<group>"; };
86E116B00FE75AC800B512BC /* CodeLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeLocation.h; sourceTree = "<group>"; };
+ 86E3C606167BAB87006D760A /* APIJSValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIJSValue.h; sourceTree = "<group>"; };
+ 86E3C607167BAB87006D760A /* JSContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSContext.h; sourceTree = "<group>"; };
+ 86E3C608167BAB87006D760A /* JSContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = JSContext.mm; sourceTree = "<group>"; };
+ 86E3C609167BAB87006D760A /* JSContextInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSContextInternal.h; sourceTree = "<group>"; };
+ 86E3C60A167BAB87006D760A /* JSExport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSExport.h; sourceTree = "<group>"; };
+ 86E3C60B167BAB87006D760A /* JSWrapperMap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = JSWrapperMap.mm; sourceTree = "<group>"; };
+ 86E3C60C167BAB87006D760A /* JSWrapperMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWrapperMap.h; sourceTree = "<group>"; };
+ 86E3C60D167BAB87006D760A /* JSValue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = JSValue.mm; sourceTree = "<group>"; };
+ 86E3C60E167BAB87006D760A /* JSValueInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSValueInternal.h; sourceTree = "<group>"; };
+ 86E3C60F167BAB87006D760A /* JSVirtualMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSVirtualMachine.h; sourceTree = "<group>"; };
+ 86E3C610167BAB87006D760A /* JSVirtualMachine.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = JSVirtualMachine.mm; sourceTree = "<group>"; };
+ 86E3C611167BAB87006D760A /* JSVirtualMachineInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSVirtualMachineInternal.h; sourceTree = "<group>"; };
86E85538111B9968001AF51E /* JSStringBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSStringBuilder.h; sourceTree = "<group>"; };
86EBF2F91560F036008E9222 /* NameConstructor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NameConstructor.cpp; sourceTree = "<group>"; };
86EBF2FA1560F036008E9222 /* NameConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NameConstructor.h; sourceTree = "<group>"; };
@@ -1407,6 +1438,11 @@
86EC9DC31328DF82002B2AD7 /* DFGSpeculativeJIT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGSpeculativeJIT.h; path = dfg/DFGSpeculativeJIT.h; sourceTree = "<group>"; };
86ECA3E9132DEF1C002B2AD7 /* DFGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNode.h; path = dfg/DFGNode.h; sourceTree = "<group>"; };
86ECA3F9132DF25A002B2AD7 /* DFGScoreBoard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGScoreBoard.h; path = dfg/DFGScoreBoard.h; sourceTree = "<group>"; };
+ 86F3EEB616855A5B0077B92A /* ObjcRuntimeExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjcRuntimeExtras.h; sourceTree = "<group>"; };
+ 86F3EEB7168CCF750077B92A /* JSBlockAdaptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBlockAdaptor.h; sourceTree = "<group>"; };
+ 86F3EEB8168CCF750077B92A /* JSBlockAdaptor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = JSBlockAdaptor.mm; sourceTree = "<group>"; };
+ 86F3EEB9168CCF750077B92A /* ObjCCallbackFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjCCallbackFunction.h; sourceTree = "<group>"; };
+ 86F3EEBA168CCF750077B92A /* ObjCCallbackFunction.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ObjCCallbackFunction.mm; sourceTree = "<group>"; };
86F75EFB151C062F007C9BA3 /* RegExpCachedResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegExpCachedResult.cpp; sourceTree = "<group>"; };
86F75EFC151C062F007C9BA3 /* RegExpCachedResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpCachedResult.h; sourceTree = "<group>"; };
86F75EFD151C062F007C9BA3 /* RegExpMatchesArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegExpMatchesArray.cpp; sourceTree = "<group>"; };
@@ -1878,6 +1914,7 @@
144005170A531CB50005F061 /* minidom */,
14BD5A2D0A3E91F600BAF59C /* testapi.c */,
14D857740A4696C80032146C /* testapi.js */,
+ 86D22219167EF9440024C804 /* testapi.m */,
651122E5140469BA002B101D /* testRegExp.cpp */,
);
name = tests;
@@ -2045,6 +2082,7 @@
isa = PBXGroup;
children = (
1482B78A0A4305AB00517CFC /* APICast.h */,
+ 86E3C606167BAB87006D760A /* APIJSValue.h */,
865F408710E7D56300947361 /* APIShims.h */,
1CAA8B4A0D32C39A0041BCFF /* JavaScript.h */,
1CAA8B4B0D32C39A0041BCFF /* JavaScriptCore.h */,
@@ -2053,6 +2091,8 @@
1421359A0A677F4F00A8195E /* JSBase.cpp */,
142711380A460BBB0080EEEA /* JSBase.h */,
140D17D60E8AD4A9000CD17D /* JSBasePrivate.h */,
+ 86F3EEB7168CCF750077B92A /* JSBlockAdaptor.h */,
+ 86F3EEB8168CCF750077B92A /* JSBlockAdaptor.mm */,
1440F8AD0A508D200005F061 /* JSCallbackConstructor.cpp */,
1440F8AC0A508D200005F061 /* JSCallbackConstructor.h */,
1440F8900A508B100005F061 /* JSCallbackFunction.cpp */,
@@ -2062,9 +2102,13 @@
A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */,
1440FCE20A51E46B0005F061 /* JSClassRef.cpp */,
1440FCE10A51E46B0005F061 /* JSClassRef.h */,
+ 86E3C607167BAB87006D760A /* JSContext.h */,
+ 86E3C608167BAB87006D760A /* JSContext.mm */,
+ 86E3C609167BAB87006D760A /* JSContextInternal.h */,
14BD5A290A3E91F600BAF59C /* JSContextRef.cpp */,
14BD5A2A0A3E91F600BAF59C /* JSContextRef.h */,
148CD1D7108CF902008163C6 /* JSContextRefPrivate.h */,
+ 86E3C60A167BAB87006D760A /* JSExport.h */,
1482B7E20A43076000517CFC /* JSObjectRef.cpp */,
1482B7E10A43076000517CFC /* JSObjectRef.h */,
A79EDB0811531CD60019E912 /* JSObjectRefPrivate.h */,
@@ -2077,11 +2121,21 @@
1482B74B0A43032800517CFC /* JSStringRef.h */,
146AAB370B66A94400E55F16 /* JSStringRefCF.cpp */,
146AAB2A0B66A84900E55F16 /* JSStringRefCF.h */,
+ 86E3C60D167BAB87006D760A /* JSValue.mm */,
+ 86E3C60E167BAB87006D760A /* JSValueInternal.h */,
14BD5A2B0A3E91F600BAF59C /* JSValueRef.cpp */,
1482B6EA0A4300B300517CFC /* JSValueRef.h */,
+ 86E3C60F167BAB87006D760A /* JSVirtualMachine.h */,
+ 86E3C610167BAB87006D760A /* JSVirtualMachine.mm */,
+ 86E3C611167BAB87006D760A /* JSVirtualMachineInternal.h */,
A7482E37116A697B003B0712 /* JSWeakObjectMapRefInternal.h */,
A7482B7A1166CDEA003B0712 /* JSWeakObjectMapRefPrivate.cpp */,
A7482B791166CDEA003B0712 /* JSWeakObjectMapRefPrivate.h */,
+ 86E3C60B167BAB87006D760A /* JSWrapperMap.mm */,
+ 86E3C60C167BAB87006D760A /* JSWrapperMap.h */,
+ 86F3EEB9168CCF750077B92A /* ObjCCallbackFunction.h */,
+ 86F3EEBA168CCF750077B92A /* ObjCCallbackFunction.mm */,
+ 86F3EEB616855A5B0077B92A /* ObjcRuntimeExtras.h */,
E124A8F60E555775003091F1 /* OpaqueJSString.cpp */,
E124A8F50E555775003091F1 /* OpaqueJSString.h */,
5DE3D0F40DD8DDFB00468714 /* WebKitAvailability.h */,
@@ -3205,6 +3259,17 @@
0F05C3B41683CF9200BAF45B /* DFGArrayifySlowPathGenerator.h in Headers */,
0F5EF91F16878F7D003E5C25 /* JITThunks.h in Headers */,
0F572D4F16879FDD00E57FBD /* ThunkGenerator.h in Headers */,
+ 86E3C612167BABD7006D760A /* APIJSValue.h in Headers */,
+ 86E3C613167BABD7006D760A /* JSContext.h in Headers */,
+ 86E3C614167BABD7006D760A /* JSExport.h in Headers */,
+ 86E3C615167BABD7006D760A /* JSVirtualMachine.h in Headers */,
+ 86E3C617167BABEE006D760A /* JSContextInternal.h in Headers */,
+ 86E3C619167BABEE006D760A /* JSWrapperMap.h in Headers */,
+ 86E3C61B167BABEE006D760A /* JSValueInternal.h in Headers */,
+ 86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */,
+ 86F3EEBB168CDE930077B92A /* JSBlockAdaptor.h in Headers */,
+ 86F3EEBD168CDE930077B92A /* ObjCCallbackFunction.h in Headers */,
+ 86F3EEBF168CDE930077B92A /* ObjcRuntimeExtras.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3528,6 +3593,7 @@
buildActionMask = 2147483647;
files = (
1440F6100A4F85670005F061 /* testapi.c in Sources */,
+ 86D2221A167EF9440024C804 /* testapi.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3825,6 +3891,12 @@
A7C0C4AD1681067E0017011D /* JSScriptRef.cpp in Sources */,
0FEFC9AA1681A3B300567F53 /* DFGOSRExitJumpPlaceholder.cpp in Sources */,
0F5EF91E16878F7A003E5C25 /* JITThunks.cpp in Sources */,
+ 86E3C616167BABEE006D760A /* JSContext.mm in Sources */,
+ 86E3C618167BABEE006D760A /* JSWrapperMap.mm in Sources */,
+ 86E3C61A167BABEE006D760A /* JSValue.mm in Sources */,
+ 86E3C61C167BABEE006D760A /* JSVirtualMachine.mm in Sources */,
+ 86F3EEBC168CDE930077B92A /* JSBlockAdaptor.mm in Sources */,
+ 86F3EEBE168CDE930077B92A /* ObjCCallbackFunction.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
1 Source/JavaScriptCore/runtime/JSGlobalData.cpp
@@ -185,6 +185,7 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, HeapType heapType)
, m_timeoutCount(512)
#endif
, m_newStringsSinceLastHashConst(0)
+ , m_apiData(0)
#if ENABLE(ASSEMBLER)
, m_canUseAssembler(enableAssembler(executableAllocator))
#endif
View
2 Source/JavaScriptCore/runtime/JSGlobalData.h
@@ -453,6 +453,8 @@ namespace JSC {
JS_EXPORT_PRIVATE void discardAllCode();
+ void *m_apiData;
+
private:
friend class LLIntOffsetsExtractor;
View
1 Source/JavaScriptCore/runtime/JSGlobalObject.cpp
@@ -114,6 +114,7 @@ JSGlobalObject::JSGlobalObject(JSGlobalData& globalData, Structure* structure, c
, m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
, m_evalEnabled(true)
, m_globalObjectMethodTable(globalObjectMethodTable ? globalObjectMethodTable : &s_globalObjectMethodTable)
+ , m_apiData(0)
{
}
View
2 Source/JavaScriptCore/runtime/JSGlobalObject.h
@@ -196,6 +196,8 @@ namespace JSC {
bool hasDebugger() const { return m_debugger; }
bool hasProfiler() const { return globalObjectMethodTable()->supportsProfiling(this); }
+ void* m_apiData;
+
protected:
JS_EXPORT_PRIVATE explicit JSGlobalObject(JSGlobalData&, Structure*, const GlobalObjectMethodTable* = 0);
View
13 Source/WTF/ChangeLog
@@ -1,3 +1,16 @@
+2012-12-31 Gavin Barraclough <barraclough@apple.com>
+
+ Objective-C API for JavaScriptCore
+ https://bugs.webkit.org/show_bug.cgi?id=105889
+
+ Reviewed by Filip Pizlo.
+
+ * wtf/WTFThreadData.cpp:
+ (WTF::WTFThreadData::WTFThreadData):
+ * wtf/WTFThreadData.h:
+ (WTFThreadData):
+ - Added m_apiData - provide convenient storage for use by the API.
+
2012-12-28 Ilya Tikhonovsky <loislo@chromium.org>
Web Inspector: Native Memory Instrumentation: instrument not instrumented members.
View
3 Source/WTF/wtf/WTFThreadData.cpp
@@ -32,7 +32,8 @@ namespace WTF {
ThreadSpecific<WTFThreadData>* WTFThreadData::staticData;
WTFThreadData::WTFThreadData()
- : m_atomicStringTable(0)
+ : m_apiData(0)
+ , m_atomicStringTable(0)
, m_atomicStringTableDestructor(0)
#if USE(JSC)
, m_defaultIdentifierTable(new JSC::IdentifierTable())
View
2 Source/WTF/wtf/WTFThreadData.h
@@ -123,6 +123,8 @@ class WTFThreadData {
#endif
#endif // USE(JSC)
+ void* m_apiData;
+
private:
AtomicStringTable* m_atomicStringTable;
AtomicStringTableDestructor m_atomicStringTableDestructor;

0 comments on commit fc38188

Please sign in to comment.