]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - API/tests/testapi.mm
JavaScriptCore-7600.1.4.15.12.tar.gz
[apple/javascriptcore.git] / API / tests / testapi.mm
index d85d6e391e3fc4887ae36043f3cb6eb6c23c15fa..724867c6400e5876fd479234dd80c7d7e0e701c0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 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.
  */
 
 #import <JavaScriptCore/JavaScriptCore.h>
 
+#import "CurrentThisInsideBlockGetterTest.h"
+#import "DateTests.h"
+#import "JSExportTests.h"
+#import "Regress141809.h"
+
+#import <pthread.h>
+
 extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+extern "C" void JSSynchronousEdenCollectForDebugging(JSContextRef);
 
 extern "C" bool _Block_has_signature(id);
 extern "C" const char * _Block_signature(id);
 
 extern int failed;
 extern "C" void testObjectiveCAPI(void);
+extern "C" void checkResult(NSString *, bool);
 
 #if JSC_OBJC_API_ENABLED
 
@@ -170,10 +179,6 @@ bool testXYZTested = false;
     JSValue *function = [m_onclickHandler value];
     [function callWithArguments:[NSArray array]];
 }
-- (void)dealloc
-{
-    [[m_onclickHandler value].context.virtualMachine removeManagedReference:m_onclickHandler withOwner:self];
-}
 @end
 
 @class TinyDOMNode;
@@ -186,54 +191,31 @@ bool testXYZTested = false;
 @end
 
 @interface TinyDOMNode : NSObject<TinyDOMNode>
-+ (JSVirtualMachine *)sharedVirtualMachine;
-+ (void)clearSharedVirtualMachine;
 @end
 
 @implementation TinyDOMNode {
     NSMutableArray *m_children;
+    JSVirtualMachine *m_sharedVirtualMachine;
 }
 
-static JSVirtualMachine *sharedInstance = nil;
-
-+ (JSVirtualMachine *)sharedVirtualMachine
-{
-    if (!sharedInstance)
-        sharedInstance = [[JSVirtualMachine alloc] init];
-    return sharedInstance;
-}
-
-+ (void)clearSharedVirtualMachine
-{
-    sharedInstance = nil;
-}
-
-- (id)init
+- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
 {
     self = [super init];
     if (!self)
         return nil;
 
     m_children = [[NSMutableArray alloc] initWithCapacity:0];
-
-    return self;
-}
-
-- (void)dealloc
-{
-    NSEnumerator *enumerator = [m_children objectEnumerator];
-    id nextChild;
-    while ((nextChild = [enumerator nextObject]))
-        [[TinyDOMNode sharedVirtualMachine] removeManagedReference:nextChild withOwner:self];
-
+    m_sharedVirtualMachine = virtualMachine;
 #if !__has_feature(objc_arc)
-    [super dealloc];
+    [m_sharedVirtualMachine retain];
 #endif
+
+    return self;
 }
 
 - (void)appendChild:(TinyDOMNode *)child
 {
-    [[TinyDOMNode sharedVirtualMachine] addManagedReference:child withOwner:self];
+    [m_sharedVirtualMachine addManagedReference:child withOwner:self];
     [m_children addObject:child];
 }
 
@@ -253,7 +235,7 @@ static JSVirtualMachine *sharedInstance = nil;
 {
     if (index >= [m_children count])
         return;
-    [[TinyDOMNode sharedVirtualMachine] removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
+    [m_sharedVirtualMachine removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
     [m_children removeObjectAtIndex:index];
 }
 
@@ -302,12 +284,17 @@ static JSVirtualMachine *sharedInstance = nil;
 
 @protocol InitA <JSExport>
 - (id)initWithA:(int)a;
+- (int)initialize;
 @end
 
 @protocol InitB <JSExport>
 - (id)initWithA:(int)a b:(int)b;
 @end
 
+@protocol InitC <JSExport>
+- (id)_init;
+@end
+
 @interface ClassA : NSObject<InitA>
 @end
 
@@ -317,6 +304,9 @@ static JSVirtualMachine *sharedInstance = nil;
 @interface ClassC : ClassB<InitA, InitB>
 @end
 
+@interface ClassCPrime : ClassB<InitA, InitC>
+@end
+
 @interface ClassD : NSObject<InitA>
 - (id)initWithA:(int)a;
 @end
@@ -338,6 +328,10 @@ static JSVirtualMachine *sharedInstance = nil;
 
     return self;
 }
+- (int)initialize
+{
+    return 42;
+}
 @end
 
 @implementation ClassB {
@@ -374,6 +368,20 @@ static JSVirtualMachine *sharedInstance = nil;
 }
 @end
 
+@implementation ClassCPrime
+- (id)initWithA:(int)a
+{
+    self = [super initWithA:a b:0];
+    if (!self)
+        return nil;
+    return self;
+}
+- (id)_init
+{
+    return [self initWithA:42];
+}
+@end
+
 @implementation ClassD
 
 - (id)initWithA:(int)a
@@ -381,6 +389,10 @@ static JSVirtualMachine *sharedInstance = nil;
     self = nil;
     return [[ClassE alloc] initWithA:a];
 }
