2 * Copyright (C) 2013 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 extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
30 extern "C" bool _Block_has_signature(id);
31 extern "C" const char * _Block_signature(id);
34 extern "C" void testObjectiveCAPI(void);
36 #if JSC_OBJC_API_ENABLED
38 @protocol ParentObject <JSExport>
41 @interface ParentObject : NSObject<ParentObject>
42 + (NSString *)parentTest;
45 @implementation ParentObject
46 + (NSString *)parentTest
48 return [self description];
52 @protocol TestObject <JSExport>
53 @property int variable;
54 @property (readonly) int six;
55 @property CGPoint point;
56 + (NSString *)classTest;
57 + (NSString *)parentTest;
58 - (NSString *)getString;
59 JSExportAs(testArgumentTypes,
60 - (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
62 - (void)callback:(JSValue *)function;
63 - (void)bogusCallback:(void(^)(int))function;
66 @interface TestObject : ParentObject <TestObject>
71 @implementation TestObject
77 return [[TestObject alloc] init];
79 + (NSString *)classTest
81 return @"classTest - okay";
83 - (NSString *)getString
87 - (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
89 return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]];
91 - (void)callback:(JSValue *)function
93 [function callWithArguments:[NSArray arrayWithObject:[NSNumber numberWithInt:42]]];
95 - (void)bogusCallback:(void(^)(int))function
101 bool testXYZTested = false;
103 @protocol TextXYZ <JSExport>
105 @property (readonly) int y;
106 @property (assign) JSValue *onclick;
107 @property (assign) JSValue *weakOnclick;
108 - (void)test:(NSString *)message;
111 @interface TextXYZ : NSObject <TextXYZ>
118 @implementation TextXYZ {
119 JSManagedValue *m_weakOnclickHandler;
120 JSManagedValue *m_onclickHandler;
125 - (void)test:(NSString *)message
127 testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5;
129 - (void)setWeakOnclick:(JSValue *)value
131 m_weakOnclickHandler = [JSManagedValue managedValueWithValue:value];
134 - (void)setOnclick:(JSValue *)value
136 m_onclickHandler = [JSManagedValue managedValueWithValue:value owner:self];
138 - (JSValue *)weakOnclick
140 return [m_weakOnclickHandler value];
144 return [m_onclickHandler value];
148 if (!m_onclickHandler)
151 JSValue *function = [m_onclickHandler value];
152 [function callWithArguments:[NSArray array]];
158 @protocol TinyDOMNode <JSExport>
159 - (void)appendChild:(TinyDOMNode *)child;
160 - (NSUInteger)numberOfChildren;
161 - (TinyDOMNode *)childAtIndex:(NSUInteger)index;
162 - (void)removeChildAtIndex:(NSUInteger)index;
165 @interface TinyDOMNode : NSObject<TinyDOMNode>
166 + (JSVirtualMachine *)sharedVirtualMachine;
167 + (void)clearSharedVirtualMachine;
170 @implementation TinyDOMNode {
171 NSMutableArray *m_children;
174 static JSVirtualMachine *sharedInstance = nil;
176 + (JSVirtualMachine *)sharedVirtualMachine
179 sharedInstance = [[JSVirtualMachine alloc] init];
180 return sharedInstance;
183 + (void)clearSharedVirtualMachine
185 sharedInstance = nil;
194 m_children = [[NSMutableArray alloc] initWithCapacity:0];
201 NSEnumerator *enumerator = [m_children objectEnumerator];
203 while ((nextChild = [enumerator nextObject]))
204 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:nextChild withOwner:self];
206 #if !__has_feature(objc_arc)
211 - (void)appendChild:(TinyDOMNode *)child
213 [[TinyDOMNode sharedVirtualMachine] addManagedReference:child withOwner:self];
214 [m_children addObject:child];
217 - (NSUInteger)numberOfChildren
219 return [m_children count];
222 - (TinyDOMNode *)childAtIndex:(NSUInteger)index
224 if (index >= [m_children count])
226 return [m_children objectAtIndex:index];
229 - (void)removeChildAtIndex:(NSUInteger)index
231 if (index >= [m_children count])
233 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
234 [m_children removeObjectAtIndex:index];
239 static void checkResult(NSString *description, bool passed)
241 NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
246 static bool blockSignatureContainsClass()
248 static bool containsClass = ^{
249 id block = ^(NSString *string){ return string; };
250 return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
252 return containsClass;
255 void testObjectiveCAPI()
257 NSLog(@"Testing Objective-C API");
260 JSContext *context = [[JSContext alloc] init];
261 JSValue *result = [context evaluateScript:@"2 + 2"];
262 checkResult(@"2 + 2", [result isNumber] && [result toInt32] == 4);
266 JSContext *context = [[JSContext alloc] init];
267 NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]];
268 checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]);
272 JSContext *context = [[JSContext alloc] init];
273 context[@"message"] = @"Hello";
274 JSValue *result = [context evaluateScript:@"message + ', World!'"];
275 checkResult(@"Hello, World!", [result isString] && [result isEqualToObject:@"Hello, World!"]);
279 JSContext *context = [[JSContext alloc] init];
280 JSValue *result = [context evaluateScript:@"({ x:42 })"];
281 checkResult(@"({ x:42 })", [result isObject] && [result[@"x"] isEqualToObject:@42]);
282 id obj = [result toObject];
283 checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]);
284 id num = (NSDictionary *)obj[@"x"];
285 checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]);
289 JSContext *context = [[JSContext alloc] init];
291 context[@"blockCallback"] = ^(int value){
294 [context evaluateScript:@"blockCallback(42)"];
295 checkResult(@"blockCallback", result == 42);
298 if (blockSignatureContainsClass()) {
300 JSContext *context = [[JSContext alloc] init];
301 __block bool result = false;
302 context[@"blockCallback"] = ^(NSString *value){
303 result = [@"42" isEqualToString:value] == YES;
305 [context evaluateScript:@"blockCallback(42)"];
306 checkResult(@"blockCallback(NSString *)", result);
309 NSLog(@"Skipping 'blockCallback(NSString *)' test case");
312 JSContext *context = [[JSContext alloc] init];
313 checkResult(@"!context.exception", !context.exception);
314 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
315 checkResult(@"context.exception", context.exception);
319 JSContext *context = [[JSContext alloc] init];
320 __block bool caught = false;
321 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
326 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
327 checkResult(@"JSContext.exceptionHandler", caught);
331 JSContext *context = [[JSContext alloc] init];
332 context[@"callback"] = ^{
333 JSContext *context = [JSContext currentContext];
334 context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context];
336 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
337 checkResult(@"Explicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
338 checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception);
342 JSContext *context = [[JSContext alloc] init];
343 context[@"callback"] = ^{
344 JSContext *context = [JSContext currentContext];
345 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
347 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
348 checkResult(@"Implicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
349 checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception);
353 JSContext *context = [[JSContext alloc] init];
354 [context evaluateScript:
355 @"function sum(array) { \
357 for (var i in array) \
358 result += array[i]; \
361 JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context];
362 JSValue *sumFunction = context[@"sum"];
363 JSValue *result = [sumFunction callWithArguments:@[ array ]];
364 checkResult(@"sum([13, 2, 7])", [result toInt32] == 22);
368 JSContext *context = [[JSContext alloc] init];
369 JSValue *mulAddFunction = [context evaluateScript:
370 @"(function(array, object) { \
372 for (var i in array) \
373 result.push(array[i] * object.x + object.y); \
376 JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]];
377 checkResult(@"mulAddFunction", [result isObject] && [[result toString] isEqual:@"43,44,46"]);
381 JSContext *context = [[JSContext alloc] init];
382 JSValue *array = [JSValue valueWithNewArrayInContext:context];
383 checkResult(@"arrayLengthEmpty", [[array[@"length"] toNumber] unsignedIntegerValue] == 0);
384 JSValue *value1 = [JSValue valueWithInt32:42 inContext:context];
385 JSValue *value2 = [JSValue valueWithInt32:24 inContext:context];
386 NSUInteger lowIndex = 5;
387 NSUInteger maxLength = UINT_MAX;
389 [array setValue:value1 atIndex:lowIndex];
390 checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1));
392 [array setValue:value1 atIndex:(maxLength - 1)];
393 checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
395 [array setValue:value2 atIndex:maxLength];
396 checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
398 [array setValue:value2 atIndex:(maxLength + 1)];
399 checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
401 checkResult(@"valueAtIndex:0 is undefined", [[array valueAtIndex:0] isUndefined]);
402 checkResult(@"valueAtIndex:lowIndex", [[array valueAtIndex:lowIndex] toInt32] == 42);
403 checkResult(@"valueAtIndex:maxLength - 1", [[array valueAtIndex:(maxLength - 1)] toInt32] == 42);
404 checkResult(@"valueAtIndex:maxLength", [[array valueAtIndex:maxLength] toInt32] == 24);
405 checkResult(@"valueAtIndex:maxLength + 1", [[array valueAtIndex:(maxLength + 1)] toInt32] == 24);
409 JSContext *context = [[JSContext alloc] init];
410 JSValue *object = [JSValue valueWithNewObjectInContext:context];
412 object[@"point"] = @{ @"x":@1, @"y":@2 };
413 object[@"point"][@"x"] = @3;
414 CGPoint point = [object[@"point"] toPoint];
415 checkResult(@"toPoint", point.x == 3 && point.y == 2);
417 object[@{ @"toString":^{ return @"foo"; } }] = @"bar";
418 checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
420 object[[@"foobar" substringToIndex:3]] = @"bar";
421 checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
425 JSContext *context = [[JSContext alloc] init];
426 TextXYZ *testXYZ = [[TextXYZ alloc] init];
427 context[@"testXYZ"] = testXYZ;
431 [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"];
432 [context evaluateScript:@"testXYZ.test('test')"];
433 checkResult(@"TextXYZ - testXYZTested", testXYZTested);
434 JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"];
435 checkResult(@"TextXYZ - result", [result isEqualToObject:@"13,4,undefined"]);
439 JSContext *context = [[JSContext alloc] init];
440 [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{
441 JSPropertyDescriptorGetKey:^{
442 return [JSContext currentThis][@"x"];
445 JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context];
446 int result = [object [@"getterProperty"] toInt32];
447 checkResult(@"getterProperty", result == 101);
451 JSContext *context = [[JSContext alloc] init];
452 context[@"concatenate"] = ^{
453 NSArray *arguments = [JSContext currentArguments];
454 if (![arguments count])
456 NSString *message = [arguments[0] description];
457 for (NSUInteger index = 1; index < [arguments count]; ++index)
458 message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]];
461 JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"];
462 checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]);
466 JSContext *context = [[JSContext alloc] init];
467 context[@"foo"] = @YES;
468 checkResult(@"@YES is boolean", [context[@"foo"] isBoolean]);
469 JSValue *result = [context evaluateScript:@"typeof foo"];
470 checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]);
474 JSContext *context = [[JSContext alloc] init];
475 TestObject* testObject = [TestObject testObject];
476 context[@"testObject"] = testObject;
477 JSValue *result = [context evaluateScript:@"String(testObject)"];
478 checkResult(@"String(testObject)", [result isEqualToObject:@"[object TestObject]"]);
482 JSContext *context = [[JSContext alloc] init];
483 TestObject* testObject = [TestObject testObject];
484 context[@"testObject"] = testObject;
485 JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"];
486 checkResult(@"String(testObject.__proto__)", [result isEqualToObject:@"[object TestObjectPrototype]"]);
490 JSContext *context = [[JSContext alloc] init];
491 context[@"TestObject"] = [TestObject class];
492 JSValue *result = [context evaluateScript:@"String(TestObject)"];
493 checkResult(@"String(TestObject)", [result isEqualToObject:@"[object TestObjectConstructor]"]);
497 JSContext *context = [[JSContext alloc] init];
498 JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context];
499 checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]);
503 JSContext *context = [[JSContext alloc] init];
504 context[@"TestObject"] = [TestObject class];
505 JSValue *result = [context evaluateScript:@"TestObject.parentTest()"];
506 checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]);
510 JSContext *context = [[JSContext alloc] init];
511 TestObject* testObject = [TestObject testObject];
512 context[@"testObjectA"] = testObject;
513 context[@"testObjectB"] = testObject;
514 JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"];
515 checkResult(@"testObjectA == testObjectB", [result isBoolean] && [result toBool]);
519 JSContext *context = [[JSContext alloc] init];
520 TestObject* testObject = [TestObject testObject];
521 context[@"testObject"] = testObject;
522 testObject.point = (CGPoint){3,4};
523 JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"];
524 checkResult(@"testObject.point - result", [result isEqualToObject:@"{\"x\":3,\"y\":4}"]);
525 checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14);
529 JSContext *context = [[JSContext alloc] init];
530 TestObject* testObject = [TestObject testObject];
532 context[@"testObject"] = testObject;
533 context[@"mul"] = ^(int x, int y){ return x * y; };
534 JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"];
535 checkResult(@"mul(testObject.six, 7)", [result isNumber] && [result toInt32] == 42);
539 JSContext *context = [[JSContext alloc] init];
540 TestObject* testObject = [TestObject testObject];
541 context[@"testObject"] = testObject;
542 context[@"testObject"][@"variable"] = @4;
543 [context evaluateScript:@"++testObject.variable"];
544 checkResult(@"++testObject.variable", testObject.variable == 5);
548 JSContext *context = [[JSContext alloc] init];
549 context[@"point"] = @{ @"x":@6, @"y":@7 };
550 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
551 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
555 JSContext *context = [[JSContext alloc] init];
556 context[@"point"] = @{ @"x":@6, @"y":@7 };
557 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
558 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
562 JSContext *context = [[JSContext alloc] init];
563 TestObject* testObject = [TestObject testObject];
564 context[@"testObject"] = testObject;
565 JSValue *result = [context evaluateScript:@"testObject.getString()"];
566 checkResult(@"testObject.getString()", [result isString] && [result toInt32] == 42);
570 JSContext *context = [[JSContext alloc] init];
571 TestObject* testObject = [TestObject testObject];
572 context[@"testObject"] = testObject;
573 JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"];
574 checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]);
578 JSContext *context = [[JSContext alloc] init];
579 TestObject* testObject = [TestObject testObject];
580 context[@"testObject"] = testObject;
581 JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"];
582 checkResult(@"testObject.getString.call(testObject)", [result isString] && [result toInt32] == 42);
586 JSContext *context = [[JSContext alloc] init];
587 TestObject* testObject = [TestObject testObject];
588 context[@"testObject"] = testObject;
589 checkResult(@"testObject.getString.call({}) pre", !context.exception);
590 [context evaluateScript:@"testObject.getString.call({})"];
591 checkResult(@"testObject.getString.call({}) post", context.exception);
595 JSContext *context = [[JSContext alloc] init];
596 TestObject* testObject = [TestObject testObject];
597 context[@"testObject"] = testObject;
598 JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"];
599 checkResult(@"testObject.callback", [result isNumber] && [result toInt32] == 42);
600 result = [context evaluateScript:@"testObject.bogusCallback"];
601 checkResult(@"testObject.bogusCallback == undefined", [result isUndefined]);
605 JSContext *context = [[JSContext alloc] init];
606 TestObject *testObject = [TestObject testObject];
607 context[@"testObject"] = testObject;
608 JSValue *result = [context evaluateScript:@"Function.prototype.toString.call(testObject.callback)"];
609 checkResult(@"Function.prototype.toString", !context.exception && ![result isUndefined]);
613 JSContext *context1 = [[JSContext alloc] init];
614 JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine];
615 JSValue *value = [JSValue valueWithDouble:42 inContext:context2];
616 context1[@"passValueBetweenContexts"] = value;
617 JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"];
618 checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]);
622 JSContext *context = [[JSContext alloc] init];
623 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
624 NSDictionary *expectedDict = @{
625 @"foo" : [NSNumber numberWithInt:1],
627 @"baz": [NSNumber numberWithInt:2]
630 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
632 [context evaluateScript:@"var myDict = { \
636 handleTheDictionary(myDict);"];
638 context[@"handleTheArray"] = ^(NSArray *array) {
639 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
640 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
642 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
646 JSContext *context = [[JSContext alloc] init];
647 TestObject *testObject = [TestObject testObject];
649 context[@"testObject"] = testObject;
650 [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"];
651 [context evaluateScript:@"testObject = undefined"];
654 JSSynchronousGarbageCollectForDebugging([context globalContextRef]);
657 context[@"testObject"] = testObject;
662 JSContext *context = [[JSContext alloc] init];
663 TextXYZ *testXYZ = [[TextXYZ alloc] init];
666 context[@"testXYZ"] = testXYZ;
668 [context evaluateScript:@" \
670 testXYZ.onclick = function() { \
674 testXYZ.weakOnclick = function() { \
682 JSValue *result = [context evaluateScript:@"didClick"];
683 checkResult(@"Event handler onclick", [result toBool]);
686 JSSynchronousGarbageCollectForDebugging([context globalContextRef]);
689 JSValue *result = [context evaluateScript:@"testXYZ.onclick"];
690 checkResult(@"onclick still around after GC", !([result isNull] || [result isUndefined]));
695 JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"];
696 checkResult(@"weakOnclick not around after GC", [result isNull] || [result isUndefined]);
700 [context evaluateScript:@" \
706 JSSynchronousGarbageCollectForDebugging([context globalContextRef]);
710 JSValue *result = [context evaluateScript:@"didClick"];
711 checkResult(@"Event handler onclick doesn't fire", ![result toBool]);
716 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
717 TestObject *testObject = [TestObject testObject];
718 JSManagedValue *weakValue;
720 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
721 context[@"testObject"] = testObject;
722 weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]];
726 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
727 context[@"testObject"] = testObject;
728 JSSynchronousGarbageCollectForDebugging([context globalContextRef]);
729 checkResult(@"weak value == nil", ![weakValue value]);
730 checkResult(@"root is still alive", ![context[@"testObject"] isUndefined]);
735 JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
736 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
737 TinyDOMNode *root = [[TinyDOMNode alloc] init];
738 TinyDOMNode *lastNode = root;
739 for (NSUInteger i = 0; i < 3; i++) {
740 TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
741 [lastNode appendChild:newNode];
746 context[@"root"] = root;
747 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
748 TinyDOMNode *lastNode = nil;
751 head = [lastNode childAtIndex:0];
755 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
758 JSSynchronousGarbageCollectForDebugging([context globalContextRef]);
760 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
761 checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
763 [TinyDOMNode clearSharedVirtualMachine];
767 JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
768 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
769 TinyDOMNode *root = [[TinyDOMNode alloc] init];
770 TinyDOMNode *lastNode = root;
771 for (NSUInteger i = 0; i < 3; i++) {
772 TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
773 [lastNode appendChild:newNode];
778 context[@"root"] = root;
779 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
780 TinyDOMNode *lastNode = nil;
783 head = [lastNode childAtIndex:0];
787 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
789 [root appendChild:[root childAtIndex:0]];
790 [root removeChildAtIndex:0];
793 JSSynchronousGarbageCollectForDebugging([context globalContextRef]);
795 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
796 checkResult(@"duplicate calls to addManagedReference don't cause things to die", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
798 [TinyDOMNode clearSharedVirtualMachine];
804 void testObjectiveCAPI()