2 * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #import <JavaScriptCore/JavaScriptCore.h>
28 #import "CurrentThisInsideBlockGetterTest.h"
30 #import "JSExportTests.h"
31 #import "Regress141275.h"
32 #import "Regress141809.h"
36 extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
37 extern "C" void JSSynchronousEdenCollectForDebugging(JSContextRef);
39 extern "C" bool _Block_has_signature(id);
40 extern "C" const char * _Block_signature(id);
43 extern "C" void testObjectiveCAPI(void);
44 extern "C" void checkResult(NSString *, bool);
46 #if JSC_OBJC_API_ENABLED
48 @interface UnexportedObject : NSObject
51 @implementation UnexportedObject
54 @protocol ParentObject <JSExport>
57 @interface ParentObject : NSObject<ParentObject>
58 + (NSString *)parentTest;
61 @implementation ParentObject
62 + (NSString *)parentTest
64 return [self description];
68 @protocol TestObject <JSExport>
70 @property int variable;
71 @property (readonly) int six;
72 @property CGPoint point;
73 + (NSString *)classTest;
74 + (NSString *)parentTest;
75 - (NSString *)getString;
76 JSExportAs(testArgumentTypes,
77 - (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
79 - (void)callback:(JSValue *)function;
80 - (void)bogusCallback:(void(^)(int))function;
83 @interface TestObject : ParentObject <TestObject>
88 @implementation TestObject
94 return [[TestObject alloc] init];
96 + (NSString *)classTest
98 return @"classTest - okay";
100 - (NSString *)getString
104 - (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
106 return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]];
108 - (void)callback:(JSValue *)function
110 [function callWithArguments:[NSArray arrayWithObject:[NSNumber numberWithInt:42]]];
112 - (void)bogusCallback:(void(^)(int))function
118 bool testXYZTested = false;
120 @protocol TextXYZ <JSExport>
121 - (id)initWithString:(NSString*)string;
123 @property (readonly) int y;
124 @property (assign) JSValue *onclick;
125 @property (assign) JSValue *weakOnclick;
126 - (void)test:(NSString *)message;
129 @interface TextXYZ : NSObject <TextXYZ>
136 @implementation TextXYZ {
137 JSManagedValue *m_weakOnclickHandler;
138 JSManagedValue *m_onclickHandler;
143 - (id)initWithString:(NSString*)string
149 NSLog(@"%@", string);
153 - (void)test:(NSString *)message
155 testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5;
157 - (void)setWeakOnclick:(JSValue *)value
159 m_weakOnclickHandler = [JSManagedValue managedValueWithValue:value];
162 - (void)setOnclick:(JSValue *)value
164 m_onclickHandler = [JSManagedValue managedValueWithValue:value];
165 [value.context.virtualMachine addManagedReference:m_onclickHandler withOwner:self];
167 - (JSValue *)weakOnclick
169 return [m_weakOnclickHandler value];
173 return [m_onclickHandler value];
177 if (!m_onclickHandler)
180 JSValue *function = [m_onclickHandler value];
181 [function callWithArguments:[NSArray array]];
187 @protocol TinyDOMNode <JSExport>
188 - (void)appendChild:(TinyDOMNode *)child;
189 - (NSUInteger)numberOfChildren;
190 - (TinyDOMNode *)childAtIndex:(NSUInteger)index;
191 - (void)removeChildAtIndex:(NSUInteger)index;
194 @interface TinyDOMNode : NSObject<TinyDOMNode>
197 @implementation TinyDOMNode {
198 NSMutableArray *m_children;
199 JSVirtualMachine *m_sharedVirtualMachine;
202 - (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
208 m_children = [[NSMutableArray alloc] initWithCapacity:0];
209 m_sharedVirtualMachine = virtualMachine;
210 #if !__has_feature(objc_arc)
211 [m_sharedVirtualMachine retain];
217 - (void)appendChild:(TinyDOMNode *)child
219 [m_sharedVirtualMachine addManagedReference:child withOwner:self];
220 [m_children addObject:child];
223 - (NSUInteger)numberOfChildren
225 return [m_children count];
228 - (TinyDOMNode *)childAtIndex:(NSUInteger)index
230 if (index >= [m_children count])
232 return [m_children objectAtIndex:index];
235 - (void)removeChildAtIndex:(NSUInteger)index
237 if (index >= [m_children count])
239 [m_sharedVirtualMachine removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
240 [m_children removeObjectAtIndex:index];
245 @interface JSCollection : NSObject
246 - (void)setValue:(JSValue *)value forKey:(NSString *)key;
247 - (JSValue *)valueForKey:(NSString *)key;
250 @implementation JSCollection {
251 NSMutableDictionary *_dict;
259 _dict = [[NSMutableDictionary alloc] init];
264 - (void)setValue:(JSValue *)value forKey:(NSString *)key
266 JSManagedValue *oldManagedValue = [_dict objectForKey:key];
267 if (oldManagedValue) {
268 JSValue* oldValue = [oldManagedValue value];
270 [oldValue.context.virtualMachine removeManagedReference:oldManagedValue withOwner:self];
272 JSManagedValue *managedValue = [JSManagedValue managedValueWithValue:value];
273 [value.context.virtualMachine addManagedReference:managedValue withOwner:self];
274 [_dict setObject:managedValue forKey:key];
277 - (JSValue *)valueForKey:(NSString *)key
279 JSManagedValue *managedValue = [_dict objectForKey:key];
282 return [managedValue value];
286 @protocol InitA <JSExport>
287 - (id)initWithA:(int)a;
291 @protocol InitB <JSExport>
292 - (id)initWithA:(int)a b:(int)b;
295 @protocol InitC <JSExport>
299 @interface ClassA : NSObject<InitA>
302 @interface ClassB : ClassA<InitB>
305 @interface ClassC : ClassB<InitA, InitB>
308 @interface ClassCPrime : ClassB<InitA, InitC>
311 @interface ClassD : NSObject<InitA>
312 - (id)initWithA:(int)a;
315 @interface ClassE : ClassD
316 - (id)initWithA:(int)a;
319 @implementation ClassA {
322 - (id)initWithA:(int)a
338 @implementation ClassB {
341 - (id)initWithA:(int)a b:(int)b
343 self = [super initWithA:a];
353 @implementation ClassC {
356 - (id)initWithA:(int)a
358 return [self initWithA:a b:0];
360 - (id)initWithA:(int)a b:(int)b
362 self = [super initWithA:a b:b];
372 @implementation ClassCPrime
373 - (id)initWithA:(int)a
375 self = [super initWithA:a b:0];
382 return [self initWithA:42];
386 @implementation ClassD
388 - (id)initWithA:(int)a
391 return [[ClassE alloc] initWithA:a];
399 @implementation ClassE {
403 - (id)initWithA:(int)a
415 static bool evilAllocationObjectWasDealloced = false;
417 @interface EvilAllocationObject : NSObject
418 - (JSValue *)doEvilThingsWithContext:(JSContext *)context;
421 @implementation EvilAllocationObject {
422 JSContext *m_context;
424 - (id)initWithContext:(JSContext *)context
436 [self doEvilThingsWithContext:m_context];
437 evilAllocationObjectWasDealloced = true;
438 #if !__has_feature(objc_arc)
443 - (JSValue *)doEvilThingsWithContext:(JSContext *)context
445 JSValue *result = [context evaluateScript:@" \
449 for (var i = 0; i < 10000; ++i) { \
456 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
461 extern "C" void checkResult(NSString *description, bool passed)
463 NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
468 static bool blockSignatureContainsClass()
470 static bool containsClass = ^{
471 id block = ^(NSString *string){ return string; };
472 return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
474 return containsClass;
477 static void* threadMain(void* contextPtr)
479 JSContext *context = (__bridge JSContext*)contextPtr;
481 // Do something to enter the VM.
482 TestObject *testObject = [TestObject testObject];
483 context[@"testObject"] = testObject;
484 pthread_exit(nullptr);
487 // This test is flaky. Since GC marks C stack and registers as roots conservatively,
488 // objects not referenced logically can be accidentally marked and alive.
489 // To avoid this situation as possible as we can,
490 // 1. run this test first before stack is polluted,
491 // 2. extract this test as a function to suppress stack height.
492 static void testWeakValue()
495 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
496 TestObject *testObject = [TestObject testObject];
497 JSManagedValue *weakValue;
499 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
500 context[@"testObject"] = testObject;
501 weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]];
505 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
506 context[@"testObject"] = testObject;
507 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
508 checkResult(@"weak value == nil", ![weakValue value]);
509 checkResult(@"root is still alive", !context[@"testObject"].isUndefined);
514 static void testObjectiveCAPIMain()
517 JSVirtualMachine* vm = [[JSVirtualMachine alloc] init];
518 JSContext* context = [[JSContext alloc] initWithVirtualMachine:vm];
519 [context evaluateScript:@"bad"];
523 JSContext *context = [[JSContext alloc] init];
524 JSValue *result = [context evaluateScript:@"2 + 2"];
525 checkResult(@"2 + 2", result.isNumber && [result toInt32] == 4);
529 JSContext *context = [[JSContext alloc] init];
530 NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]];
531 checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]);
535 JSContext *context = [[JSContext alloc] init];
536 context[@"message"] = @"Hello";
537 JSValue *result = [context evaluateScript:@"message + ', World!'"];
538 checkResult(@"Hello, World!", result.isString && [result isEqualToObject:@"Hello, World!"]);
542 JSContext *context = [[JSContext alloc] init];
543 checkResult(@"Promise is not exposed", [context[@"Promise"] isUndefined]);
544 JSValue *result = [context evaluateScript:@"typeof Promise"];
545 checkResult(@"typeof Promise is 'undefined'", result.isString && [result isEqualToObject:@"undefined"]);
549 JSContext *context = [[JSContext alloc] init];
550 JSValue *result = [context evaluateScript:@"({ x:42 })"];
551 checkResult(@"({ x:42 })", result.isObject && [result[@"x"] isEqualToObject:@42]);
552 id obj = [result toObject];
553 checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]);
554 id num = (NSDictionary *)obj[@"x"];
555 checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]);
559 JSContext *context = [[JSContext alloc] init];
560 JSValue *result = [context evaluateScript:@"[ ]"];
561 checkResult(@"[ ]", result.isArray);
565 JSContext *context = [[JSContext alloc] init];
566 JSValue *result = [context evaluateScript:@"new Date"];
567 checkResult(@"new Date", result.isDate);
571 JSCollection* myPrivateProperties = [[JSCollection alloc] init];
574 JSContext* context = [[JSContext alloc] init];
575 TestObject* rootObject = [TestObject testObject];
576 context[@"root"] = rootObject;
577 [context.virtualMachine addManagedReference:myPrivateProperties withOwner:rootObject];
578 [myPrivateProperties setValue:[JSValue valueWithBool:true inContext:context] forKey:@"is_ham"];
579 [myPrivateProperties setValue:[JSValue valueWithObject:@"hello!" inContext:context] forKey:@"message"];
580 [myPrivateProperties setValue:[JSValue valueWithInt32:42 inContext:context] forKey:@"my_number"];
581 [myPrivateProperties setValue:[JSValue valueWithNullInContext:context] forKey:@"definitely_null"];
582 [myPrivateProperties setValue:[JSValue valueWithUndefinedInContext:context] forKey:@"not_sure_if_undefined"];
584 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
586 JSValue *isHam = [myPrivateProperties valueForKey:@"is_ham"];
587 JSValue *message = [myPrivateProperties valueForKey:@"message"];
588 JSValue *myNumber = [myPrivateProperties valueForKey:@"my_number"];
589 JSValue *definitelyNull = [myPrivateProperties valueForKey:@"definitely_null"];
590 JSValue *notSureIfUndefined = [myPrivateProperties valueForKey:@"not_sure_if_undefined"];
591 checkResult(@"is_ham is true", isHam.isBoolean && [isHam toBool]);
592 checkResult(@"message is hello!", message.isString && [@"hello!" isEqualToString:[message toString]]);
593 checkResult(@"my_number is 42", myNumber.isNumber && [myNumber toInt32] == 42);
594 checkResult(@"definitely_null is null", definitelyNull.isNull);
595 checkResult(@"not_sure_if_undefined is undefined", notSureIfUndefined.isUndefined);
598 checkResult(@"is_ham is nil", ![myPrivateProperties valueForKey:@"is_ham"]);
599 checkResult(@"message is nil", ![myPrivateProperties valueForKey:@"message"]);
600 checkResult(@"my_number is 42", ![myPrivateProperties valueForKey:@"my_number"]);
601 checkResult(@"definitely_null is null", ![myPrivateProperties valueForKey:@"definitely_null"]);
602 checkResult(@"not_sure_if_undefined is undefined", ![myPrivateProperties valueForKey:@"not_sure_if_undefined"]);
606 JSContext *context = [[JSContext alloc] init];
607 JSValue *message = [JSValue valueWithObject:@"hello" inContext:context];
608 TestObject *rootObject = [TestObject testObject];
609 JSCollection *collection = [[JSCollection alloc] init];
610 context[@"root"] = rootObject;
612 JSValue *jsCollection = [JSValue valueWithObject:collection inContext:context];
613 JSManagedValue *weakCollection = [JSManagedValue managedValueWithValue:jsCollection andOwner:rootObject];
614 [context.virtualMachine addManagedReference:weakCollection withOwner:message];
615 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
620 JSContext *context = [[JSContext alloc] init];
622 context[@"blockCallback"] = ^(int value){
625 [context evaluateScript:@"blockCallback(42)"];
626 checkResult(@"blockCallback", result == 42);
629 if (blockSignatureContainsClass()) {
631 JSContext *context = [[JSContext alloc] init];
632 __block bool result = false;
633 context[@"blockCallback"] = ^(NSString *value){
634 result = [@"42" isEqualToString:value] == YES;
636 [context evaluateScript:@"blockCallback(42)"];
637 checkResult(@"blockCallback(NSString *)", result);
640 NSLog(@"Skipping 'blockCallback(NSString *)' test case");
643 JSContext *context = [[JSContext alloc] init];
644 checkResult(@"!context.exception", !context.exception);
645 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
646 checkResult(@"context.exception", context.exception);
650 JSContext *context = [[JSContext alloc] init];
651 __block bool caught = false;
652 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
657 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
658 checkResult(@"JSContext.exceptionHandler", caught);
662 JSContext *context = [[JSContext alloc] init];
663 __block int expectedExceptionLineNumber = 1;
664 __block bool sawExpectedExceptionLineNumber = false;
665 context.exceptionHandler = ^(JSContext *, JSValue *exception) {
666 sawExpectedExceptionLineNumber = [exception[@"line"] toInt32] == expectedExceptionLineNumber;
668 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
669 checkResult(@"evaluteScript exception on line 1", sawExpectedExceptionLineNumber);
671 expectedExceptionLineNumber = 2;
672 sawExpectedExceptionLineNumber = false;
673 [context evaluateScript:@"// Line 1\n!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
674 checkResult(@"evaluteScript exception on line 2", sawExpectedExceptionLineNumber);
678 JSContext *context = [[JSContext alloc] init];
679 __block bool emptyExceptionSourceURL = false;
680 context.exceptionHandler = ^(JSContext *, JSValue *exception) {
681 emptyExceptionSourceURL = exception[@"sourceURL"].isUndefined;
683 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
684 checkResult(@"evaluteScript: exception has no sourceURL", emptyExceptionSourceURL);
686 __block NSString *exceptionSourceURL = nil;
687 context.exceptionHandler = ^(JSContext *, JSValue *exception) {
688 exceptionSourceURL = [exception[@"sourceURL"] toString];
690 NSURL *url = [NSURL fileURLWithPath:@"/foo/bar.js"];
691 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()" withSourceURL:url];
692 checkResult(@"evaluateScript:withSourceURL: exception has expected sourceURL", [exceptionSourceURL isEqualToString:[url absoluteString]]);
696 JSContext *context = [[JSContext alloc] init];
697 context[@"callback"] = ^{
698 JSContext *context = [JSContext currentContext];
699 context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context];
701 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
702 checkResult(@"Explicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
703 checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception);
707 JSContext *context = [[JSContext alloc] init];
708 context[@"callback"] = ^{
709 JSContext *context = [JSContext currentContext];
710 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
712 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
713 checkResult(@"Implicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
714 checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception);
718 JSContext *context = [[JSContext alloc] init];
719 [context evaluateScript:
720 @"function sum(array) { \
722 for (var i in array) \
723 result += array[i]; \
726 JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context];
727 JSValue *sumFunction = context[@"sum"];
728 JSValue *result = [sumFunction callWithArguments:@[ array ]];
729 checkResult(@"sum([13, 2, 7])", [result toInt32] == 22);
733 JSContext *context = [[JSContext alloc] init];
734 JSValue *mulAddFunction = [context evaluateScript:
735 @"(function(array, object) { \
737 for (var i in array) \
738 result.push(array[i] * object.x + object.y); \
741 JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]];
742 checkResult(@"mulAddFunction", result.isObject && [[result toString] isEqual:@"43,44,46"]);
746 JSContext *context = [[JSContext alloc] init];
747 JSValue *array = [JSValue valueWithNewArrayInContext:context];
748 checkResult(@"arrayLengthEmpty", [[array[@"length"] toNumber] unsignedIntegerValue] == 0);
749 JSValue *value1 = [JSValue valueWithInt32:42 inContext:context];
750 JSValue *value2 = [JSValue valueWithInt32:24 inContext:context];
751 NSUInteger lowIndex = 5;
752 NSUInteger maxLength = UINT_MAX;
754 [array setValue:value1 atIndex:lowIndex];
755 checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1));
757 [array setValue:value1 atIndex:(maxLength - 1)];
758 checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
760 [array setValue:value2 atIndex:maxLength];
761 checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
763 [array setValue:value2 atIndex:(maxLength + 1)];
764 checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
766 if (sizeof(NSUInteger) == 8)
767 checkResult(@"valueAtIndex:0 is undefined", [array valueAtIndex:0].isUndefined);
769 checkResult(@"valueAtIndex:0", [[array valueAtIndex:0] toInt32] == 24);
770 checkResult(@"valueAtIndex:lowIndex", [[array valueAtIndex:lowIndex] toInt32] == 42);
771 checkResult(@"valueAtIndex:maxLength - 1", [[array valueAtIndex:(maxLength - 1)] toInt32] == 42);
772 checkResult(@"valueAtIndex:maxLength", [[array valueAtIndex:maxLength] toInt32] == 24);
773 checkResult(@"valueAtIndex:maxLength + 1", [[array valueAtIndex:(maxLength + 1)] toInt32] == 24);
777 JSContext *context = [[JSContext alloc] init];
778 JSValue *object = [JSValue valueWithNewObjectInContext:context];
780 object[@"point"] = @{ @"x":@1, @"y":@2 };
781 object[@"point"][@"x"] = @3;
782 CGPoint point = [object[@"point"] toPoint];
783 checkResult(@"toPoint", point.x == 3 && point.y == 2);
785 object[@{ @"toString":^{ return @"foo"; } }] = @"bar";
786 checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
788 object[[@"foobar" substringToIndex:3]] = @"bar";
789 checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
793 JSContext *context = [[JSContext alloc] init];
794 TextXYZ *testXYZ = [[TextXYZ alloc] init];
795 context[@"testXYZ"] = testXYZ;
799 [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"];
800 [context evaluateScript:@"testXYZ.test('test')"];
801 checkResult(@"TextXYZ - testXYZTested", testXYZTested);
802 JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"];
803 checkResult(@"TextXYZ - result", [result isEqualToObject:@"13,4,undefined"]);
807 JSContext *context = [[JSContext alloc] init];
808 [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{
809 JSPropertyDescriptorGetKey:^{
810 return [JSContext currentThis][@"x"];
813 JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context];
814 int result = [object [@"getterProperty"] toInt32];
815 checkResult(@"getterProperty", result == 101);
819 JSContext *context = [[JSContext alloc] init];
820 context[@"concatenate"] = ^{
821 NSArray *arguments = [JSContext currentArguments];
822 if (![arguments count])
824 NSString *message = [arguments[0] description];
825 for (NSUInteger index = 1; index < [arguments count]; ++index)
826 message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]];
829 JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"];
830 checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]);
834 JSContext *context = [[JSContext alloc] init];
835 context[@"foo"] = @YES;
836 checkResult(@"@YES is boolean", [context[@"foo"] isBoolean]);
837 JSValue *result = [context evaluateScript:@"typeof foo"];
838 checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]);
842 JSContext *context = [[JSContext alloc] init];
843 JSValue *result = [context evaluateScript:@"String(console)"];
844 checkResult(@"String(console)", [result isEqualToObject:@"[object Console]"]);
845 result = [context evaluateScript:@"typeof console.log"];
846 checkResult(@"typeof console.log", [result isEqualToObject:@"function"]);
850 JSContext *context = [[JSContext alloc] init];
851 TestObject* testObject = [TestObject testObject];
852 context[@"testObject"] = testObject;
853 JSValue *result = [context evaluateScript:@"String(testObject)"];
854 checkResult(@"String(testObject)", [result isEqualToObject:@"[object TestObject]"]);
858 JSContext *context = [[JSContext alloc] init];
859 TestObject* testObject = [TestObject testObject];
860 context[@"testObject"] = testObject;
861 JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"];
862 checkResult(@"String(testObject.__proto__)", [result isEqualToObject:@"[object TestObjectPrototype]"]);
866 JSContext *context = [[JSContext alloc] init];
867 context[@"TestObject"] = [TestObject class];
868 JSValue *result = [context evaluateScript:@"String(TestObject)"];
869 checkResult(@"String(TestObject)", [result isEqualToObject:@"function TestObject() {\n [native code]\n}"]);
873 JSContext *context = [[JSContext alloc] init];
874 JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context];
875 checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]);
879 JSContext *context = [[JSContext alloc] init];
880 context[@"TestObject"] = [TestObject class];
881 JSValue *result = [context evaluateScript:@"TestObject.parentTest()"];
882 checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]);
886 JSContext *context = [[JSContext alloc] init];
887 TestObject* testObject = [TestObject testObject];
888 context[@"testObjectA"] = testObject;
889 context[@"testObjectB"] = testObject;
890 JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"];
891 checkResult(@"testObjectA == testObjectB", result.isBoolean && [result toBool]);
895 JSContext *context = [[JSContext alloc] init];
896 TestObject* testObject = [TestObject testObject];
897 context[@"testObject"] = testObject;
898 testObject.point = (CGPoint){3,4};
899 JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"];
900 checkResult(@"testObject.point - result", [result isEqualToObject:@"{\"x\":3,\"y\":4}"]);
901 checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14);
905 JSContext *context = [[JSContext alloc] init];
906 TestObject* testObject = [TestObject testObject];
908 context[@"testObject"] = testObject;
909 context[@"mul"] = ^(int x, int y){ return x * y; };
910 JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"];
911 checkResult(@"mul(testObject.six, 7)", result.isNumber && [result toInt32] == 42);
915 JSContext *context = [[JSContext alloc] init];
916 TestObject* testObject = [TestObject testObject];
917 context[@"testObject"] = testObject;
918 context[@"testObject"][@"variable"] = @4;
919 [context evaluateScript:@"++testObject.variable"];
920 checkResult(@"++testObject.variable", testObject.variable == 5);
924 JSContext *context = [[JSContext alloc] init];
925 context[@"point"] = @{ @"x":@6, @"y":@7 };
926 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
927 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
931 JSContext *context = [[JSContext alloc] init];
932 context[@"point"] = @{ @"x":@6, @"y":@7 };
933 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
934 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
938 JSContext *context = [[JSContext alloc] init];
939 TestObject* testObject = [TestObject testObject];
940 context[@"testObject"] = testObject;
941 JSValue *result = [context evaluateScript:@"testObject.getString()"];
942 checkResult(@"testObject.getString()", result.isString && [result toInt32] == 42);
946 JSContext *context = [[JSContext alloc] init];
947 TestObject* testObject = [TestObject testObject];
948 context[@"testObject"] = testObject;
949 JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"];
950 checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]);
954 JSContext *context = [[JSContext alloc] init];
955 TestObject* testObject = [TestObject testObject];
956 context[@"testObject"] = testObject;
957 JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"];
958 checkResult(@"testObject.getString.call(testObject)", result.isString && [result toInt32] == 42);
962 JSContext *context = [[JSContext alloc] init];
963 TestObject* testObject = [TestObject testObject];
964 context[@"testObject"] = testObject;
965 checkResult(@"testObject.getString.call({}) pre", !context.exception);
966 [context evaluateScript:@"testObject.getString.call({})"];
967 checkResult(@"testObject.getString.call({}) post", context.exception);
971 JSContext *context = [[JSContext alloc] init];
972 TestObject* testObject = [TestObject testObject];
973 context[@"testObject"] = testObject;
974 JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"];
975 checkResult(@"testObject.callback", result.isNumber && [result toInt32] == 42);
976 result = [context evaluateScript:@"testObject.bogusCallback"];
977 checkResult(@"testObject.bogusCallback == undefined", result.isUndefined);
981 JSContext *context = [[JSContext alloc] init];
982 TestObject *testObject = [TestObject testObject];
983 context[@"testObject"] = testObject;
984 JSValue *result = [context evaluateScript:@"Function.prototype.toString.call(testObject.callback)"];
985 checkResult(@"Function.prototype.toString", !context.exception && !result.isUndefined);
989 JSContext *context1 = [[JSContext alloc] init];
990 JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine];
991 JSValue *value = [JSValue valueWithDouble:42 inContext:context2];
992 context1[@"passValueBetweenContexts"] = value;
993 JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"];
994 checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]);
998 JSContext *context = [[JSContext alloc] init];
999 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
1000 NSDictionary *expectedDict = @{
1001 @"foo" : [NSNumber numberWithInt:1],
1003 @"baz": [NSNumber numberWithInt:2]
1006 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
1008 [context evaluateScript:@"var myDict = { \
1012 handleTheDictionary(myDict);"];
1014 context[@"handleTheArray"] = ^(NSArray *array) {
1015 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
1016 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
1018 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
1022 JSContext *context = [[JSContext alloc] init];
1023 TestObject *testObject = [TestObject testObject];
1025 context[@"testObject"] = testObject;
1026 [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"];
1027 [context evaluateScript:@"testObject = undefined"];
1030 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1033 context[@"testObject"] = testObject;
1038 JSContext *context = [[JSContext alloc] init];
1039 TextXYZ *testXYZ = [[TextXYZ alloc] init];
1042 context[@"testXYZ"] = testXYZ;
1044 [context evaluateScript:@" \
1046 testXYZ.onclick = function() { \
1050 testXYZ.weakOnclick = function() { \
1058 JSValue *result = [context evaluateScript:@"didClick"];
1059 checkResult(@"Event handler onclick", [result toBool]);
1062 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1065 JSValue *result = [context evaluateScript:@"testXYZ.onclick"];
1066 checkResult(@"onclick still around after GC", !(result.isNull || result.isUndefined));
1071 JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"];
1072 checkResult(@"weakOnclick not around after GC", result.isNull || result.isUndefined);
1076 [context evaluateScript:@" \
1082 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1086 JSValue *result = [context evaluateScript:@"didClick"];
1087 checkResult(@"Event handler onclick doesn't fire", ![result toBool]);
1092 JSContext *context = [[JSContext alloc] init];
1093 TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
1094 TinyDOMNode *lastNode = root;
1095 for (NSUInteger i = 0; i < 3; i++) {
1096 TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
1097 [lastNode appendChild:newNode];
1102 context[@"root"] = root;
1103 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
1104 TinyDOMNode *lastNode = nil;
1107 head = [lastNode childAtIndex:0];
1111 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
1114 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1116 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
1117 checkResult(@"My custom property == 42", myCustomProperty.isNumber && [myCustomProperty toInt32] == 42);
1121 JSContext *context = [[JSContext alloc] init];
1122 TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
1123 TinyDOMNode *lastNode = root;
1124 for (NSUInteger i = 0; i < 3; i++) {
1125 TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
1126 [lastNode appendChild:newNode];
1131 context[@"root"] = root;
1132 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
1133 TinyDOMNode *lastNode = nil;
1136 head = [lastNode childAtIndex:0];
1140 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
1142 [root appendChild:[root childAtIndex:0]];
1143 [root removeChildAtIndex:0];
1146 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1148 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
1149 checkResult(@"duplicate calls to addManagedReference don't cause things to die", myCustomProperty.isNumber && [myCustomProperty toInt32] == 42);
1153 JSContext *context = [[JSContext alloc] init];
1154 JSValue *o = [JSValue valueWithNewObjectInContext:context];
1156 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1158 checkResult(@"JSValue correctly protected its internal value", [[o[@"foo"] toString] isEqualToString:@"foo"]);
1162 JSContext *context = [[JSContext alloc] init];
1163 TestObject *testObject = [TestObject testObject];
1164 context[@"testObject"] = testObject;
1165 [context evaluateScript:@"testObject.__lookupGetter__('variable').call({})"];
1166 checkResult(@"Make sure we throw an exception when calling getter on incorrect |this|", context.exception);
1170 TestObject *testObject = [TestObject testObject];
1171 JSManagedValue *managedTestObject;
1173 JSContext *context = [[JSContext alloc] init];
1174 context[@"testObject"] = testObject;
1175 managedTestObject = [JSManagedValue managedValueWithValue:context[@"testObject"]];
1176 [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject];
1181 JSContext *context = [[JSContext alloc] init];
1182 TestObject *testObject = [TestObject testObject];
1183 context[@"testObject"] = testObject;
1184 JSManagedValue *managedValue = nil;
1186 JSValue *object = [JSValue valueWithNewObjectInContext:context];
1187 managedValue = [JSManagedValue managedValueWithValue:object andOwner:testObject];
1188 [context.virtualMachine addManagedReference:managedValue withOwner:testObject];
1190 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1194 JSContext *context = [[JSContext alloc] init];
1195 context[@"MyClass"] = ^{
1196 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1197 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1198 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1199 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyClass"][@"prototype"] JSValueRef]);
1203 context[@"MyOtherClass"] = ^{
1204 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1205 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1206 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1207 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyOtherClass"][@"prototype"] JSValueRef]);
1211 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
1212 NSLog(@"EXCEPTION: %@", [exception toString]);
1213 context.exception = nil;
1216 JSValue *constructor1 = context[@"MyClass"];
1217 JSValue *constructor2 = context[@"MyOtherClass"];
1219 JSValue *value1 = [context evaluateScript:@"new MyClass()"];
1220 checkResult(@"value1 instanceof MyClass", [value1 isInstanceOf:constructor1]);
1221 checkResult(@"!(value1 instanceof MyOtherClass)", ![value1 isInstanceOf:constructor2]);
1222 checkResult(@"MyClass.prototype.constructor === MyClass", [[context evaluateScript:@"MyClass.prototype.constructor === MyClass"] toBool]);
1223 checkResult(@"MyClass instanceof Function", [[context evaluateScript:@"MyClass instanceof Function"] toBool]);
1225 JSValue *value2 = [context evaluateScript:@"new MyOtherClass()"];
1226 checkResult(@"value2 instanceof MyOtherClass", [value2 isInstanceOf:constructor2]);
1227 checkResult(@"!(value2 instanceof MyClass)", ![value2 isInstanceOf:constructor1]);
1228 checkResult(@"MyOtherClass.prototype.constructor === MyOtherClass", [[context evaluateScript:@"MyOtherClass.prototype.constructor === MyOtherClass"] toBool]);
1229 checkResult(@"MyOtherClass instanceof Function", [[context evaluateScript:@"MyOtherClass instanceof Function"] toBool]);
1233 JSContext *context = [[JSContext alloc] init];
1234 context[@"MyClass"] = ^{
1235 NSLog(@"I'm intentionally not returning anything.");
1237 JSValue *result = [context evaluateScript:@"new MyClass()"];
1238 checkResult(@"result === undefined", result.isUndefined);
1239 checkResult(@"exception.message is correct'", context.exception
1240 && [@"Objective-C blocks called as constructors must return an object." isEqualToString:[context.exception[@"message"] toString]]);
1244 checkResult(@"[JSContext currentThis] == nil outside of callback", ![JSContext currentThis]);
1245 checkResult(@"[JSContext currentArguments] == nil outside of callback", ![JSContext currentArguments]);
1246 if ([JSContext currentCallee])
1247 checkResult(@"[JSContext currentCallee] == nil outside of callback", ![JSContext currentCallee]);
1250 if ([JSContext currentCallee]) {
1252 JSContext *context = [[JSContext alloc] init];
1253 context[@"testFunction"] = ^{
1254 checkResult(@"testFunction.foo === 42", [[JSContext currentCallee][@"foo"] toInt32] == 42);
1256 context[@"testFunction"][@"foo"] = @42;
1257 [context[@"testFunction"] callWithArguments:nil];
1259 context[@"TestConstructor"] = ^{
1260 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1261 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1262 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1263 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentCallee][@"prototype"] JSValueRef]);
1266 checkResult(@"(new TestConstructor) instanceof TestConstructor", [context evaluateScript:@"(new TestConstructor) instanceof TestConstructor"]);
1271 JSContext *context = [[JSContext alloc] init];
1272 context[@"TestObject"] = [TestObject class];
1273 JSValue *testObject = [context evaluateScript:@"(new TestObject())"];
1274 checkResult(@"testObject instanceof TestObject", [testObject isInstanceOf:context[@"TestObject"]]);
1276 context[@"TextXYZ"] = [TextXYZ class];
1277 JSValue *textObject = [context evaluateScript:@"(new TextXYZ(\"Called TextXYZ constructor!\"))"];
1278 checkResult(@"textObject instanceof TextXYZ", [textObject isInstanceOf:context[@"TextXYZ"]]);
1282 JSContext *context = [[JSContext alloc] init];
1283 context[@"ClassA"] = [ClassA class];
1284 context[@"ClassB"] = [ClassB class];
1285 context[@"ClassC"] = [ClassC class]; // Should print error message about too many inits found.
1286 context[@"ClassCPrime"] = [ClassCPrime class]; // Ditto.
1288 JSValue *a = [context evaluateScript:@"(new ClassA(42))"];
1289 checkResult(@"a instanceof ClassA", [a isInstanceOf:context[@"ClassA"]]);
1290 checkResult(@"a.initialize() is callable", [[a invokeMethod:@"initialize" withArguments:@[]] toInt32] == 42);
1292 JSValue *b = [context evaluateScript:@"(new ClassB(42, 53))"];
1293 checkResult(@"b instanceof ClassB", [b isInstanceOf:context[@"ClassB"]]);
1295 JSValue *canConstructClassC = [context evaluateScript:@"(function() { \
1297 (new ClassC(1, 2)); \
1303 checkResult(@"shouldn't be able to construct ClassC", ![canConstructClassC toBool]);
1304 JSValue *canConstructClassCPrime = [context evaluateScript:@"(function() { \
1306 (new ClassCPrime(1)); \
1312 checkResult(@"shouldn't be able to construct ClassCPrime", ![canConstructClassCPrime toBool]);
1316 JSContext *context = [[JSContext alloc] init];
1317 context[@"ClassD"] = [ClassD class];
1318 context[@"ClassE"] = [ClassE class];
1320 JSValue *d = [context evaluateScript:@"(new ClassD())"];
1321 checkResult(@"Returning instance of ClassE from ClassD's init has correct class", [d isInstanceOf:context[@"ClassE"]]);
1325 JSContext *context = [[JSContext alloc] init];
1326 while (!evilAllocationObjectWasDealloced) {
1328 EvilAllocationObject *evilObject = [[EvilAllocationObject alloc] initWithContext:context];
1329 context[@"evilObject"] = evilObject;
1330 context[@"evilObject"] = nil;
1333 checkResult(@"EvilAllocationObject was successfully dealloced without crashing", evilAllocationObjectWasDealloced);
1337 JSContext *context = [[JSContext alloc] init];
1338 checkResult(@"default context.name is nil", context.name == nil);
1339 NSString *name1 = @"Name1";
1340 NSString *name2 = @"Name2";
1341 context.name = name1;
1342 NSString *fetchedName1 = context.name;
1343 context.name = name2;
1344 NSString *fetchedName2 = context.name;
1346 NSString *fetchedName3 = context.name;
1347 checkResult(@"fetched context.name was expected", [fetchedName1 isEqualToString:name1]);
1348 checkResult(@"fetched context.name was expected", [fetchedName2 isEqualToString:name2]);
1349 checkResult(@"fetched context.name was expected", ![fetchedName1 isEqualToString:fetchedName2]);
1350 checkResult(@"fetched context.name was expected", fetchedName3 == nil);
1354 JSContext *context = [[JSContext alloc] init];
1355 context[@"UnexportedObject"] = [UnexportedObject class];
1356 context[@"makeObject"] = ^{
1357 return [[UnexportedObject alloc] init];
1359 JSValue *result = [context evaluateScript:@"(makeObject() instanceof UnexportedObject)"];
1360 checkResult(@"makeObject() instanceof UnexportedObject", result.isBoolean && [result toBool]);
1364 JSContext *context = [[JSContext alloc] init];
1365 [[JSValue valueWithInt32:42 inContext:context] toDictionary];
1366 [[JSValue valueWithInt32:42 inContext:context] toArray];
1370 JSContext *context = [[JSContext alloc] init];
1372 // Create the root, make it reachable from JS, and force an EdenCollection
1373 // so that we scan the external object graph.
1374 TestObject *root = [TestObject testObject];
1376 context[@"root"] = root;
1378 JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]);
1380 // Create a new Obj-C object only reachable via the external object graph
1381 // through the object we already scanned during the EdenCollection.
1382 TestObject *child = [TestObject testObject];
1383 [context.virtualMachine addManagedReference:child withOwner:root];
1385 // Create a new managed JSValue that will only be kept alive if we properly rescan
1386 // the external object graph.
1387 JSManagedValue *managedJSObject = nil;
1389 JSValue *jsObject = [JSValue valueWithObject:@"hello" inContext:context];
1390 managedJSObject = [JSManagedValue managedValueWithValue:jsObject];
1391 [context.virtualMachine addManagedReference:managedJSObject withOwner:child];
1394 // Force another EdenCollection. It should rescan the new part of the external object graph.
1395 JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]);
1397 // Check that the managed JSValue is still alive.
1398 checkResult(@"EdenCollection doesn't reclaim new managed values", [managedJSObject value] != nil);
1402 JSContext *context = [[JSContext alloc] init];
1405 pthread_create(&threadID, NULL, &threadMain, (__bridge void*)context);
1406 pthread_join(threadID, nullptr);
1407 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1409 checkResult(@"Did not crash after entering the VM from another thread", true);
1412 currentThisInsideBlockGetterTest();
1419 @protocol NumberProtocol <JSExport>
1421 @property (nonatomic) NSInteger number;
1425 @interface NumberObject : NSObject <NumberProtocol>
1427 @property (nonatomic) NSInteger number;
1431 @implementation NumberObject
1435 // Check that negative NSIntegers retain the correct value when passed into JS code.
1436 static void checkNegativeNSIntegers()
1438 NumberObject *container = [[NumberObject alloc] init];
1439 container.number = -1;
1440 JSContext *context = [[JSContext alloc] init];
1441 context[@"container"] = container;
1442 NSString *jsID = @"var getContainerNumber = function() { return container.number }";
1443 [context evaluateScript:jsID];
1444 JSValue *jsFunction = context[@"getContainerNumber"];
1445 JSValue *result = [jsFunction callWithArguments:@[]];
1447 checkResult(@"Negative number maintained its original value", [[result toString] isEqualToString:@"-1"]);
1450 void testObjectiveCAPI()
1452 NSLog(@"Testing Objective-C API");
1453 checkNegativeNSIntegers();
1455 testObjectiveCAPIMain();
1460 void testObjectiveCAPI()
1464 #endif // JSC_OBJC_API_ENABLED