+- (int)initialize
+{
+    return 0;
+}
 @end
 
 @implementation ClassE {
@@ -398,7 +410,54 @@ static JSVirtualMachine *sharedInstance = nil;
     return self;
 }
 @end
-static void checkResult(NSString *description, bool passed)
+
+static bool evilAllocationObjectWasDealloced = false;
+
+@interface EvilAllocationObject : NSObject
+- (JSValue *)doEvilThingsWithContext:(JSContext *)context;
+@end
+
+@implementation EvilAllocationObject {
+    JSContext *m_context;
+}
+- (id)initWithContext:(JSContext *)context
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    m_context = context;
+
+    return self;
+}
+- (void)dealloc
+{
+    [self doEvilThingsWithContext:m_context];
+    evilAllocationObjectWasDealloced = true;
+#if !__has_feature(objc_arc)
+    [super dealloc];
+#endif
+}
+
+- (JSValue *)doEvilThingsWithContext:(JSContext *)context
+{
+    JSValue *result = [context evaluateScript:@" \
+        (function() { \
+            var a = []; \
+            var sum = 0; \
+            for (var i = 0; i < 10000; ++i) { \
+                sum += i; \
+                a[i] = sum; \
+            } \
+            return sum; \
+        })()"];
+
+    JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+    return result;
+}
+@end
+
+extern "C" void checkResult(NSString *description, bool passed)
 {
     NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
     if (!passed)
@@ -414,10 +473,26 @@ static bool blockSignatureContainsClass()
     return containsClass;
 }
 
