Unreviewed, rolling out r243672.
[WebKit-https.git] / Source / JavaScriptCore / API / JSValue.mm
index d1b817d..86e10cf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * 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
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "config.h"
+
 #import "APICast.h"
-#import "APIShims.h"
 #import "DateInstance.h"
 #import "Error.h"
+#import "Exception.h"
 #import "JavaScriptCore.h"
 #import "JSContextInternal.h"
+#import "JSObjectRefPrivate.h"
 #import "JSVirtualMachineInternal.h"
 #import "JSValueInternal.h"
+#import "JSValuePrivate.h"
 #import "JSWrapperMap.h"
 #import "ObjcRuntimeExtras.h"
-#import "JSValue.h"
-#import "wtf/HashMap.h"
-#import "wtf/HashSet.h"
-#import "wtf/Vector.h"
-#import <wtf/TCSpinLock.h>
-#import "wtf/text/WTFString.h"
+#import "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/ObjCRuntimeExtras.h>
+#import <wtf/Vector.h>
+#import <wtf/text/WTFString.h>
 #import <wtf/text/StringHash.h>
 
-#if JS_OBJC_API_ENABLED
+#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";
@@ -53,442 +67,473 @@ NSString * const JSPropertyDescriptorSetKey = @"set";
 
 @implementation JSValue {
     JSValueRef m_value;
-    JSContext *m_weakContext;
+}
+
+- (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 valueWithValue:objectToValue(context, value) inContext:context];
+    return [JSValue valueWithJSValueRef:objectToValue(context, value) inContext:context];
 }
