| /* | 
 |  * Copyright (C) 2013-2022 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 | 
 |  | 
 | using JSC::Integrity::audit; | 
 |  | 
 | 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 | 
 | { | 
 |     if (_context) { | 
 |         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(JSObjectRef object, JSGlobalContextRef context) | 
 | { | 
 |     JSC::JSLockHolder locker(toJS(context)); | 
 |     return toJS(object)->inherits<JSC::DateInstance>(); | 
 | } | 
 |  | 
 | inline bool isArray(JSObjectRef object, JSGlobalContextRef context) | 
 | { | 
 |     JSC::JSLockHolder locker(toJS(context)); | 
 |     return toJS(object)->inherits<JSC::JSArray>(); | 
 | } | 
 |  | 
 | @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) | 
 | { | 
 |     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(object, context)) | 
 |         return { object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0) / 1000.0], ContainerNone }; | 
 |  | 
 |     if (isArray(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 audit(it->value); | 
 |  | 
 |     ObjcContainerConvertor::Task task = objectToValueWithoutCopy(m_context, object); | 
 |     add(task); | 
 |     return audit(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 = audit([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 audit(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 audit(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) { | 
 |         [self release]; | 
 |         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::createFromCString(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(String::fromLatin1(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(String::fromLatin1(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 |