+static void* threadMain(void* contextPtr)
+{
+    JSContext *context = (__bridge JSContext*)contextPtr;
+
+    // Do something to enter the VM.
+    TestObject *testObject = [TestObject testObject];
+    context[@"testObject"] = testObject;
+    pthread_exit(nullptr);
+}
+
 void testObjectiveCAPI()
 {
     NSLog(@"Testing Objective-C API");
 
+    @autoreleasepool {
+        JSVirtualMachine* vm = [[JSVirtualMachine alloc] init];
+        JSContext* context = [[JSContext alloc] initWithVirtualMachine:vm];
+        [context evaluateScript:@"bad"];
+    }
+
     @autoreleasepool {
         JSContext *context = [[JSContext alloc] init];
         JSValue *result = [context evaluateScript:@"2 + 2"];
@@ -482,6 +557,20 @@ void testObjectiveCAPI()
         checkResult(@"not_sure_if_undefined is undefined", ![myPrivateProperties valueForKey:@"not_sure_if_undefined"]);
     }
 
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        JSValue *message = [JSValue valueWithObject:@"hello" inContext:context];
+        TestObject *rootObject = [TestObject testObject];
+        JSCollection *collection = [[JSCollection alloc] init];
+        context[@"root"] = rootObject;
+        @autoreleasepool {
+            JSValue *jsCollection = [JSValue valueWithObject:collection inContext:context];
+            JSManagedValue *weakCollection = [JSManagedValue managedValueWithValue:jsCollection andOwner:rootObject];
+            [context.virtualMachine addManagedReference:weakCollection withOwner:message];
+            JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+        }
+    }
+
     @autoreleasepool {
         JSContext *context = [[JSContext alloc] init];
         __block int result;
@@ -524,6 +613,40 @@ void testObjectiveCAPI()
         checkResult(@"JSContext.exceptionHandler", caught);
     }
 
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        __block int expectedExceptionLineNumber = 1;
+        __block bool sawExpectedExceptionLineNumber = false;
+        context.exceptionHandler = ^(JSContext *, JSValue *exception) {
+            sawExpectedExceptionLineNumber = [exception[@"line"] toInt32] == expectedExceptionLineNumber;
+        };
+        [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
+        checkResult(@"evaluteScript exception on line 1", sawExpectedExceptionLineNumber);
+
+        expectedExceptionLineNumber = 2;
+        sawExpectedExceptionLineNumber = false;
+        [context evaluateScript:@"// Line 1\n!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
+        checkResult(@"evaluteScript exception on line 2", sawExpectedExceptionLineNumber);
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        __block bool emptyExceptionSourceURL = false;
+        context.exceptionHandler = ^(JSContext *, JSValue *exception) {
+            emptyExceptionSourceURL = [exception[@"sourceURL"] isUndefined];
+        };
+        [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
+        checkResult(@"evaluteScript: exception has no sourceURL", emptyExceptionSourceURL);
+
+        __block NSString *exceptionSourceURL = nil;
+        context.exceptionHandler = ^(JSContext *, JSValue *exception) {
+            exceptionSourceURL = [exception[@"sourceURL"] toString];
+        };
+        NSURL *url = [NSURL fileURLWithPath:@"/foo/bar.js"];
+        [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()" withSourceURL:url];
+        checkResult(@"evaluateScript:withSourceURL: exception has expected sourceURL", [exceptionSourceURL isEqualToString:[url absoluteString]]);
+    }
+
     @autoreleasepool {
         JSContext *context = [[JSContext alloc] init];
         context[@"callback"] = ^{
@@ -621,30 +744,6 @@ void testObjectiveCAPI()
         checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
     }
 
-    @autoreleasepool {
-        JSContext *context = [[JSContext alloc] init];
-        context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
-            NSDictionary *expectedDict = @{
-                @"foo" : [NSNumber numberWithInt:1],
-                @"bar" : @{
-                    @"baz": [NSNumber numberWithInt:2]
-                }
-            };
-            checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
-        };
-        [context evaluateScript:@"var myDict = { \
-            'foo': 1, \
-            'bar': {'baz': 2} \
-        }; \
-        handleTheDictionary(myDict);"];
-
-        context[@"handleTheArray"] = ^(NSArray *array) {
-            NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
-            checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
-        };
-        [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
-    }
-
     @autoreleasepool {
         JSContext *context = [[JSContext alloc] init];
         TextXYZ *testXYZ = [[TextXYZ alloc] init];
@@ -694,6 +793,14 @@ void testObjectiveCAPI()
         checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]);
     }
 
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        JSValue *result = [context evaluateScript:@"String(console)"];
+        checkResult(@"String(console)", [result isEqualToObject:@"[object Console]"]);
+        result = [context evaluateScript:@"typeof console.log"];
+        checkResult(@"typeof console.log", [result isEqualToObject:@"function"]);
+    }
+
     @autoreleasepool {
         JSContext *context = [[JSContext alloc] init];
         TestObject* testObject = [TestObject testObject];
@@ -956,12 +1063,11 @@ void testObjectiveCAPI()
     }
 
     @autoreleasepool {
-        JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
-        JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
-        TinyDOMNode *root = [[TinyDOMNode alloc] init];
+        JSContext *context = [[JSContext alloc] init];
+        TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
         TinyDOMNode *lastNode = root;
         for (NSUInteger i = 0; i < 3; i++) {
-            TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
+            TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
             [lastNode appendChild:newNode];
             lastNode = newNode;
         }
@@ -983,17 +1089,14 @@ void testObjectiveCAPI()
 
         JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
         checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
-
-        [TinyDOMNode clearSharedVirtualMachine];
     }
 
     @autoreleasepool {
-        JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
-        JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
-        TinyDOMNode *root = [[TinyDOMNode alloc] init];
+        JSContext *context = [[JSContext alloc] init];
+        TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
         TinyDOMNode *lastNode = root;
         for (NSUInteger i = 0; i < 3; i++) {
-            TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
+            TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
             [lastNode appendChild:newNode];
             lastNode = newNode;
         }
@@ -1018,8 +1121,6 @@ void testObjectiveCAPI()
 
         JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
         checkResult(@"duplicate calls to addManagedReference don't cause things to die", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
-
-        [TinyDOMNode clearSharedVirtualMachine];
     }
 
     @autoreleasepool {
@@ -1052,12 +1153,15 @@ void testObjectiveCAPI()
 
     @autoreleasepool {
         JSContext *context = [[JSContext alloc] init];
-        context[@"UnexportedObject"] = [UnexportedObject class];
-        context[@"makeObject"] = ^{
-            return [[UnexportedObject alloc] init];
-        };
-        JSValue *result = [context evaluateScript:@"(makeObject() instanceof UnexportedObject)"];
-        checkResult(@"makeObject() instanceof UnexportedObject", [result isBoolean] && [result toBool]);
+        TestObject *testObject = [TestObject testObject];
+        context[@"testObject"] = testObject;
+        JSManagedValue *managedValue = nil;
+        @autoreleasepool {
+            JSValue *object = [JSValue valueWithNewObjectInContext:context];
+            managedValue = [JSManagedValue managedValueWithValue:object andOwner:testObject];
+            [context.virtualMachine addManagedReference:managedValue withOwner:testObject];
+        }
+        JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
     }
 
     @autoreleasepool {
@@ -1105,6 +1209,38 @@ void testObjectiveCAPI()
             NSLog(@"I'm intentionally not returning anything.");
         };
         JSValue *result = [context evaluateScript:@"new MyClass()"];
+        checkResult(@"result === undefined", [result isUndefined]);
+        checkResult(@"exception.message is correct'", context.exception 
+            && [@"Objective-C blocks called as constructors must return an object." isEqualToString:[context.exception[@"message"] toString]]);
+    }
+
+    @autoreleasepool {
+        checkResult(@"[JSContext currentThis] == nil outside of callback", ![JSContext currentThis]);
+        checkResult(@"[JSContext currentArguments] == nil outside of callback", ![JSContext currentArguments]);
+        if ([JSContext currentCallee])
+            checkResult(@"[JSContext currentCallee] == nil outside of callback", ![JSContext currentCallee]);
+    }
+
+    if ([JSContext currentCallee]) {
+        @autoreleasepool {
+            JSContext *context = [[JSContext alloc] init];
+            context[@"testFunction"] = ^{
+                checkResult(@"testFunction.foo === 42", [[JSContext currentCallee][@"foo"] toInt32] == 42);
+            };
+            context[@"testFunction"][@"foo"] = @42;
+            [context[@"testFunction"] callWithArguments:nil];
+
+            context[@"TestConstructor"] = ^{
+                JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
+                JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
+                JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
+                JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentCallee][@"prototype"] JSValueRef]);
+                return newThis;
+            };
+            checkResult(@"(new TestConstructor) instanceof TestConstructor", [context evaluateScript:@"(new TestConstructor) instanceof TestConstructor"]);
+        }
+    }
+
     @autoreleasepool {
         JSContext *context = [[JSContext alloc] init];
         context[@"TestObject"] = [TestObject class];
@@ -1121,9 +1257,11 @@ void testObjectiveCAPI()
         context[@"ClassA"] = [ClassA class];
         context[@"ClassB"] = [ClassB class];
         context[@"ClassC"] = [ClassC class]; // Should print error message about too many inits found.
+        context[@"ClassCPrime"] = [ClassCPrime class]; // Ditto.
 
         JSValue *a = [context evaluateScript:@"(new ClassA(42))"];
         checkResult(@"a instanceof ClassA", [a isInstanceOf:context[@"ClassA"]]);
+        checkResult(@"a.initialize() is callable", [[a invokeMethod:@"initialize" withArguments:@[]] toInt32] == 42);
 
         JSValue *b = [context evaluateScript:@"(new ClassB(42, 53))"];
         checkResult(@"b instanceof ClassB", [b isInstanceOf:context[@"ClassB"]]);
@@ -1137,6 +1275,15 @@ void testObjectiveCAPI()
             } \
         })()"];
         checkResult(@"shouldn't be able to construct ClassC", ![canConstructClassC toBool]);