+
 + (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context
 {
-    return [JSValue valueWithValue:JSValueMakeBoolean(contextInternalContext(context), value) inContext:context];
+    return [JSValue valueWithJSValueRef:JSValueMakeBoolean([context JSGlobalContextRef], value) inContext:context];
 }
+
 + (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context
 {
-    return [JSValue valueWithValue:JSValueMakeNumber(contextInternalContext(context), value) inContext:context];
+    return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
 }
+
 + (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context
 {
-    return [JSValue valueWithValue:JSValueMakeNumber(contextInternalContext(context), value) inContext:context];
+    return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
 }
+
 + (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context
 {
-    return [JSValue valueWithValue:JSValueMakeNumber(contextInternalContext(context), value) inContext:context];
+    return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
 }
+
 + (JSValue *)valueWithNewObjectInContext:(JSContext *)context
 {
-    return [JSValue valueWithValue:JSObjectMake(contextInternalContext(context), 0, 0) inContext:context];
+    return [JSValue valueWithJSValueRef:JSObjectMake([context JSGlobalContextRef], 0, 0) inContext:context];
 }
+
 + (JSValue *)valueWithNewArrayInContext:(JSContext *)context
 {
-    return [JSValue valueWithValue:JSObjectMakeArray(contextInternalContext(context), 0, NULL, 0) inContext:context];
+    return [JSValue valueWithJSValueRef:JSObjectMakeArray([context JSGlobalContextRef], 0, NULL, 0) inContext:context];
 }
+
 + (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context
 {
-    JSStringRef patternString = JSStringCreateWithCFString((CFStringRef)pattern);
-    JSStringRef flagsString = JSStringCreateWithCFString((CFStringRef)flags);
-    JSValueRef arguments[2] = { JSValueMakeString(contextInternalContext(context), patternString), JSValueMakeString(contextInternalContext(context), flagsString) };
-    JSStringRelease(patternString);
-    JSStringRelease(flagsString);
-
-    return [JSValue valueWithValue:JSObjectMakeRegExp(contextInternalContext(context), 2, arguments, 0) inContext:context];
+    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
 {
-    JSStringRef string = JSStringCreateWithCFString((CFStringRef)message);
-    JSValueRef argument = JSValueMakeString(contextInternalContext(context), string);
-    JSStringRelease(string);
-
-    return [JSValue valueWithValue:JSObjectMakeError(contextInternalContext(context), 1, &argument, 0) inContext: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 valueWithValue:JSValueMakeNull(contextInternalContext(context)) inContext:context];
+    return [JSValue valueWithJSValueRef:JSValueMakeNull([context JSGlobalContextRef]) inContext:context];
 }
+
 + (JSValue *)valueWithUndefinedInContext:(JSContext *)context
 {
-    return [JSValue valueWithValue:JSValueMakeUndefined(contextInternalContext(context)) inContext: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
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-    return valueToObject(context, m_value);
+    return valueToObject(_context, m_value);
 }
-- (id)toObjectOfClass:(Class)cls
+
+- (id)toObjectOfClass:(Class)expectedClass
 {
     id result = [self toObject];
-    return [result isKindOfClass:cls] ? result : nil;
+    return [result isKindOfClass:expectedClass] ? result : nil;
 }
 
 - (BOOL)toBool
 {
-    JSContext *context = [self context];
-    return (context && JSValueToBoolean(contextInternalContext(context), m_value)) ? YES : NO;
+    return JSValueToBoolean([_context JSGlobalContextRef], m_value);
 }
+
 - (double)toDouble
 {
-    JSContext *context = [self context];
-    if (!context)
-        return std::numeric_limits<double>::quiet_NaN();
-
     JSValueRef exception = 0;
-    double result = JSValueToNumber(contextInternalContext(context), m_value, &exception);
+    double result = JSValueToNumber([_context JSGlobalContextRef], m_value, &exception);
     if (exception) {
-        [context notifyException:exception];
+        [_context notifyException:exception];
         return std::numeric_limits<double>::quiet_NaN();
     }
 
     return result;
 }
+
 - (int32_t)toInt32
 {
     return JSC::toInt32([self toDouble]);
 }
+
 - (uint32_t)toUInt32
 {
     return JSC::toUInt32([self toDouble]);
 }
+
 - (NSNumber *)toNumber
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
     JSValueRef exception = 0;
-    id result = valueToNumber(contextInternalContext(context), m_value, &exception);
+    id result = valueToNumber([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        [context notifyException:exception];
+        [_context notifyException:exception];
     return result;
 }
+
 - (NSString *)toString
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
     JSValueRef exception = 0;
-    id result = valueToString(contextInternalContext(context), m_value, &exception);
+    id result = valueToString([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        [context notifyException:exception];
+        [_context notifyException:exception];
     return result;
 }
+
 - (NSDate *)toDate
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
     JSValueRef exception = 0;
-    id result = valueToDate(contextInternalContext(context), m_value, &exception);
+    id result = valueToDate([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        [context notifyException:exception];
+        [_context notifyException:exception];
     return result;
 }
+
 - (NSArray *)toArray
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
     JSValueRef exception = 0;
-    id result = valueToArray(contextInternalContext(context), m_value, &exception);
+    id result = valueToArray([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        [context notifyException:exception];
+        [_context notifyException:exception];
     return result;
 }
+
 - (NSDictionary *)toDictionary
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
     JSValueRef exception = 0;
-    id result = valueToDictionary(contextInternalContext(context), m_value, &exception);
+    id result = valueToDictionary([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        [context notifyException:exception];
+        [_context notifyException:exception];
     return result;
 }
 
-- (JSValue *)valueForProperty:(NSString *)propertyName
+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 = [self context];
-    if (!context)
-        return nil;
-
-    JSValueRef exception = 0;
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception);
+    JSContext* context = [value context];
+    JSValueRef exception = nullptr;
+    JSObjectRef object = JSValueToObject([context JSGlobalContextRef], [value JSValueRef], &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return Unexpected<JSValueRef>(exception);
 
-    JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
-    JSValueRef result = JSObjectGetProperty(contextInternalContext(context), object, name, &exception);
-    JSStringRelease(name);
-    if (exception)
-        return [context valueFromNotifyException: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 valueWithValue:result inContext:context];
+    return [JSValue valueWithJSValueRef:result.value() inContext:_context];
 }
-- (void)setValue:(id)value forProperty:(NSString *)propertyName
+
+
+- (void)setValue:(id)value forProperty:(JSValueProperty)key
 {
-    JSContext *context = [self context];
-    if (!context)
-        return;
+    // 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 { };
+    };
 
-    JSValueRef exception = 0;
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception);
-    if (exception) {
-        [context notifyException:exception];
-        return;
-    }
+    auto jsValueSetProperty = [] (auto... args) -> Unit {
+        JSObjectSetPropertyForKey(args...);
+        return { };
+    };
 
-    JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
-    JSObjectSetProperty(contextInternalContext(context), object, name, objectToValue(context, value), 0, &exception);
-    JSStringRelease(name);
-    if (exception) {
-        [context notifyException:exception];
+    auto result = performPropertyOperation<Unit>(stringSetProperty, jsValueSetProperty, self, key, objectToValue(_context, value), kJSPropertyAttributeNone);
+    if (!result) {
+        [_context notifyException:result.error()];
         return;
     }
 }
-- (BOOL)deleteProperty:(NSString *)propertyName
-{
-    JSContext *context = [self context];
-    if (!context)
-        return NO;
-
-    JSValueRef exception = 0;
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception);
-    if (exception)
-        return [context boolFromNotifyException:exception];
 
-    JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
-    BOOL result = JSObjectDeleteProperty(contextInternalContext(context), object, name, &exception) ? YES : NO;
-    JSStringRelease(name);
-    if (exception)
-        return [context boolFromNotifyException:exception];
-
-    return result;
-}
-- (BOOL)hasProperty:(NSString *)propertyName
+- (BOOL)deleteProperty:(JSValueProperty)key
 {
-    JSContext *context = [self context];
-    if (!context)
-        return NO;
+    Expected<BOOL, JSValueRef> result = performPropertyOperation<BOOL>(JSObjectDeleteProperty, JSObjectDeletePropertyForKey, self, key);
+    if (!result)
+        return [_context boolFromNotifyException:result.error()];
+    return result.value();
+}
 
-    JSValueRef exception = 0;
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception);
-    if (exception)
-        return [context boolFromNotifyException:exception];
+- (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);
+    };
 
-    JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
-    BOOL result = JSObjectHasProperty(contextInternalContext(context), object, name) ? YES : NO;
-    JSStringRelease(name);
-    return result;
+    Expected<BOOL, JSValueRef> result = performPropertyOperation<BOOL>(stringHasProperty, JSObjectHasPropertyForKey, self, key);
+    if (!result)
+        return [_context boolFromNotifyException:result.error()];
+    return result.value();
 }
-- (void)defineProperty:(NSString *)property descriptor:(id)descriptor
+
+- (void)defineProperty:(JSValueProperty)key descriptor:(id)descriptor
 {
-    JSContext *context = [self context];
-    if (!context)
-        return;
-    [[context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, property, descriptor ]];
+    [[_context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, key, descriptor ]];
 }
 
 - (JSValue *)valueAtIndex:(NSUInteger)index
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
+    // 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)
-        [self valueForProperty:[[JSValue valueWithDouble:index inContext:context] toString]];
+        return [self valueForProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
 
     JSValueRef exception = 0;
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception);
+    JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    JSValueRef result = JSObjectGetPropertyAtIndex(contextInternalContext(context), object, (unsigned)index, &exception);
+    JSValueRef result = JSObjectGetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    return [JSValue valueWithValue:result inContext:context];
+    return [JSValue valueWithJSValueRef:result inContext:_context];
 }
+
 - (void)setValue:(id)value atIndex:(NSUInteger)index
 {
-    JSContext *context = [self context];
-    if (!context)
-        return;
-
+    // 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]];
+        return [self setValue:value forProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
 
     JSValueRef exception = 0;
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception);
+    JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
     if (exception) {
-        [context notifyException:exception];
+        [_context notifyException:exception];
         return;
     }
 
-    JSObjectSetPropertyAtIndex(contextInternalContext(context), object, (unsigned)index, objectToValue(context, value), &exception);
+    JSObjectSetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, objectToValue(_context, value), &exception);
     if (exception) {
-        [context notifyException:exception];
+        [_context notifyException:exception];
         return;
     }
 }
 
 - (BOOL)isUndefined
 {
-    JSContext *context = [self context];
-    return (context && JSValueIsUndefined(contextInternalContext(context), m_value)) ? YES : NO;
+    return JSValueIsUndefined([_context JSGlobalContextRef], m_value);
 }
+
 - (BOOL)isNull
 {
-    JSContext *context = [self context];
-    return (context && JSValueIsNull(contextInternalContext(context), m_value)) ? YES : NO;
+    return JSValueIsNull([_context JSGlobalContextRef], m_value);
 }
+
 - (BOOL)isBoolean
 {
-    JSContext *context = [self context];
-    return (context && JSValueIsBoolean(contextInternalContext(context), m_value)) ? YES : NO;
+    return JSValueIsBoolean([_context JSGlobalContextRef], m_value);
 }
+
 - (BOOL)isNumber
 {
-    JSContext *context = [self context];
-    return (context && JSValueIsNumber(contextInternalContext(context), m_value)) ? YES : NO;
+    return JSValueIsNumber([_context JSGlobalContextRef], m_value);
 }
+
 - (BOOL)isString
 {
-    JSContext *context = [self context];
-    return (context && JSValueIsString(contextInternalContext(context), m_value)) ? YES : NO;
+    return JSValueIsString([_context JSGlobalContextRef], m_value);
 }
+
 - (BOOL)isObject
 {
-    JSContext *context = [self context];
-    return (context && JSValueIsObject(contextInternalContext(context), m_value)) ? YES : NO;
+    return JSValueIsObject([_context JSGlobalContextRef], m_value);
 }
 
-- (BOOL)isEqualToObject:(id)value
+- (BOOL)isSymbol
 {
-    JSContext *context = [self context];
-    if (!context)
-        return NO;
+    return JSValueIsSymbol([_context JSGlobalContextRef], m_value);
+}
 
-    return JSValueIsStrictEqual(contextInternalContext(context), m_value, objectToValue(context, value)) ? YES : NO;
+- (BOOL)isArray
+{
+    return JSValueIsArray([_context JSGlobalContextRef], m_value);
 }
-- (BOOL)isEqualWithTypeCoercionToObject:(id)value
+
+- (BOOL)isDate
 {
-    JSContext *context = [self context];
-    if (!context)
-        return NO;
+    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(contextInternalContext(context), m_value, objectToValue(context, value), &exception) ? YES : NO;
+    BOOL result = JSValueIsEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value), &exception);
     if (exception)
-        return [context boolFromNotifyException:exception];
+        return [_context boolFromNotifyException:exception];
 
     return result;
 }
+
 - (BOOL)isInstanceOf:(id)value
 {
-    JSContext *context = [self context];
-    if (!context)
-        return NO;
-
     JSValueRef exception = 0;
-    JSObjectRef constructor = JSValueToObject(contextInternalContext(context), objectToValue(context, value), &exception);
+    JSObjectRef constructor = JSValueToObject([_context JSGlobalContextRef], objectToValue(_context, value), &exception);
     if (exception)
-        return [context boolFromNotifyException:exception];
+        return [_context boolFromNotifyException:exception];
 
-    BOOL result = JSValueIsInstanceOfConstructor(contextInternalContext(context), m_value, constructor, &exception) ? YES : NO;
+    BOOL result = JSValueIsInstanceOfConstructor([_context JSGlobalContextRef], m_value, constructor, &exception);
     if (exception)
-        return [context boolFromNotifyException:exception];
+        return [_context boolFromNotifyException:exception];
 
     return result;
 }
 
-- (JSValue *)callWithArguments:(NSArray *)args
+- (JSValue *)callWithArguments:(NSArray *)argumentArray
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
-    NSUInteger argumentCount = [args count];
+    NSUInteger argumentCount = [argumentArray count];
     JSValueRef arguments[argumentCount];
     for (unsigned i = 0; i < argumentCount; ++i)
-        arguments[i] = objectToValue(context, [args objectAtIndex:i]);
+        arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
 
     JSValueRef exception = 0;
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception);
+    JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    JSValueRef result = JSObjectCallAsFunction(contextInternalContext(context), object, 0, argumentCount, arguments, &exception);
+    JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, 0, argumentCount, arguments, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    return [JSValue valueWithValue:result inContext:context];
+    return [JSValue valueWithJSValueRef:result inContext:_context];
 }
