|  | /* | 
|  | * Copyright (C) 2013-2018 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 "config.h" | 
|  |  | 
|  | #import "APICast.h" | 
|  | #import "DateInstance.h" | 
|  | #import "Error.h" | 
|  | #import "Exception.h" | 
|  | #import "JavaScriptCore.h" | 
|  | #import "JSContextInternal.h" | 
|  | #import "JSObjectRefPrivate.h" | 
|  | #import "JSVirtualMachineInternal.h" | 
|  | #import "JSValueInternal.h" | 
|  | #import "JSValuePrivate.h" | 
|  | #import "JSWrapperMap.h" | 
|  | #import "MarkedJSValueRefArray.h" | 
|  | #import "ObjcRuntimeExtras.h" | 
|  | #import "JSCInlines.h" | 
|  | #import "JSCJSValue.h" | 
|  | #import "Strong.h" | 
|  | #import "StrongInlines.h" | 
|  | #import <wtf/Expected.h> | 
|  | #import <wtf/HashMap.h> | 
|  | #import <wtf/HashSet.h> | 
|  | #import <wtf/Lock.h> | 
|  | #import <wtf/Vector.h> | 
|  | #import <wtf/text/WTFString.h> | 
|  | #import <wtf/text/StringHash.h> | 
|  |  | 
|  | #if ENABLE(REMOTE_INSPECTOR) | 
|  | #import "CallFrame.h" | 
|  | #import "JSGlobalObject.h" | 
|  | #import "JSGlobalObjectInspectorController.h" | 
|  | #endif | 
|  |  | 
|  | #if JSC_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; | 
|  | } | 
|  |  | 
|  | - (void)dealloc | 
|  | { | 
|  | JSValueUnprotect([_context JSGlobalContextRef], m_value); | 
|  | [_context release]; | 
|  | _context = nil; | 
|  | [super dealloc]; | 
|  | } | 
|  |  | 
|  | - (NSString *)description | 
|  | { | 
|  | if (id wrapped = tryUnwrapObjcObject([_context JSGlobalContextRef], m_value)) | 
|  | return [wrapped description]; | 
|  | return [self toString]; | 
|  | } | 
|  |  | 
|  | - (JSValueRef)JSValueRef | 
|  | { | 
|  | return m_value; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:objectToValue(context, value) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:JSValueMakeBoolean([context JSGlobalContextRef], value) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNewObjectInContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:JSObjectMake([context JSGlobalContextRef], 0, 0) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNewArrayInContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:JSObjectMakeArray([context JSGlobalContextRef], 0, NULL, 0) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context | 
|  | { | 
|  | auto patternString = OpaqueJSString::tryCreate(pattern); | 
|  | auto flagsString = OpaqueJSString::tryCreate(flags); | 
|  | JSValueRef arguments[2] = { JSValueMakeString([context JSGlobalContextRef], patternString.get()), JSValueMakeString([context JSGlobalContextRef], flagsString.get()) }; | 
|  | return [JSValue valueWithJSValueRef:JSObjectMakeRegExp([context JSGlobalContextRef], 2, arguments, 0) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context | 
|  | { | 
|  | auto string = OpaqueJSString::tryCreate(message); | 
|  | JSValueRef argument = JSValueMakeString([context JSGlobalContextRef], string.get()); | 
|  | return [JSValue valueWithJSValueRef:JSObjectMakeError([context JSGlobalContextRef], 1, &argument, 0) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNullInContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:JSValueMakeNull([context JSGlobalContextRef]) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithUndefinedInContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithJSValueRef:JSValueMakeUndefined([context JSGlobalContextRef]) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNewSymbolFromDescription:(NSString *)description inContext:(JSContext *)context | 
|  | { | 
|  | auto string = OpaqueJSString::tryCreate(description); | 
|  | return [JSValue valueWithJSValueRef:JSValueMakeSymbol([context JSGlobalContextRef], string.get()) inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNewPromiseInContext:(JSContext *)context fromExecutor:(void (^)(JSValue *, JSValue *))executor | 
|  | { | 
|  | JSObjectRef resolve; | 
|  | JSObjectRef reject; | 
|  | JSValueRef exception = nullptr; | 
|  | JSObjectRef promise = JSObjectMakeDeferredPromise([context JSGlobalContextRef], &resolve, &reject, &exception); | 
|  | if (exception) { | 
|  | [context notifyException:exception]; | 
|  | return [JSValue valueWithUndefinedInContext:context]; | 
|  | } | 
|  |  | 
|  | JSValue *result = [JSValue valueWithJSValueRef:promise inContext:context]; | 
|  | JSValue *rejection = [JSValue valueWithJSValueRef:reject inContext:context]; | 
|  | CallbackData callbackData; | 
|  | const size_t argumentCount = 2; | 
|  | JSValueRef arguments[argumentCount]; | 
|  | arguments[0] = resolve; | 
|  | arguments[1] = reject; | 
|  |  | 
|  | [context beginCallbackWithData:&callbackData calleeValue:nullptr thisValue:promise argumentCount:argumentCount arguments:arguments]; | 
|  | executor([JSValue valueWithJSValueRef:resolve inContext:context], rejection); | 
|  | if (context.exception) | 
|  | [rejection callWithArguments:@[context.exception]]; | 
|  | [context endCallbackWithData:&callbackData]; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNewPromiseResolvedWithResult:(id)result inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithNewPromiseInContext:context fromExecutor:^(JSValue *resolve, JSValue *) { | 
|  | [resolve callWithArguments:@[result]]; | 
|  | }]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithNewPromiseRejectedWithReason:(id)reason inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithNewPromiseInContext:context fromExecutor:^(JSValue *, JSValue *reject) { | 
|  | [reject callWithArguments:@[reason]]; | 
|  | }]; | 
|  | } | 
|  |  | 
|  | - (id)toObject | 
|  | { | 
|  | return valueToObject(_context, m_value); | 
|  | } | 
|  |  | 
|  | - (id)toObjectOfClass:(Class)expectedClass | 
|  | { | 
|  | id result = [self toObject]; | 
|  | return [result isKindOfClass:expectedClass] ? result : nil; | 
|  | } | 
|  |  | 
|  | - (BOOL)toBool | 
|  | { | 
|  | return JSValueToBoolean([_context JSGlobalContextRef], m_value); | 
|  | } | 
|  |  | 
|  | - (double)toDouble | 
|  | { | 
|  | JSValueRef exception = 0; | 
|  | double result = JSValueToNumber([_context JSGlobalContextRef], 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 | 
|  | { | 
|  | JSValueRef exception = 0; | 
|  | id result = valueToNumber([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | [_context notifyException:exception]; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | - (NSString *)toString | 
|  | { | 
|  | JSValueRef exception = 0; | 
|  | id result = valueToString([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | [_context notifyException:exception]; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | - (NSDate *)toDate | 
|  | { | 
|  | JSValueRef exception = 0; | 
|  | id result = valueToDate([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | [_context notifyException:exception]; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | - (NSArray *)toArray | 
|  | { | 
|  | JSValueRef exception = 0; | 
|  | id result = valueToArray([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | [_context notifyException:exception]; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | - (NSDictionary *)toDictionary | 
|  | { | 
|  | JSValueRef exception = 0; | 
|  | id result = valueToDictionary([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | [_context notifyException:exception]; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | template<typename Result, typename NSStringFunction, typename JSValueFunction, typename... Types> | 
|  | inline Expected<Result, JSValueRef> performPropertyOperation(NSStringFunction stringFunction, JSValueFunction jsFunction, JSValue* value, id propertyKey, Types... arguments) | 
|  | { | 
|  | JSContext* context = [value context]; | 
|  | JSValueRef exception = nullptr; | 
|  | JSObjectRef object = JSValueToObject([context JSGlobalContextRef], [value JSValueRef], &exception); | 
|  | if (exception) | 
|  | return Unexpected<JSValueRef>(exception); | 
|  |  | 
|  | Result result; | 
|  | // If it's a NSString already, reduce indirection and just pass the NSString. | 
|  | if ([propertyKey isKindOfClass:[NSString class]]) { | 
|  | auto name = OpaqueJSString::tryCreate((NSString *)propertyKey); | 
|  | result = stringFunction([context JSGlobalContextRef], object, name.get(), arguments..., &exception); | 
|  | } else | 
|  | result = jsFunction([context JSGlobalContextRef], object, [[JSValue valueWithObject:propertyKey inContext:context] JSValueRef], arguments..., &exception); | 
|  | return Expected<Result, JSValueRef>(result); | 
|  | } | 
|  |  | 
|  | - (JSValue *)valueForProperty:(id)key | 
|  | { | 
|  | auto result = performPropertyOperation<JSValueRef>(JSObjectGetProperty, JSObjectGetPropertyForKey, self, key); | 
|  | if (!result) | 
|  | return [_context valueFromNotifyException:result.error()]; | 
|  |  | 
|  | return [JSValue valueWithJSValueRef:result.value() inContext:_context]; | 
|  | } | 
|  |  | 
|  |  | 
|  | - (void)setValue:(id)value forProperty:(JSValueProperty)key | 
|  | { | 
|  | // We need Unit business because void can't be assigned to in performPropertyOperation and I don't want to duplicate the code... | 
|  | using Unit = std::tuple<>; | 
|  | auto stringSetProperty = [] (auto... args) -> Unit { | 
|  | JSObjectSetProperty(args...); | 
|  | return { }; | 
|  | }; | 
|  |  | 
|  | auto jsValueSetProperty = [] (auto... args) -> Unit { | 
|  | JSObjectSetPropertyForKey(args...); | 
|  | return { }; | 
|  | }; | 
|  |  | 
|  | auto result = performPropertyOperation<Unit>(stringSetProperty, jsValueSetProperty, self, key, objectToValue(_context, value), kJSPropertyAttributeNone); | 
|  | if (!result) { | 
|  | [_context notifyException:result.error()]; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | - (BOOL)deleteProperty:(JSValueProperty)key | 
|  | { | 
|  | Expected<BOOL, JSValueRef> result = performPropertyOperation<BOOL>(JSObjectDeleteProperty, JSObjectDeletePropertyForKey, self, key); | 
|  | if (!result) | 
|  | return [_context boolFromNotifyException:result.error()]; | 
|  | return result.value(); | 
|  | } | 
|  |  | 
|  | - (BOOL)hasProperty:(JSValueProperty)key | 
|  | { | 
|  | // The C-api doesn't return an exception value for the string version of has property. | 
|  | auto stringHasProperty = [] (JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef*) -> BOOL { | 
|  | return JSObjectHasProperty(ctx, object, propertyName); | 
|  | }; | 
|  |  | 
|  | Expected<BOOL, JSValueRef> result = performPropertyOperation<BOOL>(stringHasProperty, JSObjectHasPropertyForKey, self, key); | 
|  | if (!result) | 
|  | return [_context boolFromNotifyException:result.error()]; | 
|  | return result.value(); | 
|  | } | 
|  |  | 
|  | - (void)defineProperty:(JSValueProperty)key descriptor:(id)descriptor | 
|  | { | 
|  | [[_context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, key, descriptor ]]; | 
|  | } | 
|  |  | 
|  | - (JSValue *)valueAtIndex:(NSUInteger)index | 
|  | { | 
|  | // Properties that are higher than an unsigned value can hold are converted to a double then inserted as a normal property. | 
|  | // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in get(). | 
|  | if (index != (unsigned)index) | 
|  | return [self valueForProperty:[[JSValue valueWithDouble:index inContext:_context] toString]]; | 
|  |  | 
|  | JSValueRef exception = 0; | 
|  | JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | JSValueRef result = JSObjectGetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | return [JSValue valueWithJSValueRef:result inContext:_context]; | 
|  | } | 
|  |  | 
|  | - (void)setValue:(id)value atIndex:(NSUInteger)index | 
|  | { | 
|  | // Properties that are higher than an unsigned value can hold are converted to a double, then inserted as a normal property. | 
|  | // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in putByIndex(). | 
|  | if (index != (unsigned)index) | 
|  | return [self setValue:value forProperty:[[JSValue valueWithDouble:index inContext:_context] toString]]; | 
|  |  | 
|  | JSValueRef exception = 0; | 
|  | JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) { | 
|  | [_context notifyException:exception]; | 
|  | return; | 
|  | } | 
|  |  | 
|  | JSObjectSetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, objectToValue(_context, value), &exception); | 
|  | if (exception) { | 
|  | [_context notifyException:exception]; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | - (BOOL)isUndefined | 
|  | { | 
|  | #if !CPU(ADDRESS64) | 
|  | return JSValueIsUndefined([_context JSGlobalContextRef], m_value); | 
|  | #else | 
|  | return toJS(m_value).isUndefined(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | - (BOOL)isNull | 
|  | { | 
|  | #if !CPU(ADDRESS64) | 
|  | return JSValueIsNull([_context JSGlobalContextRef], m_value); | 
|  | #else | 
|  | return toJS(m_value).isNull(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | - (BOOL)isBoolean | 
|  | { | 
|  | #if !CPU(ADDRESS64) | 
|  | return JSValueIsBoolean([_context JSGlobalContextRef], m_value); | 
|  | #else | 
|  | return toJS(m_value).isBoolean(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | - (BOOL)isNumber | 
|  | { | 
|  | #if !CPU(ADDRESS64) | 
|  | return JSValueIsNumber([_context JSGlobalContextRef], m_value); | 
|  | #else | 
|  | return toJS(m_value).isNumber(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | - (BOOL)isString | 
|  | { | 
|  | #if !CPU(ADDRESS64) | 
|  | return JSValueIsString([_context JSGlobalContextRef], m_value); | 
|  | #else | 
|  | return toJS(m_value).isString(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | - (BOOL)isObject | 
|  | { | 
|  | #if !CPU(ADDRESS64) | 
|  | return JSValueIsObject([_context JSGlobalContextRef], m_value); | 
|  | #else | 
|  | return toJS(m_value).isObject(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | - (BOOL)isSymbol | 
|  | { | 
|  | #if !CPU(ADDRESS64) | 
|  | return JSValueIsSymbol([_context JSGlobalContextRef], m_value); | 
|  | #else | 
|  | return toJS(m_value).isSymbol(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | - (BOOL)isArray | 
|  | { | 
|  | return JSValueIsArray([_context JSGlobalContextRef], m_value); | 
|  | } | 
|  |  | 
|  | - (BOOL)isDate | 
|  | { | 
|  | return JSValueIsDate([_context JSGlobalContextRef], m_value); | 
|  | } | 
|  |  | 
|  | - (BOOL)isEqualToObject:(id)value | 
|  | { | 
|  | return JSValueIsStrictEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value)); | 
|  | } | 
|  |  | 
|  | - (BOOL)isEqualWithTypeCoercionToObject:(id)value | 
|  | { | 
|  | JSValueRef exception = 0; | 
|  | BOOL result = JSValueIsEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value), &exception); | 
|  | if (exception) | 
|  | return [_context boolFromNotifyException:exception]; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | - (BOOL)isInstanceOf:(id)value | 
|  | { | 
|  | JSValueRef exception = 0; | 
|  | JSObjectRef constructor = JSValueToObject([_context JSGlobalContextRef], objectToValue(_context, value), &exception); | 
|  | if (exception) | 
|  | return [_context boolFromNotifyException:exception]; | 
|  |  | 
|  | BOOL result = JSValueIsInstanceOfConstructor([_context JSGlobalContextRef], m_value, constructor, &exception); | 
|  | if (exception) | 
|  | return [_context boolFromNotifyException:exception]; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | - (JSValue *)callWithArguments:(NSArray *)argumentArray | 
|  | { | 
|  | JSC::JSGlobalObject* globalObject = toJS([_context JSGlobalContextRef]); | 
|  | JSC::VM& vm = globalObject->vm(); | 
|  | JSC::JSLockHolder locker(vm); | 
|  |  | 
|  | NSUInteger argumentCount = [argumentArray count]; | 
|  | JSC::MarkedJSValueRefArray arguments([_context JSGlobalContextRef], argumentCount); | 
|  | for (unsigned i = 0; i < argumentCount; ++i) | 
|  | arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]); | 
|  |  | 
|  | JSValueRef exception = 0; | 
|  | JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, 0, argumentCount, arguments.data(), &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | return [JSValue valueWithJSValueRef:result inContext:_context]; | 
|  | } | 
|  |  | 
|  | - (JSValue *)constructWithArguments:(NSArray *)argumentArray | 
|  | { | 
|  | JSC::JSGlobalObject* globalObject = toJS([_context JSGlobalContextRef]); | 
|  | JSC::VM& vm = globalObject->vm(); | 
|  | JSC::JSLockHolder locker(vm); | 
|  |  | 
|  | NSUInteger argumentCount = [argumentArray count]; | 
|  | JSC::MarkedJSValueRefArray arguments([_context JSGlobalContextRef], argumentCount); | 
|  | for (unsigned i = 0; i < argumentCount; ++i) | 
|  | arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]); | 
|  |  | 
|  | JSValueRef exception = 0; | 
|  | JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | JSObjectRef result = JSObjectCallAsConstructor([_context JSGlobalContextRef], object, argumentCount, arguments.data(), &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | return [JSValue valueWithJSValueRef:result inContext:_context]; | 
|  | } | 
|  |  | 
|  | - (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments | 
|  | { | 
|  | JSC::JSGlobalObject* globalObject = toJS([_context JSGlobalContextRef]); | 
|  | JSC::VM& vm = globalObject->vm(); | 
|  | JSC::JSLockHolder locker(vm); | 
|  |  | 
|  | NSUInteger argumentCount = [arguments count]; | 
|  | JSC::MarkedJSValueRefArray argumentArray([_context JSGlobalContextRef], argumentCount); | 
|  | for (unsigned i = 0; i < argumentCount; ++i) | 
|  | argumentArray[i] = objectToValue(_context, [arguments objectAtIndex:i]); | 
|  |  | 
|  | JSValueRef exception = 0; | 
|  | JSObjectRef thisObject = JSValueToObject([_context JSGlobalContextRef], m_value, &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | auto name = OpaqueJSString::tryCreate(method); | 
|  | JSValueRef function = JSObjectGetProperty([_context JSGlobalContextRef], thisObject, name.get(), &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], function, &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, thisObject, argumentCount, argumentArray.data(), &exception); | 
|  | if (exception) | 
|  | return [_context valueFromNotifyException:exception]; | 
|  |  | 
|  | return [JSValue valueWithJSValueRef:result inContext:_context]; | 
|  | } | 
|  |  | 
|  | @end | 
|  |  | 
|  | @implementation JSValue(StructSupport) | 
|  |  | 
|  | - (CGPoint)toPoint | 
|  | { | 
|  | return (CGPoint){ | 
|  | static_cast<CGFloat>([self[@"x"] toDouble]), | 
|  | static_cast<CGFloat>([self[@"y"] toDouble]) | 
|  | }; | 
|  | } | 
|  |  | 
|  | - (NSRange)toRange | 
|  | { | 
|  | return (NSRange){ | 
|  | [[self[@"location"] toNumber] unsignedIntegerValue], | 
|  | [[self[@"length"] toNumber] unsignedIntegerValue] | 
|  | }; | 
|  | } | 
|  |  | 
|  | - (CGRect)toRect | 
|  | { | 
|  | return (CGRect){ | 
|  | [self toPoint], | 
|  | [self toSize] | 
|  | }; | 
|  | } | 
|  |  | 
|  | - (CGSize)toSize | 
|  | { | 
|  | return (CGSize){ | 
|  | static_cast<CGFloat>([self[@"width"] toDouble]), | 
|  | static_cast<CGFloat>([self[@"height"] toDouble]) | 
|  | }; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithObject:@{ | 
|  | @"x":@(point.x), | 
|  | @"y":@(point.y) | 
|  | } inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithObject:@{ | 
|  | @"location":@(range.location), | 
|  | @"length":@(range.length) | 
|  | } inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithObject:@{ | 
|  | @"x":@(rect.origin.x), | 
|  | @"y":@(rect.origin.y), | 
|  | @"width":@(rect.size.width), | 
|  | @"height":@(rect.size.height) | 
|  | } inContext:context]; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context | 
|  | { | 
|  | return [JSValue valueWithObject:@{ | 
|  | @"width":@(size.width), | 
|  | @"height":@(size.height) | 
|  | } inContext:context]; | 
|  | } | 
|  |  | 
|  | @end | 
|  |  | 
|  | @implementation JSValue(SubscriptSupport) | 
|  |  | 
|  | - (JSValue *)objectForKeyedSubscript:(id)key | 
|  | { | 
|  | return [self valueForProperty:key]; | 
|  | } | 
|  |  | 
|  | - (JSValue *)objectAtIndexedSubscript:(NSUInteger)index | 
|  | { | 
|  | return [self valueAtIndex:index]; | 
|  | } | 
|  |  | 
|  | - (void)setObject:(id)object forKeyedSubscript:(id)key | 
|  | { | 
|  | [self setValue:object forProperty:key]; | 
|  | } | 
|  |  | 
|  | - (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index | 
|  | { | 
|  | [self setValue:object atIndex:index]; | 
|  | } | 
|  |  | 
|  | @end | 
|  |  | 
|  | inline bool isDate(JSC::VM& vm, JSObjectRef object, JSGlobalContextRef context) | 
|  | { | 
|  | JSC::JSLockHolder locker(toJS(context)); | 
|  | return toJS(object)->inherits<JSC::DateInstance>(vm); | 
|  | } | 
|  |  | 
|  | inline bool isArray(JSC::VM& vm, JSObjectRef object, JSGlobalContextRef context) | 
|  | { | 
|  | JSC::JSLockHolder locker(toJS(context)); | 
|  | return toJS(object)->inherits<JSC::JSArray>(vm); | 
|  | } | 
|  |  | 
|  | @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 take(); | 
|  | bool isWorkListEmpty() const { return !m_worklist.size(); } | 
|  |  | 
|  | private: | 
|  | JSGlobalContextRef m_context; | 
|  | HashMap<JSValueRef, __unsafe_unretained id> m_objectMap; | 
|  | Vector<Task> m_worklist; | 
|  | Vector<JSC::Strong<JSC::Unknown>> m_jsValues; | 
|  | }; | 
|  |  | 
|  | inline id JSContainerConvertor::convert(JSValueRef value) | 
|  | { | 
|  | auto 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) | 
|  | { | 
|  | JSC::JSGlobalObject* globalObject = toJS(m_context); | 
|  | m_jsValues.append(JSC::Strong<JSC::Unknown>(globalObject->vm(), toJSForGC(globalObject, task.js))); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #if ENABLE(REMOTE_INSPECTOR) | 
|  | static void reportExceptionToInspector(JSGlobalContextRef context, JSC::JSValue exceptionValue) | 
|  | { | 
|  | JSC::JSGlobalObject* globalObject = toJS(context); | 
|  | JSC::VM& vm = globalObject->vm(); | 
|  | JSC::Exception* exception = JSC::Exception::create(vm, exceptionValue); | 
|  | globalObject->inspectorController().reportAPIException(globalObject, exception); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef context, JSValueRef value) | 
|  | { | 
|  | JSC::JSGlobalObject* globalObject = toJS(context); | 
|  | JSC::VM& vm = globalObject->vm(); | 
|  |  | 
|  | 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 = @(JSValueToNumber(context, value, 0)); | 
|  | } else if (JSValueIsString(context, value)) { | 
|  | // Would be nice to unique strings, too. | 
|  | auto jsstring = adoptRef(JSValueToStringCopy(context, value, 0)); | 
|  | primitive = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, jsstring.get())).bridgingAutorelease(); | 
|  | } else if (JSValueIsNull(context, value)) | 
|  | primitive = [NSNull null]; | 
|  | else { | 
|  | ASSERT(JSValueIsUndefined(context, value)); | 
|  | primitive = nil; | 
|  | } | 
|  | return { value, primitive, ContainerNone }; | 
|  | } | 
|  |  | 
|  | JSObjectRef object = JSValueToObject(context, value, 0); | 
|  |  | 
|  | if (id wrapped = tryUnwrapObjcObject(context, object)) | 
|  | return { object, wrapped, ContainerNone }; | 
|  |  | 
|  | if (isDate(vm, object, context)) | 
|  | return { object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0) / 1000.0], ContainerNone }; | 
|  |  | 
|  | if (isArray(vm, object, context)) | 
|  | return { object, [NSMutableArray array], ContainerArray }; | 
|  |  | 
|  | return { object, [NSMutableDictionary dictionary], ContainerDictionary }; | 
|  | } | 
|  |  | 
|  | static id containerValueToObject(JSGlobalContextRef context, JSContainerConvertor::Task task) | 
|  | { | 
|  | ASSERT(task.type != ContainerNone); | 
|  | JSC::JSLockHolder locker(toJS(context)); | 
|  | JSContainerConvertor convertor(context); | 
|  | convertor.add(task); | 
|  | ASSERT(!convertor.isWorkListEmpty()); | 
|  |  | 
|  | do { | 
|  | JSContainerConvertor::Task current = 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; | 
|  |  | 
|  | auto lengthString = OpaqueJSString::tryCreate("length"_s); | 
|  | unsigned length = JSC::toUInt32(JSValueToNumber(context, JSObjectGetProperty(context, js, lengthString.get(), 0), 0)); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | JSC::JSLockHolder locker(toJS(context)); | 
|  |  | 
|  | 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[(__bridge NSString *)adoptCF(JSStringCopyCFString(kCFAllocatorDefault, propertyName)).get()] = objc; | 
|  | } | 
|  |  | 
|  | JSPropertyNameArrayRelease(propertyNameArray); | 
|  | } | 
|  |  | 
|  | } while (!convertor.isWorkListEmpty()); | 
|  |  | 
|  | return task.objc; | 
|  | } | 
|  |  | 
|  | id valueToObject(JSContext *context, JSValueRef value) | 
|  | { | 
|  | JSContainerConvertor::Task result = valueToObjectWithoutCopy([context JSGlobalContextRef], value); | 
|  | if (result.type == ContainerNone) | 
|  | return result.objc; | 
|  | return containerValueToObject([context JSGlobalContextRef], 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 @(*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; | 
|  | } | 
|  |  | 
|  | auto jsstring = adoptRef(JSValueToStringCopy(context, value, exception)); | 
|  | if (*exception) { | 
|  | ASSERT(!jsstring); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | return adoptCF(JSStringCopyCFString(kCFAllocatorDefault, jsstring.get())).bridgingAutorelease(); | 
|  | } | 
|  |  | 
|  | 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) / 1000.0; | 
|  | 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, { value, [NSMutableArray array], ContainerArray}); | 
|  |  | 
|  | JSC::JSLockHolder locker(toJS(context)); | 
|  | if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value))) { | 
|  | JSC::JSObject* exceptionObject = JSC::createTypeError(toJS(context), "Cannot convert primitive to NSArray"_s); | 
|  | *exception = toRef(exceptionObject); | 
|  | #if ENABLE(REMOTE_INSPECTOR) | 
|  | reportExceptionToInspector(context, exceptionObject); | 
|  | #endif | 
|  | } | 
|  | 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, { value, [NSMutableDictionary dictionary], ContainerDictionary}); | 
|  |  | 
|  | JSC::JSLockHolder locker(toJS(context)); | 
|  | if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value))) { | 
|  | JSC::JSObject* exceptionObject = JSC::createTypeError(toJS(context), "Cannot convert primitive to NSDictionary"_s); | 
|  | *exception = toRef(exceptionObject); | 
|  | #if ENABLE(REMOTE_INSPECTOR) | 
|  | reportExceptionToInspector(context, exceptionObject); | 
|  | #endif | 
|  | } | 
|  | 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 take(); | 
|  | bool isWorkListEmpty() const { return !m_worklist.size(); } | 
|  |  | 
|  | private: | 
|  | JSContext *m_context; | 
|  | HashMap<__unsafe_unretained id, JSValueRef> m_objectMap; | 
|  | Vector<Task> m_worklist; | 
|  | Vector<JSC::Strong<JSC::Unknown>> m_jsValues; | 
|  | }; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | JSC::JSGlobalObject* globalObject = toJS(m_context.JSGlobalContextRef); | 
|  | m_jsValues.append(JSC::Strong<JSC::Unknown>(globalObject->vm(), toJSForGC(globalObject, task.js))); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | inline bool isNSBoolean(id object) | 
|  | { | 
|  | ASSERT([@YES class] == [@NO class]); | 
|  | ASSERT([@YES class] != [NSNumber class]); | 
|  | ASSERT([[@YES class] isSubclassOfClass:[NSNumber class]]); | 
|  | return [object isKindOfClass:[@YES class]]; | 
|  | } | 
|  |  | 
|  | static ObjcContainerConvertor::Task objectToValueWithoutCopy(JSContext *context, id object) | 
|  | { | 
|  | JSGlobalContextRef contextRef = [context JSGlobalContextRef]; | 
|  |  | 
|  | if (!object) | 
|  | return { object, JSValueMakeUndefined(contextRef), ContainerNone }; | 
|  |  | 
|  | if (!class_conformsToProtocol(object_getClass(object), getJSExportProtocol())) { | 
|  | if ([object isKindOfClass:[NSArray class]]) | 
|  | return { object, JSObjectMakeArray(contextRef, 0, NULL, 0), ContainerArray }; | 
|  |  | 
|  | if ([object isKindOfClass:[NSDictionary class]]) | 
|  | return { object, JSObjectMake(contextRef, 0, 0), ContainerDictionary }; | 
|  |  | 
|  | if ([object isKindOfClass:[NSNull class]]) | 
|  | return { object, JSValueMakeNull(contextRef), ContainerNone }; | 
|  |  | 
|  | if ([object isKindOfClass:[JSValue class]]) | 
|  | return { object, ((JSValue *)object)->m_value, ContainerNone }; | 
|  |  | 
|  | if ([object isKindOfClass:[NSString class]]) { | 
|  | auto string = OpaqueJSString::tryCreate((NSString *)object); | 
|  | return { object, JSValueMakeString(contextRef, string.get()), ContainerNone }; | 
|  | } | 
|  |  | 
|  | if ([object isKindOfClass:[NSNumber class]]) { | 
|  | if (isNSBoolean(object)) | 
|  | return { object, JSValueMakeBoolean(contextRef, [object boolValue]), ContainerNone }; | 
|  | return { object, JSValueMakeNumber(contextRef, [object doubleValue]), ContainerNone }; | 
|  | } | 
|  |  | 
|  | if ([object isKindOfClass:[NSDate class]]) { | 
|  | JSValueRef argument = JSValueMakeNumber(contextRef, [object timeIntervalSince1970] * 1000.0); | 
|  | JSObjectRef result = JSObjectMakeDate(contextRef, 1, &argument, 0); | 
|  | return { object, result, ContainerNone }; | 
|  | } | 
|  |  | 
|  | if ([object isKindOfClass:[JSManagedValue class]]) { | 
|  | JSValue *value = [static_cast<JSManagedValue *>(object) value]; | 
|  | if (!value) | 
|  | return  { object, JSValueMakeUndefined(contextRef), ContainerNone }; | 
|  | return { object, value->m_value, ContainerNone }; | 
|  | } | 
|  | } | 
|  |  | 
|  | return { object, valueInternalValue([context wrapperForObjCObject:object]), ContainerNone }; | 
|  | } | 
|  |  | 
|  | JSValueRef objectToValue(JSContext *context, id object) | 
|  | { | 
|  | JSGlobalContextRef contextRef = [context JSGlobalContextRef]; | 
|  |  | 
|  | ObjcContainerConvertor::Task task = objectToValueWithoutCopy(context, object); | 
|  | if (task.type == ContainerNone) | 
|  | return task.js; | 
|  |  | 
|  | JSC::JSLockHolder locker(toJS(contextRef)); | 
|  | ObjcContainerConvertor convertor(context); | 
|  | convertor.add(task); | 
|  | ASSERT(!convertor.isWorkListEmpty()); | 
|  |  | 
|  | do { | 
|  | ObjcContainerConvertor::Task current = convertor.take(); | 
|  | ASSERT(JSValueIsObject(contextRef, current.js)); | 
|  | JSObjectRef js = JSValueToObject(contextRef, 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(contextRef, js, index, convertor.convert([array objectAtIndex:index]), 0); | 
|  | } else { | 
|  | ASSERT(current.type == ContainerDictionary); | 
|  | ASSERT([current.objc isKindOfClass:[NSDictionary class]]); | 
|  | NSDictionary *dictionary = (NSDictionary *)current.objc; | 
|  | for (id key in [dictionary keyEnumerator]) { | 
|  | if ([key isKindOfClass:[NSString class]]) { | 
|  | auto propertyName = OpaqueJSString::tryCreate((NSString *)key); | 
|  | JSObjectSetProperty(contextRef, js, propertyName.get(), convertor.convert([dictionary objectForKey:key]), 0, 0); | 
|  | } | 
|  | } | 
|  | } | 
|  | } while (!convertor.isWorkListEmpty()); | 
|  |  | 
|  | return task.js; | 
|  | } | 
|  |  | 
|  | JSValueRef valueInternalValue(JSValue * value) | 
|  | { | 
|  | return value->m_value; | 
|  | } | 
|  |  | 
|  | + (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context | 
|  | { | 
|  | return [context wrapperForJSObject:value]; | 
|  | } | 
|  |  | 
|  | - (JSValue *)init | 
|  | { | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | - (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context | 
|  | { | 
|  | if (!value || !context) | 
|  | return nil; | 
|  |  | 
|  | self = [super init]; | 
|  | if (!self) | 
|  | return nil; | 
|  |  | 
|  | _context = [context retain]; | 
|  | m_value = value; | 
|  | JSValueProtect([_context JSGlobalContextRef], m_value); | 
|  | return self; | 
|  | } | 
|  |  | 
|  | struct StructTagHandler { | 
|  | SEL typeToValueSEL; | 
|  | SEL valueToTypeSEL; | 
|  | }; | 
|  | typedef HashMap<String, StructTagHandler> StructHandlers; | 
|  |  | 
|  | static StructHandlers* createStructHandlerMap() | 
|  | { | 
|  | StructHandlers* structHandlers = new StructHandlers(); | 
|  |  | 
|  | size_t valueWithXinContextLength = strlen("valueWithX:inContext:"); | 
|  | size_t toXLength = strlen("toX"); | 
|  |  | 
|  | // Step 1: find all valueWith<Foo>:inContext: class methods in JSValue. | 
|  | forEachMethodInClass(object_getClass([JSValue class]), ^(Method method){ | 
|  | SEL selector = method_getName(method); | 
|  | const char* name = sel_getName(selector); | 
|  | size_t nameLength = strlen(name); | 
|  | // Check for valueWith<Foo>:context: | 
|  | if (nameLength < valueWithXinContextLength || memcmp(name, "valueWith", 9) || memcmp(name + nameLength - 11, ":inContext:", 11)) | 
|  | return; | 
|  | // Check for [ id, SEL, <type>, <contextType> ] | 
|  | if (method_getNumberOfArguments(method) != 4) | 
|  | return; | 
|  | char idType[3]; | 
|  | // Check 2nd argument type is "@" | 
|  | { | 
|  | auto secondType = adoptSystem<char[]>(method_copyArgumentType(method, 3)); | 
|  | if (strcmp(secondType.get(), "@") != 0) | 
|  | return; | 
|  | } | 
|  | // Check result type is also "@" | 
|  | method_getReturnType(method, idType, 3); | 
|  | if (strcmp(idType, "@") != 0) | 
|  | return; | 
|  | { | 
|  | auto type = adoptSystem<char[]>(method_copyArgumentType(method, 2)); | 
|  | structHandlers->add(StringImpl::create(type.get()), (StructTagHandler) { selector, 0 }); | 
|  | } | 
|  | }); | 
|  |  | 
|  | // Step 2: find all to<Foo> instance methods in JSValue. | 
|  | forEachMethodInClass([JSValue class], ^(Method method){ | 
|  | SEL selector = method_getName(method); | 
|  | const char* name = sel_getName(selector); | 
|  | size_t nameLength = strlen(name); | 
|  | // Check for to<Foo> | 
|  | if (nameLength < toXLength || memcmp(name, "to", 2)) | 
|  | return; | 
|  | // Check for [ id, SEL ] | 
|  | if (method_getNumberOfArguments(method) != 2) | 
|  | return; | 
|  | // Try to find a matching valueWith<Foo>:context: method. | 
|  | auto type = adoptSystem<char[]>(method_copyReturnType(method)); | 
|  | StructHandlers::iterator iter = structHandlers->find(type.get()); | 
|  | if (iter == structHandlers->end()) | 
|  | return; | 
|  | StructTagHandler& handler = iter->value; | 
|  |  | 
|  | // check that strlen(<foo>) == strlen(<Foo>) | 
|  | const char* valueWithName = sel_getName(handler.typeToValueSEL); | 
|  | size_t valueWithLength = strlen(valueWithName); | 
|  | if (valueWithLength - valueWithXinContextLength != nameLength - toXLength) | 
|  | return; | 
|  | // Check that <Foo> == <Foo> | 
|  | if (memcmp(valueWithName + 9, name + 2, nameLength - toXLength - 1)) | 
|  | return; | 
|  | handler.valueToTypeSEL = selector; | 
|  | }); | 
|  |  | 
|  | // Step 3: clean up - remove entries where we found prospective valueWith<Foo>:inContext: conversions, but no matching to<Foo> methods. | 
|  | typedef HashSet<String> RemoveSet; | 
|  | RemoveSet removeSet; | 
|  | for (StructHandlers::iterator iter = structHandlers->begin(); iter != structHandlers->end(); ++iter) { | 
|  | StructTagHandler& handler = iter->value; | 
|  | if (!handler.valueToTypeSEL) | 
|  | removeSet.add(iter->key); | 
|  | } | 
|  |  | 
|  | for (RemoveSet::iterator iter = removeSet.begin(); iter != removeSet.end(); ++iter) | 
|  | structHandlers->remove(*iter); | 
|  |  | 
|  | return structHandlers; | 
|  | } | 
|  |  | 
|  | static StructTagHandler* handerForStructTag(const char* encodedType) | 
|  | { | 
|  | static Lock handerForStructTagLock; | 
|  | Locker lockHolder { handerForStructTagLock }; | 
|  |  | 
|  | static StructHandlers* structHandlers = createStructHandlerMap(); | 
|  |  | 
|  | StructHandlers::iterator iter = structHandlers->find(encodedType); | 
|  | if (iter == structHandlers->end()) | 
|  | return 0; | 
|  | return &iter->value; | 
|  | } | 
|  |  | 
|  | + (SEL)selectorForStructToValue:(const char *)structTag | 
|  | { | 
|  | StructTagHandler* handler = handerForStructTag(structTag); | 
|  | return handler ? handler->typeToValueSEL : nil; | 
|  | } | 
|  |  | 
|  | + (SEL)selectorForValueToStruct:(const char *)structTag | 
|  | { | 
|  | StructTagHandler* handler = handerForStructTag(structTag); | 
|  | return handler ? handler->valueToTypeSEL : nil; | 
|  | } | 
|  |  | 
|  | 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 |