+        JSValue *canConstructClassCPrime = [context evaluateScript:@"(function() { \
+            try { \
+                (new ClassCPrime(1)); \
+                return true; \
+            } catch(e) { \
+                return false; \
+            } \
+        })()"];
+        checkResult(@"shouldn't be able to construct ClassCPrime", ![canConstructClassCPrime toBool]);
     }
 
     @autoreleasepool {
@@ -1148,10 +1295,98 @@ void testObjectiveCAPI()
         checkResult(@"Returning instance of ClassE from ClassD's init has correct class", [d isInstanceOf:context[@"ClassE"]]);
     }
 
-        checkResult(@"result === undefined", [result isUndefined]);
-        checkResult(@"exception.message is correct'", context.exception 
-            && [@"Objective-C blocks called as constructors must return an object." isEqualToString:[context.exception[@"message"] toString]]);
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        while (!evilAllocationObjectWasDealloced) {
+            @autoreleasepool {
+                EvilAllocationObject *evilObject = [[EvilAllocationObject alloc] initWithContext:context];
+                context[@"evilObject"] = evilObject;
+                context[@"evilObject"] = nil;
+            }
+        }
+        checkResult(@"EvilAllocationObject was successfully dealloced without crashing", evilAllocationObjectWasDealloced);
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        checkResult(@"default context.name is nil", context.name == nil);
+        NSString *name1 = @"Name1";
+        NSString *name2 = @"Name2";
+        context.name = name1;
+        NSString *fetchedName1 = context.name;
+        context.name = name2;
+        NSString *fetchedName2 = context.name;
+        context.name = nil;
+        NSString *fetchedName3 = context.name;
+        checkResult(@"fetched context.name was expected", [fetchedName1 isEqualToString:name1]);
+        checkResult(@"fetched context.name was expected", [fetchedName2 isEqualToString:name2]);
+        checkResult(@"fetched context.name was expected", ![fetchedName1 isEqualToString:fetchedName2]);
+        checkResult(@"fetched context.name was expected", fetchedName3 == nil);
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        context[@"UnexportedObject"] = [UnexportedObject class];
+        context[@"makeObject"] = ^{
+            return [[UnexportedObject alloc] init];
+        };
+        JSValue *result = [context evaluateScript:@"(makeObject() instanceof UnexportedObject)"];
+        checkResult(@"makeObject() instanceof UnexportedObject", [result isBoolean] && [result toBool]);
     }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        [[JSValue valueWithInt32:42 inContext:context] toDictionary];