-- (JSValue *)constructWithArguments:(NSArray *)args
-{
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
 
-    NSUInteger argumentCount = [args count];
+- (JSValue *)constructWithArguments:(NSArray *)argumentArray
+{
+    NSUInteger argumentCount = [argumentArray count];
     JSValueRef arguments[argumentCount];
     for (unsigned i = 0; i < argumentCount; ++i)
-        arguments[i] = objectToValue(context, [args objectAtIndex:i]);
+        arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
 
     JSValueRef exception = 0;
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), m_value, &exception);
+    JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    JSObjectRef result = JSObjectCallAsConstructor(contextInternalContext(context), object, argumentCount, arguments, &exception);
+    JSObjectRef result = JSObjectCallAsConstructor([_context JSGlobalContextRef], object, argumentCount, arguments, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    return [JSValue valueWithValue:result inContext:context];
+    return [JSValue valueWithJSValueRef:result inContext:_context];
 }
+
 - (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
     NSUInteger argumentCount = [arguments count];
-    JSValueRef args[argumentCount];
+    JSValueRef argumentArray[argumentCount];
     for (unsigned i = 0; i < argumentCount; ++i)
-        args[i] = objectToValue(context, [arguments objectAtIndex:i]);
+        argumentArray[i] = objectToValue(_context, [arguments objectAtIndex:i]);
 
     JSValueRef exception = 0;
-    JSObjectRef thisObject = JSValueToObject(contextInternalContext(context), m_value, &exception);
+    JSObjectRef thisObject = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    JSStringRef name = JSStringCreateWithCFString((CFStringRef)method);
-    JSValueRef func = JSObjectGetProperty(contextInternalContext(context), thisObject, name, &exception);
-    JSStringRelease(name);
+    auto name = OpaqueJSString::tryCreate(method);
+    JSValueRef function = JSObjectGetProperty([_context JSGlobalContextRef], thisObject, name.get(), &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    JSObjectRef object = JSValueToObject(contextInternalContext(context), func, &exception);
+    JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], function, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    JSValueRef result = JSObjectCallAsFunction(contextInternalContext(context), object, thisObject, argumentCount, args, &exception);
+    JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, thisObject, argumentCount, argumentArray, &exception);
     if (exception)
