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];
137 [value.context.virtualMachine addManagedReference:m_onclickHandler withOwner:self];
139 - (JSValue *)weakOnclick
141 return [m_weakOnclickHandler value];
145 return [m_onclickHandler value];
149 if (!m_onclickHandler)
152 JSValue *function = [m_onclickHandler value];
153 [function callWithArguments:[NSArray array]];
157 [[m_onclickHandler value].context.virtualMachine removeManagedReference:m_onclickHandler withOwner:self];
163 @protocol TinyDOMNode <JSExport>
164 - (void)appendChild:(TinyDOMNode *)child;
165 - (NSUInteger)numberOfChildren;
166 - (TinyDOMNode *)childAtIndex:(NSUInteger)index;
167 - (void)removeChildAtIndex:(NSUInteger)index;
170 @interface TinyDOMNode : NSObject<TinyDOMNode>
171 + (JSVirtualMachine *)sharedVirtualMachine;
172 + (void)clearSharedVirtualMachine;
175 @implementation TinyDOMNode {
176 NSMutableArray *m_children;
179 static JSVirtualMachine *sharedInstance = nil;
181 + (JSVirtualMachine *)sharedVirtualMachine
184 sharedInstance = [[JSVirtualMachine alloc] init];
185 return sharedInstance;
188 + (void)clearSharedVirtualMachine
190 sharedInstance = nil;
199 m_children = [[NSMutableArray alloc] initWithCapacity:0];
206 NSEnumerator *enumerator = [m_children objectEnumerator];
208 while ((nextChild = [enumerator nextObject]))
209 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:nextChild withOwner:self];
211 #if !__has_feature(objc_arc)
216 - (void)appendChild:(TinyDOMNode *)child
218 [[TinyDOMNode sharedVirtualMachine] addManagedReference:child withOwner:self];
219 [m_children addObject:child];
222 - (NSUInteger)numberOfChildren
224 return [m_children count];
227 - (TinyDOMNode *)childAtIndex:(NSUInteger)index
229 if (index >= [m_children count])
231 return [m_children objectAtIndex:index];
234 - (void)removeChildAtIndex:(NSUInteger)index
236 if (index >= [m_children count])
238 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
239 [m_children removeObjectAtIndex:index];
244 static void checkResult(NSString *description, bool passed)
246 NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
251 static bool blockSignatureContainsClass()
253 static bool containsClass = ^{
254 id block = ^(NSString *string){ return string; };
255 return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
257 return containsClass;
260 void testObjectiveCAPI()
262 NSLog(@"Testing Objective-C API");
265 JSContext *context = [[JSContext alloc] init];
266 JSValue *result = [context evaluateScript:@"2 + 2"];
267 checkResult(@"2 + 2", [result isNumber] && [result toInt32] == 4);
271 JSContext *context = [[JSContext alloc] init];
272 NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]];
273 checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]);
277 JSContext *context = [[JSContext alloc] init];
278 context[@"message"] = @"Hello";
279 JSValue *result = [context evaluateScript:@"message + ', World!'"];
280 checkResult(@"Hello, World!", [result isString] && [result isEqualToObject:@"Hello, World!"]);
284 JSContext *context = [[JSContext alloc] init];
285 JSValue *result = [context evaluateScript:@"({ x:42 })"];
286 checkResult(@"({ x:42 })", [result isObject] && [result[@"x"] isEqualToObject:@42]);
287 id obj = [result toObject];
288 checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]);
289 id num = (NSDictionary *)obj[@"x"];
290 checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]);
294 JSContext *context = [[JSContext alloc] init];
296 context[@"blockCallback"] = ^(int value){
299 [context evaluateScript:@"blockCallback(42)"];
300 checkResult(@"blockCallback", result == 42);
303 if (blockSignatureContainsClass()) {
305 JSContext *context = [[JSContext alloc] init];
306 __block bool result = false;
307 context[@"blockCallback"] = ^(NSString *value){
308 result = [@"42" isEqualToString:value] == YES;
310 [context evaluateScript:@"blockCallback(42)"];
311 checkResult(@"blockCallback(NSString *)", result);
314 NSLog(@"Skipping 'blockCallback(NSString *)' test case");
317 JSContext *context = [[JSContext alloc] init];
318 checkResult(@"!context.exception", !context.exception);
319 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
320 checkResult(@"context.exception", context.exception);
324 JSContext *context = [[JSContext alloc] init];
325 __block bool caught = false;
326 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
331 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
332 checkResult(@"JSContext.exceptionHandler", caught);
336 JSContext *context = [[JSContext alloc] init];
337 context[@"callback"] = ^{
338 JSContext *context = [JSContext currentContext];
339 context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context];
341 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
342 checkResult(@"Explicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
343 checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception);
347 JSContext *context = [[JSContext alloc] init];
348 context[@"callback"] = ^{
349 JSContext *context = [JSContext currentContext];
350 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
352 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
353 checkResult(@"Implicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
354 checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception);
358 JSContext *context = [[JSContext alloc] init];
359 [context evaluateScript:
360 @"function sum(array) { \
362 for (var i in array) \
363 result += array[i]; \
366 JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context];
367 JSValue *sumFunction = context[@"sum"];
368 JSValue *result = [sumFunction callWithArguments:@[ array ]];
369 checkResult(@"sum([13, 2, 7])", [result toInt32] == 22);
373 JSContext *context = [[JSContext alloc] init];
374 JSValue *mulAddFunction = [context evaluateScript:
375 @"(function(array, object) { \
377 for (var i in array) \
378 result.push(array[i] * object.x + object.y); \
381 JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]];
382 checkResult(@"mulAddFunction", [result isObject] && [[result toString] isEqual:@"43,44,46"]);
386 JSContext *context = [[JSContext alloc] init];
387 JSValue *array = [JSValue valueWithNewArrayInContext:context];
388 checkResult(@"arrayLengthEmpty", [[array[@"length"] toNumber] unsignedIntegerValue] == 0);
389 JSValue *value1 = [JSValue valueWithInt32:42 inContext:context];
390 JSValue *value2 = [JSValue valueWithInt32:24 inContext:context];
391 NSUInteger lowIndex = 5;
392 NSUInteger maxLength = UINT_MAX;
394 [array setValue:value1 atIndex:lowIndex];
395 checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1));
397 [array setValue:value1 atIndex:(maxLength - 1)];
398 checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
400 [array setValue:value2 atIndex:maxLength];
401 checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
403 [array setValue:value2 atIndex:(maxLength + 1)];
404 checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
406 if (sizeof(NSUInteger) == 8)
407 checkResult(@"valueAtIndex:0 is undefined", [[array valueAtIndex:0] isUndefined]);
409 checkResult(@"valueAtIndex:0", [[array valueAtIndex:0] toInt32] == 24);
410 checkResult(@"valueAtIndex:lowIndex", [[array valueAtIndex:lowIndex] toInt32] == 42);
411 checkResult(@"valueAtIndex:maxLength - 1", [[array valueAtIndex:(maxLength - 1)] toInt32] == 42);
412 checkResult(@"valueAtIndex:maxLength", [[array valueAtIndex:maxLength] toInt32] == 24);
413 checkResult(@"valueAtIndex:maxLength + 1", [[array valueAtIndex:(maxLength + 1)] toInt32] == 24);
417 JSContext *context = [[JSContext alloc] init];
418 JSValue *object = [JSValue valueWithNewObjectInContext:context];
420 object[@"point"] = @{ @"x":@1, @"y":@2 };
421 object[@"point"][@"x"] = @3;
422 CGPoint point = [object[@"point"] toPoint];
423 checkResult(@"toPoint", point.x == 3 && point.y == 2);
425 object[@{ @"toString":^{ return @"foo"; } }] = @"bar";
426 checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
428 object[[@"foobar" substringToIndex:3]] = @"bar";
429 checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
433 JSContext *context = [[JSContext alloc] init];
434 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
435 NSDictionary *expectedDict = @{
436 @"foo" : [NSNumber numberWithInt:1],
438 @"baz": [NSNumber numberWithInt:2]
441 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
443 [context evaluateScript:@"var myDict = { \
447 handleTheDictionary(myDict);"];
449 context[@"handleTheArray"] = ^(NSArray *array) {
450 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
451 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
453 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
457 JSContext *context = [[JSContext alloc] init];
458 TextXYZ *testXYZ = [[TextXYZ alloc] init];
459 context[@"testXYZ"] = testXYZ;
463 [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"];
464 [context evaluateScript:@"testXYZ.test('test')"];
465 checkResult(@"TextXYZ - testXYZTested", testXYZTested);
466 JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"];
467 checkResult(@"TextXYZ - result", [result isEqualToObject:@"13,4,undefined"]);
471 JSContext *context = [[JSContext alloc] init];
472 [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{
473 JSPropertyDescriptorGetKey:^{
474 return [JSContext currentThis][@"x"];
477 JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context];
478 int result = [object [@"getterProperty"] toInt32];
479 checkResult(@"getterProperty", result == 101);
483 JSContext *context = [[JSContext alloc] init];
484 context[@"concatenate"] = ^{
485 NSArray *arguments = [JSContext currentArguments];
486 if (![arguments count])
488 NSString *message = [arguments[0] description];
489 for (NSUInteger index = 1; index < [arguments count]; ++index)
490 message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]];
493 JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"];
494 checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]);
498 JSContext *context = [[JSContext alloc] init];
499 context[@"foo"] = @YES;
500 checkResult(@"@YES is boolean", [context[@"foo"] isBoolean]);
501 JSValue *result = [context evaluateScript:@"typeof foo"];
502 checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]);
506 JSContext *context = [[JSContext alloc] init];
507 TestObject* testObject = [TestObject testObject];
508 context[@"testObject"] = testObject;
509 JSValue *result = [context evaluateScript:@"String(testObject)"];
510 checkResult(@"String(testObject)", [result isEqualToObject:@"[object TestObject]"]);
514 JSContext *context = [[JSContext alloc] init];
515 TestObject* testObject = [TestObject testObject];
516 context[@"testObject"] = testObject;
517 JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"];
518 checkResult(@"String(testObject.__proto__)", [result isEqualToObject:@"[object TestObjectPrototype]"]);
522 JSContext *context = [[JSContext alloc] init];
523 context[@"TestObject"] = [TestObject class];
524 JSValue *result = [context evaluateScript:@"String(TestObject)"];
525 checkResult(@"String(TestObject)", [result isEqualToObject:@"[object TestObjectConstructor]"]);
529 JSContext *context = [[JSContext alloc] init];
530 JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context];
531 checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]);
535 JSContext *context = [[JSContext alloc] init];
536 context[@"TestObject"] = [TestObject class];
537 JSValue *result = [context evaluateScript:@"TestObject.parentTest()"];
538 checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]);
542 JSContext *context = [[JSContext alloc] init];
543 TestObject* testObject = [TestObject testObject];
544 context[@"testObjectA"] = testObject;
545 context[@"testObjectB"] = testObject;
546 JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"];
547 checkResult(@"testObjectA == testObjectB", [result isBoolean] && [result toBool]);
551 JSContext *context = [[JSContext alloc] init];
552 TestObject* testObject = [TestObject testObject];
553 context[@"testObject"] = testObject;
554 testObject.point = (CGPoint){3,4};
555 JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"];
556 checkResult(@"testObject.point - result", [result isEqualToObject:@"{\"x\":3,\"y\":4}"]);
557 checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14);
561 JSContext *context = [[JSContext alloc] init];
562 TestObject* testObject = [TestObject testObject];
564 context[@"testObject"] = testObject;
565 context[@"mul"] = ^(int x, int y){ return x * y; };
566 JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"];
567 checkResult(@"mul(testObject.six, 7)", [result isNumber] && [result toInt32] == 42);
571 JSContext *context = [[JSContext alloc] init];
572 TestObject* testObject = [TestObject testObject];
573 context[@"testObject"] = testObject;
574 context[@"testObject"][@"variable"] = @4;
575 [context evaluateScript:@"++testObject.variable"];
576 checkResult(@"++testObject.variable", testObject.variable == 5);
580 JSContext *context = [[JSContext alloc] init];
581 context[@"point"] = @{ @"x":@6, @"y":@7 };
582 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
583 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
587 JSContext *context = [[JSContext alloc] init];
588 context[@"point"] = @{ @"x":@6, @"y":@7 };
589 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
590 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
594 JSContext *context = [[JSContext alloc] init];
595 TestObject* testObject = [TestObject testObject];
596 context[@"testObject"] = testObject;
597 JSValue *result = [context evaluateScript:@"testObject.getString()"];
598 checkResult(@"testObject.getString()", [result isString] && [result toInt32] == 42);
602 JSContext *context = [[JSContext alloc] init];
603 TestObject* testObject = [TestObject testObject];
604 context[@"testObject"] = testObject;
605 JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"];
606 checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]);
610 JSContext *context = [[JSContext alloc] init];
611 TestObject* testObject = [TestObject testObject];
612 context[@"testObject"] = testObject;
613 JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"];
614 checkResult(@"testObject.getString.call(testObject)", [result isString] && [result toInt32] == 42);
618 JSContext *context = [[JSContext alloc] init];
619 TestObject* testObject = [TestObject testObject];
620 context[@"testObject"] = testObject;
621 checkResult(@"testObject.getString.call({}) pre", !context.exception);
622 [context evaluateScript:@"testObject.getString.call({})"];
623 checkResult(@"testObject.getString.call({}) post", context.exception);
627 JSContext *context = [[JSContext alloc] init];
628 TestObject* testObject = [TestObject testObject];
629 context[@"testObject"] = testObject;
630 JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"];
631 checkResult(@"testObject.callback", [result isNumber] && [result toInt32] == 42);
632 result = [context evaluateScript:@"testObject.bogusCallback"];
633 checkResult(@"testObject.bogusCallback == undefined", [result isUndefined]);
637 JSContext *context = [[JSContext alloc] init];
638 TestObject *testObject = [TestObject testObject];
639 context[@"testObject"] = testObject;
640 JSValue *result = [context evaluateScript:@"Function.prototype.toString.call(testObject.callback)"];
641 checkResult(@"Function.prototype.toString", !context.exception && ![result isUndefined]);
645 JSContext *context1 = [[JSContext alloc] init];
646 JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine];
647 JSValue *value = [JSValue valueWithDouble:42 inContext:context2];
648 context1[@"passValueBetweenContexts"] = value;
649 JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"];
650 checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]);
654 JSContext *context = [[JSContext alloc] init];
655 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
656 NSDictionary *expectedDict = @{
657 @"foo" : [NSNumber numberWithInt:1],
659 @"baz": [NSNumber numberWithInt:2]
662 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
664 [context evaluateScript:@"var myDict = { \
668 handleTheDictionary(myDict);"];
670 context[@"handleTheArray"] = ^(NSArray *array) {
671 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
672 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
674 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
678 JSContext *context = [[JSContext alloc] init];
679 TestObject *testObject = [TestObject testObject];
681 context[@"testObject"] = testObject;
682 [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"];
683 [context evaluateScript:@"testObject = undefined"];
686 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
689 context[@"testObject"] = testObject;
694 JSContext *context = [[JSContext alloc] init];
695 TextXYZ *testXYZ = [[TextXYZ alloc] init];
698 context[@"testXYZ"] = testXYZ;
700 [context evaluateScript:@" \
702 testXYZ.onclick = function() { \
706 testXYZ.weakOnclick = function() { \
714 JSValue *result = [context evaluateScript:@"didClick"];
715 checkResult(@"Event handler onclick", [result toBool]);
718 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
721 JSValue *result = [context evaluateScript:@"testXYZ.onclick"];
722 checkResult(@"onclick still around after GC", !([result isNull] || [result isUndefined]));
727 JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"];
728 checkResult(@"weakOnclick not around after GC", [result isNull] || [result isUndefined]);
732 [context evaluateScript:@" \
738 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
742 JSValue *result = [context evaluateScript:@"didClick"];
743 checkResult(@"Event handler onclick doesn't fire", ![result toBool]);
748 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
749 TestObject *testObject = [TestObject testObject];
750 JSManagedValue *weakValue;
752 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
753 context[@"testObject"] = testObject;
754 weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]];
758 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
759 context[@"testObject"] = testObject;
760 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
761 checkResult(@"weak value == nil", ![weakValue value]);
762 checkResult(@"root is still alive", ![context[@"testObject"] isUndefined]);
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;"];
790 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
792 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
793 checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
795 [TinyDOMNode clearSharedVirtualMachine];
799 JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
800 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
801 TinyDOMNode *root = [[TinyDOMNode alloc] init];
802 TinyDOMNode *lastNode = root;
803 for (NSUInteger i = 0; i < 3; i++) {
804 TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
805 [lastNode appendChild:newNode];
810 context[@"root"] = root;
811 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
812 TinyDOMNode *lastNode = nil;
815 head = [lastNode childAtIndex:0];
819 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
821 [root appendChild:[root childAtIndex:0]];
822 [root removeChildAtIndex:0];
825 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
827 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
828 checkResult(@"duplicate calls to addManagedReference don't cause things to die", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
830 [TinyDOMNode clearSharedVirtualMachine];
834 JSContext *context = [[JSContext alloc] init];
835 JSValue *o = [JSValue valueWithNewObjectInContext:context];
837 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
839 checkResult(@"JSValue correctly protected its internal value", [[o[@"foo"] toString] isEqualToString:@"foo"]);
843 JSContext *context = [[JSContext alloc] init];
844 TestObject *testObject = [TestObject testObject];
845 context[@"testObject"] = testObject;
846 [context evaluateScript:@"testObject.__lookupGetter__('variable').call({})"];
847 checkResult(@"Make sure we throw an exception when calling getter on incorrect |this|", context.exception);
851 TestObject *testObject = [TestObject testObject];
852 JSManagedValue *managedTestObject;
854 JSContext *context = [[JSContext alloc] init];
855 context[@"testObject"] = testObject;
856 managedTestObject = [JSManagedValue managedValueWithValue:context[@"testObject"]];
857 [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject];
864 void testObjectiveCAPI()