+        [[JSValue valueWithInt32:42 inContext:context] toArray];
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+
+        // Create the root, make it reachable from JS, and force an EdenCollection
+        // so that we scan the external object graph.
+        TestObject *root = [TestObject testObject];
+        @autoreleasepool {
+            context[@"root"] = root;
+        }
+        JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]);
+
+        // Create a new Obj-C object only reachable via the external object graph
+        // through the object we already scanned during the EdenCollection.
+        TestObject *child = [TestObject testObject];
+        [context.virtualMachine addManagedReference:child withOwner:root];
+
+        // Create a new managed JSValue that will only be kept alive if we properly rescan
+        // the external object graph.
+        JSManagedValue *managedJSObject = nil;
+        @autoreleasepool {
+            JSValue *jsObject = [JSValue valueWithObject:@"hello" inContext:context];
+            managedJSObject = [JSManagedValue managedValueWithValue:jsObject];
+            [context.virtualMachine addManagedReference:managedJSObject withOwner:child];
+        }
+
+        // Force another EdenCollection. It should rescan the new part of the external object graph.
+        JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]);
+        
+        // Check that the managed JSValue is still alive.
+        checkResult(@"EdenCollection doesn't reclaim new managed values", [managedJSObject value] != nil);
+    }
+
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        
+        pthread_t threadID;
+        pthread_create(&threadID, NULL, &threadMain, (__bridge void*)context);
+        pthread_join(threadID, nullptr);
+        JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+
+        checkResult(@"Did not crash after entering the VM from another thread", true);
+    }
+    
+    currentThisInsideBlockGetterTest();
+    runDateTests();
+    runJSExportTests();
+    runRegress141809();
 }
 
 #else
@@ -1160,4 +1395,4 @@ void testObjectiveCAPI()
 {
 }
 
-#endif
+#endif // JSC_OBJC_API_ENABLED