-        return [context valueFromNotifyException:exception];
+        return [_context valueFromNotifyException:exception];
 
-    return [JSValue valueWithValue:result inContext:context];
-}
-
-- (JSContext *)context
-{
-    return objc_loadWeak(&m_weakContext);
+    return [JSValue valueWithJSValueRef:result inContext:_context];
 }
 
 @end
@@ -498,16 +543,16 @@ NSString * const JSPropertyDescriptorSetKey = @"set";
 - (CGPoint)toPoint
 {
     return (CGPoint){
-        [self[@"x"] toDouble],
-        [self[@"y"] toDouble]
+        static_cast<CGFloat>([self[@"x"] toDouble]),
+        static_cast<CGFloat>([self[@"y"] toDouble])
     };
 }
 
 - (NSRange)toRange
 {
     return (NSRange){
-        (NSUInteger)[self[@"location"] toDouble],
-        (NSUInteger)[self[@"length"] toDouble]
+        [[self[@"location"] toNumber] unsignedIntegerValue],
+        [[self[@"length"] toNumber] unsignedIntegerValue]
     };
 }
 
@@ -522,42 +567,42 @@ NSString * const JSPropertyDescriptorSetKey = @"set";
 - (CGSize)toSize
 {
     return (CGSize){
-        [self[@"width"] toDouble],
-        [self[@"height"] toDouble]
+        static_cast<CGFloat>([self[@"width"] toDouble]),
+        static_cast<CGFloat>([self[@"height"] toDouble])
     };
 }
 
 + (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context
 {
     return [JSValue valueWithObject:@{
-        @"x":[NSNumber numberWithFloat:point.x],
-        @"y":[NSNumber numberWithFloat:point.y]
+        @"x":@(point.x),
+        @"y":@(point.y)
     } inContext:context];
 }
 
 + (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context
 {
     return [JSValue valueWithObject:@{
-        @"location":[NSNumber numberWithFloat:range.location],
-        @"length":[NSNumber numberWithFloat:range.length]
+        @"location":@(range.location),
+        @"length":@(range.length)
     } inContext:context];
 }
 
 + (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context
 {
     return [JSValue valueWithObject:@{
-        @"x":[NSNumber numberWithFloat:rect.origin.x],
-        @"y":[NSNumber numberWithFloat:rect.origin.y],
-        @"width":[NSNumber numberWithFloat:rect.size.width],
-        @"height":[NSNumber numberWithFloat:rect.size.height]
+        @"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":[NSNumber numberWithFloat:size.width],
-        @"height":[NSNumber numberWithFloat:size.height]
+        @"width":@(size.width),
+        @"height":@(size.height)
     } inContext:context];
 }
 
@@ -567,17 +612,7 @@ NSString * const JSPropertyDescriptorSetKey = @"set";
 
 - (JSValue *)objectForKeyedSubscript:(id)key
 {
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
-    if (![(NSObject*)key isKindOfClass:[NSString class]]) {
-        key = [[JSValue valueWithObject:key inContext:context] toString];
-        if (!key)
-            return [JSValue valueWithUndefinedInContext:context];
-    }
-
-    return [self valueForProperty:(NSString *)key];
+    return [self valueForProperty:key];
 }
 
 - (JSValue *)objectAtIndexedSubscript:(NSUInteger)index
@@ -585,19 +620,9 @@ NSString * const JSPropertyDescriptorSetKey = @"set";
     return [self valueAtIndex:index];
 }
 
-- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key
+- (void)setObject:(id)object forKeyedSubscript:(id)key
 {
-    JSContext *context = [self context];
-    if (!context)
-        return;
-
-    if (![(NSObject*)key isKindOfClass:[NSString class]]) {
-        key = [[JSValue valueWithObject:key inContext:context] toString];
-        if (!key)
-            return;
-    }
-
-    [self setValue:object forProperty:(NSString *)key];
+    [self setValue:object forProperty:key];
 }
 
 - (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index
@@ -607,16 +632,16 @@ NSString * const JSPropertyDescriptorSetKey = @"set";
 
 @end
 
-inline bool isDate(JSObjectRef object, JSGlobalContextRef context)
+inline bool isDate(JSC::VM& vm, JSObjectRef object, JSGlobalContextRef context)
 {
-    JSC::APIEntryShim entryShim(toJS(context));
-    return toJS(object)->inherits(&JSC::DateInstance::s_info);
+    JSC::JSLockHolder locker(toJS(context));
+    return toJS(object)->inherits<JSC::DateInstance>(vm);
 }
 
-inline bool isArray(JSObjectRef object, JSGlobalContextRef context)
+inline bool isArray(JSC::VM& vm, JSObjectRef object, JSGlobalContextRef context)
 {
-    JSC::APIEntryShim entryShim(toJS(context));
-    return toJS(object)->inherits(&JSC::JSArray::s_info);
+    JSC::JSLockHolder locker(toJS(context));
+    return toJS(object)->inherits<JSC::JSArray>(vm);
 }
 
 @implementation JSValue(Internal)
@@ -641,19 +666,20 @@ public:
     }
 
     id convert(JSValueRef property);
-    void add(Task task);
+    void add(Task);
     Task take();
-    bool isWorkListEmpty() { return !m_worklist.size(); }
+    bool isWorkListEmpty() const { return !m_worklist.size(); }
 
 private:
     JSGlobalContextRef m_context;
-    WTF::HashMap<JSValueRef, id> m_objectMap;
-    WTF::Vector<Task> m_worklist;
+    HashMap<JSValueRef, __unsafe_unretained id> m_objectMap;
+    Vector<Task> m_worklist;
+    Vector<JSC::Strong<JSC::Unknown>> m_jsValues;
 };
 
 inline id JSContainerConvertor::convert(JSValueRef value)
 {
-    WTF::HashMap<JSValueRef, id>::iterator iter = m_objectMap.find(value);
+    auto iter = m_objectMap.find(value);
     if (iter != m_objectMap.end())
         return iter->value;
 
@@ -665,6 +691,8 @@ inline id JSContainerConvertor::convert(JSValueRef value)
 
 void JSContainerConvertor::add(Task task)
 {
+    JSC::ExecState* exec = toJS(m_context);
+    m_jsValues.append(JSC::Strong<JSC::Unknown>(exec->vm(), toJSForGC(exec, task.js)));
     m_objectMap.add(task.js, task.objc);
     if (task.type != ContainerNone)
         m_worklist.append(task);
@@ -678,8 +706,21 @@ JSContainerConvertor::Task JSContainerConvertor::take()
     return last;
 }
 
+#if ENABLE(REMOTE_INSPECTOR)
+static void reportExceptionToInspector(JSGlobalContextRef context, JSC::JSValue exceptionValue)
+{
+    JSC::ExecState* exec = toJS(context);
+    JSC::VM& vm = exec->vm();
+    JSC::Exception* exception = JSC::Exception::create(vm, exceptionValue);
+    vm.vmEntryGlobalObject(exec)->inspectorController().reportAPIException(exec, exception);
+}
+#endif
+
 static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef context, JSValueRef value)
 {
+    JSC::ExecState* exec = toJS(context);
+    JSC::VM& vm = exec->vm();
+
     if (!JSValueIsObject(context, value)) {
         id primitive;
         if (JSValueIsBoolean(context, value))
@@ -691,42 +732,41 @@ static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef co
             primitive = [NSNumber numberWithDouble:JSValueToNumber(context, value, 0)];
         } else if (JSValueIsString(context, value)) {
             // Would be nice to unique strings, too.
-            JSStringRef jsstring = JSValueToStringCopy(context, value, 0);
-            NSString * stringNS = (NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring);
-            JSStringRelease(jsstring);
-            primitive = [stringNS autorelease];
+            auto jsstring = adoptRef(JSValueToStringCopy(context, value, 0));
+            primitive = CFBridgingRelease(JSStringCopyCFString(kCFAllocatorDefault, jsstring.get()));
         } else if (JSValueIsNull(context, value))
             primitive = [NSNull null];
         else {
             ASSERT(JSValueIsUndefined(context, value));
             primitive = nil;
         }
-        return (JSContainerConvertor::Task){ value, primitive, ContainerNone };
+        return { value, primitive, ContainerNone };
     }
 
     JSObjectRef object = JSValueToObject(context, value, 0);
 
     if (id wrapped = tryUnwrapObjcObject(context, object))
-        return (JSContainerConvertor::Task){ object, wrapped, ContainerNone };
+        return { object, wrapped, ContainerNone };
 
-    if (isDate(object, context))
-        return (JSContainerConvertor::Task){ object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0)], ContainerNone };
+    if (isDate(vm, object, context))
+        return { object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0) / 1000.0], ContainerNone };
 
-    if (isArray(object, context))
-        return (JSContainerConvertor::Task){ object, [NSMutableArray array], ContainerArray };
+    if (isArray(vm, object, context))
+        return { object, [NSMutableArray array], ContainerArray };
 
-    return (JSContainerConvertor::Task){ object, [NSMutableDictionary dictionary], ContainerDictionary };
+    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 = task = convertor.take();
+        JSContainerConvertor::Task current = convertor.take();
         ASSERT(JSValueIsObject(context, current.js));
         JSObjectRef js = JSValueToObject(context, current.js, 0);
 
@@ -734,9 +774,8 @@ static id containerValueToObject(JSGlobalContextRef context, JSContainerConverto
             ASSERT([current.objc isKindOfClass:[NSMutableArray class]]);
             NSMutableArray *array = (NSMutableArray *)current.objc;
         
-            JSStringRef lengthString = JSStringCreateWithUTF8CString("length");
-            unsigned length = JSC::toUInt32(JSValueToNumber(context, JSObjectGetProperty(context, js, lengthString, 0), 0));
-            JSStringRelease(lengthString);
+            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));
@@ -746,13 +785,15 @@ static id containerValueToObject(JSGlobalContextRef context, JSContainerConverto
             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[[(NSString *)JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]] = objc;
+                    dictionary[(__bridge NSString *)adoptCF(JSStringCopyCFString(kCFAllocatorDefault, propertyName)).get()] = objc;
             }
 
             JSPropertyNameArrayRelease(propertyNameArray);
@@ -765,10 +806,10 @@ static id containerValueToObject(JSGlobalContextRef context, JSContainerConverto
 
 id valueToObject(JSContext *context, JSValueRef value)
 {
-    JSContainerConvertor::Task result = valueToObjectWithoutCopy(contextInternalContext(context), value);
+    JSContainerConvertor::Task result = valueToObjectWithoutCopy([context JSGlobalContextRef], value);
     if (result.type == ContainerNone)
         return result.objc;
-    return containerValueToObject(contextInternalContext(context), result);
+    return containerValueToObject([context JSGlobalContextRef], result);
 }
 
 id valueToNumber(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
@@ -785,6 +826,7 @@ id valueToNumber(JSGlobalContextRef context, JSValueRef value, JSValueRef* excep
     double result = JSValueToNumber(context, value, exception);
     return [NSNumber numberWithDouble:*exception ? std::numeric_limits<double>::quiet_NaN() : result];
 }
+
 id valueToString(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
 {
     ASSERT(!*exception);
@@ -793,16 +835,15 @@ id valueToString(JSGlobalContextRef context, JSValueRef value, JSValueRef* excep
             return wrapped;
     }
 
-    JSStringRef jsstring = JSValueToStringCopy(context, value, exception);
+    auto jsstring = adoptRef(JSValueToStringCopy(context, value, exception));
     if (*exception) {
         ASSERT(!jsstring);
         return nil;
     }
 
-    NSString* stringNS = [(NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring) autorelease];
-    JSStringRelease(jsstring);
-    return stringNS;
+    return CFBridgingRelease(JSStringCopyCFString(kCFAllocatorDefault, jsstring.get()));
 }
+
 id valueToDate(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
 {
     ASSERT(!*exception);
@@ -811,9 +852,10 @@ id valueToDate(JSGlobalContextRef context, JSValueRef value, JSValueRef* excepti
             return wrapped;
     }
 
-    double result = JSValueToNumber(context, value, exception);
+    double result = JSValueToNumber(context, value, exception) / 1000.0;
     return *exception ? nil : [NSDate dateWithTimeIntervalSince1970:result];
 }
+
 id valueToArray(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
 {
     ASSERT(!*exception);
@@ -823,12 +865,19 @@ id valueToArray(JSGlobalContextRef context, JSValueRef value, JSValueRef* except
     }
 
     if (JSValueIsObject(context, value))
-        return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableArray array], ContainerArray});
-
-    if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value)))
-        *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSArray"));
+        return 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);
@@ -838,10 +887,16 @@ id valueToDictionary(JSGlobalContextRef context, JSValueRef value, JSValueRef* e
     }
 
     if (JSValueIsObject(context, value))
-        return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableDictionary dictionary], ContainerDictionary});
-
-    if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value)))
-        *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSDictionary"));
+        return 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;
 }
 
@@ -859,14 +914,15 @@ public:
     }
 
     JSValueRef convert(id object);
-    void add(Task task);
+    void add(Task);
     Task take();
-    bool isWorkListEmpty() { return !m_worklist.size(); }
+    bool isWorkListEmpty() const { return !m_worklist.size(); }
 
 private:
     JSContext *m_context;
-    WTF::HashMap<id, JSValueRef> m_objectMap;
-    WTF::Vector<Task> m_worklist;
+    HashMap<__unsafe_unretained id, JSValueRef> m_objectMap;
+    Vector<Task> m_worklist;
+    Vector<JSC::Strong<JSC::Unknown>> m_jsValues;
 };
 
 JSValueRef ObjcContainerConvertor::convert(id object)
@@ -884,6 +940,8 @@ JSValueRef ObjcContainerConvertor::convert(id object)
 
 void ObjcContainerConvertor::add(ObjcContainerConvertor::Task task)
 {
+    JSC::ExecState* exec = toJS(m_context.JSGlobalContextRef);
+    m_jsValues.append(JSC::Strong<JSC::Unknown>(exec->vm(), toJSForGC(exec, task.js)));
     m_objectMap.add(task.objc, task.js);
     if (task.type != ContainerNone)
         m_worklist.append(task);
@@ -897,92 +955,97 @@ ObjcContainerConvertor::Task ObjcContainerConvertor::take()
     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 (ObjcContainerConvertor::Task){ object, JSValueMakeUndefined(contextInternalContext(context)), ContainerNone };
+        return { object, JSValueMakeUndefined(contextRef), ContainerNone };
 
     if (!class_conformsToProtocol(object_getClass(object), getJSExportProtocol())) {
         if ([object isKindOfClass:[NSArray class]])
-            return (ObjcContainerConvertor::Task){ object, JSObjectMakeArray(contextInternalContext(context), 0, NULL, 0), ContainerArray };
+            return { object, JSObjectMakeArray(contextRef, 0, NULL, 0), ContainerArray };
 
         if ([object isKindOfClass:[NSDictionary class]])
-            return (ObjcContainerConvertor::Task){ object, JSObjectMake(contextInternalContext(context), 0, 0), ContainerDictionary };
+            return { object, JSObjectMake(contextRef, 0, 0), ContainerDictionary };
 
         if ([object isKindOfClass:[NSNull class]])
-            return (ObjcContainerConvertor::Task){ object, JSValueMakeNull(contextInternalContext(context)), ContainerNone };
+            return { object, JSValueMakeNull(contextRef), ContainerNone };
 
         if ([object isKindOfClass:[JSValue class]])
-            return (ObjcContainerConvertor::Task){ object, ((JSValue *)object)->m_value, ContainerNone };
-
-        if ([object isKindOfClass:[NSArray class]])
-            return (ObjcContainerConvertor::Task){ object, JSObjectMakeArray(contextInternalContext(context), 0, NULL, 0), ContainerArray };
-
-        if ([object isKindOfClass:[NSDictionary class]])
-            return (ObjcContainerConvertor::Task){ object, JSObjectMake(contextInternalContext(context), 0, 0), ContainerDictionary };
+            return { object, ((JSValue *)object)->m_value, ContainerNone };
 
         if ([object isKindOfClass:[NSString class]]) {
-            JSStringRef string = JSStringCreateWithCFString((CFStringRef)object);
-            JSValueRef js = JSValueMakeString(contextInternalContext(context), string);
-            JSStringRelease(string);
-            return (ObjcContainerConvertor::Task){ object, js, ContainerNone };
+            auto string = OpaqueJSString::tryCreate((NSString *)object);
+            return { object, JSValueMakeString(contextRef, string.get()), ContainerNone };
         }
 
         if ([object isKindOfClass:[NSNumber class]]) {
-            id literalTrue = @YES;
-            if (object == literalTrue)
-                return (ObjcContainerConvertor::Task){ object, JSValueMakeBoolean(contextInternalContext(context), true), ContainerNone };
-            id literalFalse = @NO;
-            if (object == literalFalse)
-                return (ObjcContainerConvertor::Task){ object, JSValueMakeBoolean(contextInternalContext(context), false), ContainerNone };
-            return (ObjcContainerConvertor::Task){ object, JSValueMakeNumber(contextInternalContext(context), [(NSNumber*)object doubleValue]), ContainerNone };
+            if (isNSBoolean(object))
+                return { object, JSValueMakeBoolean(contextRef, [object boolValue]), ContainerNone };
+            return { object, JSValueMakeNumber(contextRef, [object doubleValue]), ContainerNone };
         }
 
         if ([object isKindOfClass:[NSDate class]]) {
-            JSValueRef argument = JSValueMakeNumber(contextInternalContext(context), [(NSDate *)object timeIntervalSince1970]);
-            JSObjectRef result = JSObjectMakeDate(contextInternalContext(context), 1, &argument, 0);
-            return (ObjcContainerConvertor::Task){ object, result, ContainerNone };
+            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 (ObjcContainerConvertor::Task){ object, valueInternalValue([context wrapperForObject:object]), 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(contextInternalContext(context), current.js));
-        JSObjectRef js = JSValueToObject(contextInternalContext(context), current.js, 0);
+        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;
+            NSArray *array = (NSArray *)current.objc;
             NSUInteger count = [array count];
             for (NSUInteger index = 0; index < count; ++index)
-                JSObjectSetPropertyAtIndex(contextInternalContext(context), js, index, convertor.convert([array objectAtIndex:index]), 0);
+                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;
-            NSEnumerator* enumerator = [dictionary keyEnumerator];
-            while (id key = [enumerator nextObject]) {
+            NSDictionary *dictionary = (NSDictionary *)current.objc;
+            for (id key in [dictionary keyEnumerator]) {
                 if ([key isKindOfClass:[NSString class]]) {
-                    JSStringRef propertyName = JSStringCreateWithCFString((CFStringRef)key);
-                    JSObjectSetProperty(contextInternalContext(context), js, propertyName, convertor.convert([dictionary objectForKey:key]), 0, 0);
-                    JSStringRelease(propertyName);
+                    auto propertyName = OpaqueJSString::tryCreate((NSString *)key);
+                    JSObjectSetProperty(contextRef, js, propertyName.get(), convertor.convert([dictionary objectForKey:key]), 0, 0);
                 }
             }
         }
-        
     } while (!convertor.isWorkListEmpty());
 
     return task.js;
@@ -993,20 +1056,28 @@ JSValueRef valueInternalValue(JSValue * value)
     return value->m_value;
 }
 
-+ (JSValue *)valueWithValue:(JSValueRef)value inContext:(JSContext*)context
++ (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context
 {
-    return [[[JSValue alloc] initWithValue:value inContext:context] autorelease];
+    return [context wrapperForJSObject:value];
 }
 
-- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext*)context
+- (JSValue *)init
 {
-    ASSERT(value);
+    return nil;
+}
 
-    objc_initWeak(&m_weakContext, context);
+- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context
+{
+    if (!value || !context)
+        return nil;
+
+    self = [super init];
+    if (!self)
+        return nil;
 
-    [super init];
-    [context protect:value];
+    _context = [context retain];
     m_value = value;
+    JSValueProtect([_context JSGlobalContextRef], m_value);
     return self;
 }
 
@@ -1014,150 +1085,132 @@ struct StructTagHandler {
     SEL typeToValueSEL;
     SEL valueToTypeSEL;
 };
+typedef HashMap<String, StructTagHandler> StructHandlers;
 
-static StructTagHandler* getStructTagHandler(const char* encodedType)
+static StructHandlers* createStructHandlerMap()
 {
-    static SpinLock getStructTagHandlerLock = SPINLOCK_INITIALIZER;
-    SpinLockHolder lockHolder(&getStructTagHandlerLock);
-
-    typedef WTF::HashMap<RefPtr<StringImpl>, StructTagHandler> StructHandlers;
-    static StructHandlers *m_structHandlers = 0;
-
-    if (!m_structHandlers) {
-        m_structHandlers = new StructHandlers();
-
-        // Step 1: find all valueWith<Foo>:inContext: class methods in JSValue.
-        size_t valueWithMinLen = strlen("valueWithX:inContext:");
-        forEachMethodInClass(object_getClass([JSValue class]), ^(Method method){
-            SEL sel = method_getName(method);
-            const char* name = sel_getName(sel);
-            size_t len = strlen(name);
-            // Check for valueWith<Foo>:context:
-            if (len < valueWithMinLen || strncmp(name, "valueWith", 9) || strncmp(name + len - 11, ":inContext:", 11))
-                return;
-            // Check for [ id, SEL, <type>, <contextType> ]
-            if (method_getNumberOfArguments(method) != 4)
-                return;
-            char idType[3];
-            // Check 2nd argument type is "@"
-            char* secondType = method_copyArgumentType(method, 3);
-            if (strcmp(secondType, "@")) {
-                free(secondType);
-                return;
-            }
-            free(secondType);
-            // Check result type is also "@"
-            method_getReturnType(method, idType, 3);
-            if (strcmp(idType, "@"))
-                return;
-            char* type = method_copyArgumentType(method, 2);
-            m_structHandlers->add(WTF::String(type).impl(), (StructTagHandler){ sel, 0 });
-            free(type);
-        });
-
-        // Step 2: find all to<Foo> instance methods in JSValue.
-        size_t minLenValue = strlen("toX");
-        forEachMethodInClass([JSValue class], ^(Method method){
-            SEL sel = method_getName(method);
-            const char* name = sel_getName(sel);
-            size_t len = strlen(name);
-            // Check for to<Foo>
-            if (len < minLenValue || strncmp(name, "to", 2))
-                return;
-            // Check for [ id, SEL ]
-            if (method_getNumberOfArguments(method) != 2)
-                return;
-            // Try to find a matching valueWith<Foo>:context: method.
-            char* type = method_copyReturnType(method);
+    StructHandlers* structHandlers = new StructHandlers();
 
-            StructHandlers::iterator iter = m_structHandlers->find(WTF::String(type).impl());
-            free(type);
-            if (iter == m_structHandlers->end())
-                return;
-            StructTagHandler& handler = iter->value;
+    size_t valueWithXinContextLength = strlen("valueWithX:inContext:");
+    size_t toXLength = strlen("toX");
 
-            // check that strlen(<foo>) == strlen(<Foo>)
-            const char* valueWithName = sel_getName(handler.typeToValueSEL);
-            size_t valueWithLen = strlen(valueWithName);
-            if (valueWithLen - valueWithMinLen != len - minLenValue)
-                return;
-            // Check that <Foo> == <Foo>
-            if (strncmp(valueWithName + 9, name + 2, len - minLenValue - 1))
+    // 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;
-            handler.valueToTypeSEL = sel;
-        });
-
-        // Step 3: clean up - remove entries where we found prospective valueWith<Foo>:inContext: conversions, but no matching to<Foo> methods.
-        typedef WTF::HashSet<RefPtr<WTF::StringImpl> > RemoveSet;
-        RemoveSet removeSet;
-        for (StructHandlers::iterator iter = m_structHandlers->begin(); iter != m_structHandlers->end(); ++iter) {
-            StructTagHandler& handler = iter->value;
-            if (!handler.valueToTypeSEL)
-                removeSet.add(iter->key);
         }
+        // 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;
 
-        for (RemoveSet::iterator iter = removeSet.begin(); iter != removeSet.end(); ++iter)
-            m_structHandlers->remove(*iter);
+        // 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);
     }
 
-    StructHandlers::iterator iter = m_structHandlers->find(WTF::String(encodedType).impl());
-    if (iter == m_structHandlers->end())
+    for (RemoveSet::iterator iter = removeSet.begin(); iter != removeSet.end(); ++iter)
+        structHandlers->remove(*iter);
+
+    return structHandlers;
+}
+
+static StructTagHandler* handerForStructTag(const char* encodedType)
+{
+    static Lock handerForStructTagLock;
+    LockHolder 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 = getStructTagHandler(structTag);
+    StructTagHandler* handler = handerForStructTag(structTag);
     return handler ? handler->typeToValueSEL : nil;
 }
 
 + (SEL)selectorForValueToStruct:(const char *)structTag
 {
-    StructTagHandler* handler = getStructTagHandler(structTag);
+    StructTagHandler* handler = handerForStructTag(structTag);
     return handler ? handler->valueToTypeSEL : nil;
 }
 
-- (void)dealloc
-{
-    JSContext *context = [self context];
-    if (context)
-        [context unprotect:m_value];
-    objc_destroyWeak(&m_weakContext);
-    [super dealloc];
-}
-
-- (NSString *)description
-{
-    JSContext *context = [self context];
-    if (!context)
-        return nil;
-
-    if (id wrapped = tryUnwrapObjcObject(contextInternalContext(context), m_value))
-        return [wrapped description];
-    return [self toString];
-}
-
-NSInvocation* typeToValueInvocationFor(const char* encodedType)
+NSInvocation *typeToValueInvocationFor(const char* encodedType)
 {
     SEL selector = [JSValue selectorForStructToValue:encodedType];
     if (!selector)
         return 0;
 
     const char* methodTypes = method_getTypeEncoding(class_getClassMethod([JSValue class], selector));
-    NSInvocationinvocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
+    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
     [invocation setSelector:selector];
     return invocation;
 }
 
-NSInvocationvalueToTypeInvocationFor(const char* encodedType)
+NSInvocation *valueToTypeInvocationFor(const char* encodedType)
 {
     SEL selector = [JSValue selectorForValueToStruct:encodedType];
     if (!selector)
         return 0;
 
     const char* methodTypes = method_getTypeEncoding(class_getInstanceMethod([JSValue class], selector));
-    NSInvocationinvocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
+    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
     [invocation setSelector:selector];
     return invocation;
 }