]> git.saurik.com Git - apple/javascriptcore.git/blob - API/tests/testapi.mm
0ab82cd2808707f5551e47545d84b5d6ab54926d
[apple/javascriptcore.git] / API / tests / testapi.mm
1 /*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26 #import <JavaScriptCore/JavaScriptCore.h>
27
28 extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
29
30 extern "C" bool _Block_has_signature(id);
31 extern "C" const char * _Block_signature(id);
32
33 extern int failed;
34 extern "C" void testObjectiveCAPI(void);
35
36 #if JSC_OBJC_API_ENABLED
37
38 @protocol ParentObject <JSExport>
39 @end
40
41 @interface ParentObject : NSObject<ParentObject>
42 + (NSString *)parentTest;
43 @end
44
45 @implementation ParentObject
46 + (NSString *)parentTest
47 {
48 return [self description];
49 }
50 @end
51
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
61 );
62 - (void)callback:(JSValue *)function;
63 - (void)bogusCallback:(void(^)(int))function;
64 @end
65
66 @interface TestObject : ParentObject <TestObject>
67 @property int six;
68 + (id)testObject;
69 @end
70
71 @implementation TestObject
72 @synthesize variable;
73 @synthesize six;
74 @synthesize point;
75 + (id)testObject
76 {
77 return [[TestObject alloc] init];
78 }
79 + (NSString *)classTest
80 {
81 return @"classTest - okay";
82 }
83 - (NSString *)getString
84 {
85 return @"42";
86 }
87 - (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
88 {
89 return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]];
90 }
91 - (void)callback:(JSValue *)function
92 {
93 [function callWithArguments:[NSArray arrayWithObject:[NSNumber numberWithInt:42]]];
94 }
95 - (void)bogusCallback:(void(^)(int))function
96 {
97 function(42);
98 }
99 @end
100
101 bool testXYZTested = false;
102
103 @protocol TextXYZ <JSExport>
104 @property int x;
105 @property (readonly) int y;
106 @property (assign) JSValue *onclick;
107 @property (assign) JSValue *weakOnclick;
108 - (void)test:(NSString *)message;
109 @end
110
111 @interface TextXYZ : NSObject <TextXYZ>
112 @property int x;
113 @property int y;
114 @property int z;
115 - (void)click;
116 @end
117
118 @implementation TextXYZ {
119 JSManagedValue *m_weakOnclickHandler;
120 JSManagedValue *m_onclickHandler;
121 }
122 @synthesize x;
123 @synthesize y;
124 @synthesize z;
125 - (void)test:(NSString *)message
126 {
127 testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5;
128 }
129 - (void)setWeakOnclick:(JSValue *)value
130 {
131 m_weakOnclickHandler = [JSManagedValue managedValueWithValue:value];
132 }
133
134 - (void)setOnclick:(JSValue *)value
135 {
136 m_onclickHandler = [JSManagedValue managedValueWithValue:value];
137 [value.context.virtualMachine addManagedReference:m_onclickHandler withOwner:self];
138 }
139 - (JSValue *)weakOnclick
140 {
141 return [m_weakOnclickHandler value];
142 }
143 - (JSValue *)onclick
144 {
145 return [m_onclickHandler value];
146 }
147 - (void)click
148 {
149 if (!m_onclickHandler)
150 return;
151
152 JSValue *function = [m_onclickHandler value];
153 [function callWithArguments:[NSArray array]];
154 }
155 - (void)dealloc
156 {
157 [[m_onclickHandler value].context.virtualMachine removeManagedReference:m_onclickHandler withOwner:self];
158 }
159 @end
160
161 @class TinyDOMNode;
162
163 @protocol TinyDOMNode <JSExport>
164 - (void)appendChild:(TinyDOMNode *)child;
165 - (NSUInteger)numberOfChildren;
166 - (TinyDOMNode *)childAtIndex:(NSUInteger)index;
167 - (void)removeChildAtIndex:(NSUInteger)index;
168 @end
169
170 @interface TinyDOMNode : NSObject<TinyDOMNode>
171 + (JSVirtualMachine *)sharedVirtualMachine;
172 + (void)clearSharedVirtualMachine;
173 @end
174
175 @implementation TinyDOMNode {
176 NSMutableArray *m_children;
177 }
178
179 static JSVirtualMachine *sharedInstance = nil;
180
181 + (JSVirtualMachine *)sharedVirtualMachine
182 {
183 if (!sharedInstance)
184 sharedInstance = [[JSVirtualMachine alloc] init];
185 return sharedInstance;
186 }
187
188 + (void)clearSharedVirtualMachine
189 {
190 sharedInstance = nil;
191 }
192
193 - (id)init
194 {
195 self = [super init];
196 if (!self)
197 return nil;
198
199 m_children = [[NSMutableArray alloc] initWithCapacity:0];
200
201 return self;
202 }
203
204 - (void)dealloc
205 {
206 NSEnumerator *enumerator = [m_children objectEnumerator];
207 id nextChild;
208 while ((nextChild = [enumerator nextObject]))
209 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:nextChild withOwner:self];
210
211 #if !__has_feature(objc_arc)
212 [super dealloc];
213 #endif
214 }
215
216 - (void)appendChild:(TinyDOMNode *)child
217 {
218 [[TinyDOMNode sharedVirtualMachine] addManagedReference:child withOwner:self];
219 [m_children addObject:child];
220 }
221
222 - (NSUInteger)numberOfChildren
223 {
224 return [m_children count];
225 }
226
227 - (TinyDOMNode *)childAtIndex:(NSUInteger)index
228 {
229 if (index >= [m_children count])
230 return nil;
231 return [m_children objectAtIndex:index];
232 }
233
234 - (void)removeChildAtIndex:(NSUInteger)index
235 {
236 if (index >= [m_children count])
237 return;
238 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
239 [m_children removeObjectAtIndex:index];
240 }
241
242 @end
243
244 static void checkResult(NSString *description, bool passed)
245 {
246 NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
247 if (!passed)
248 failed = 1;
249 }
250
251 static bool blockSignatureContainsClass()
252 {
253 static bool containsClass = ^{
254 id block = ^(NSString *string){ return string; };
255 return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
256 }();
257 return containsClass;
258 }
259
260 void testObjectiveCAPI()
261 {
262 NSLog(@"Testing Objective-C API");
263
264 @autoreleasepool {
265 JSContext *context = [[JSContext alloc] init];
266 JSValue *result = [context evaluateScript:@"2 + 2"];
267 checkResult(@"2 + 2", [result isNumber] && [result toInt32] == 4);
268 }
269
270 @autoreleasepool {
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"]);
274 }
275
276 @autoreleasepool {
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!"]);
281 }
282
283 @autoreleasepool {
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]]);
291 }
292
293 @autoreleasepool {
294 JSContext *context = [[JSContext alloc] init];
295 __block int result;
296 context[@"blockCallback"] = ^(int value){
297 result = value;
298 };
299 [context evaluateScript:@"blockCallback(42)"];
300 checkResult(@"blockCallback", result == 42);
301 }
302
303 if (blockSignatureContainsClass()) {
304 @autoreleasepool {
305 JSContext *context = [[JSContext alloc] init];
306 __block bool result = false;
307 context[@"blockCallback"] = ^(NSString *value){
308 result = [@"42" isEqualToString:value] == YES;
309 };
310 [context evaluateScript:@"blockCallback(42)"];
311 checkResult(@"blockCallback(NSString *)", result);
312 }
313 } else
314 NSLog(@"Skipping 'blockCallback(NSString *)' test case");
315
316 @autoreleasepool {
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);
321 }
322
323 @autoreleasepool {
324 JSContext *context = [[JSContext alloc] init];
325 __block bool caught = false;
326 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
327 (void)context;
328 (void)exception;
329 caught = true;
330 };
331 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
332 checkResult(@"JSContext.exceptionHandler", caught);
333 }
334
335 @autoreleasepool {
336 JSContext *context = [[JSContext alloc] init];
337 context[@"callback"] = ^{
338 JSContext *context = [JSContext currentContext];
339 context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context];
340 };
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);
344 }
345
346 @autoreleasepool {
347 JSContext *context = [[JSContext alloc] init];
348 context[@"callback"] = ^{
349 JSContext *context = [JSContext currentContext];
350 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
351 };
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);
355 }
356
357 @autoreleasepool {
358 JSContext *context = [[JSContext alloc] init];
359 [context evaluateScript:
360 @"function sum(array) { \
361 var result = 0; \
362 for (var i in array) \
363 result += array[i]; \
364 return result; \
365 }"];
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);
370 }
371
372 @autoreleasepool {
373 JSContext *context = [[JSContext alloc] init];
374 JSValue *mulAddFunction = [context evaluateScript:
375 @"(function(array, object) { \
376 var result = []; \
377 for (var i in array) \
378 result.push(array[i] * object.x + object.y); \
379 return result; \
380 })"];
381 JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]];
382 checkResult(@"mulAddFunction", [result isObject] && [[result toString] isEqual:@"43,44,46"]);
383 }
384
385 @autoreleasepool {
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;
393
394 [array setValue:value1 atIndex:lowIndex];
395 checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1));
396
397 [array setValue:value1 atIndex:(maxLength - 1)];
398 checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
399
400 [array setValue:value2 atIndex:maxLength];
401 checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
402
403 [array setValue:value2 atIndex:(maxLength + 1)];
404 checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
405
406 if (sizeof(NSUInteger) == 8)
407 checkResult(@"valueAtIndex:0 is undefined", [[array valueAtIndex:0] isUndefined]);
408 else
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);
414 }
415
416 @autoreleasepool {
417 JSContext *context = [[JSContext alloc] init];
418 JSValue *object = [JSValue valueWithNewObjectInContext:context];
419
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);
424
425 object[@{ @"toString":^{ return @"foo"; } }] = @"bar";
426 checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
427
428 object[[@"foobar" substringToIndex:3]] = @"bar";
429 checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
430 }
431
432 @autoreleasepool {
433 JSContext *context = [[JSContext alloc] init];
434 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
435 NSDictionary *expectedDict = @{
436 @"foo" : [NSNumber numberWithInt:1],
437 @"bar" : @{
438 @"baz": [NSNumber numberWithInt:2]
439 }
440 };
441 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
442 };
443 [context evaluateScript:@"var myDict = { \
444 'foo': 1, \
445 'bar': {'baz': 2} \
446 }; \
447 handleTheDictionary(myDict);"];
448
449 context[@"handleTheArray"] = ^(NSArray *array) {
450 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
451 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
452 };
453 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
454 }
455
456 @autoreleasepool {
457 JSContext *context = [[JSContext alloc] init];
458 TextXYZ *testXYZ = [[TextXYZ alloc] init];
459 context[@"testXYZ"] = testXYZ;
460 testXYZ.x = 3;
461 testXYZ.y = 4;
462 testXYZ.z = 5;
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"]);
468 }
469
470 @autoreleasepool {
471 JSContext *context = [[JSContext alloc] init];
472 [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{
473 JSPropertyDescriptorGetKey:^{
474 return [JSContext currentThis][@"x"];
475 }
476 }];
477 JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context];
478 int result = [object [@"getterProperty"] toInt32];
479 checkResult(@"getterProperty", result == 101);
480 }
481
482 @autoreleasepool {
483 JSContext *context = [[JSContext alloc] init];
484 context[@"concatenate"] = ^{
485 NSArray *arguments = [JSContext currentArguments];
486 if (![arguments count])
487 return @"";
488 NSString *message = [arguments[0] description];
489 for (NSUInteger index = 1; index < [arguments count]; ++index)
490 message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]];
491 return message;
492 };
493 JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"];
494 checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]);
495 }
496
497 @autoreleasepool {
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"]);
503 }
504
505 @autoreleasepool {
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]"]);
511 }
512
513 @autoreleasepool {
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]"]);
519 }
520
521 @autoreleasepool {
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]"]);
526 }
527
528 @autoreleasepool {
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]);
532 }
533
534 @autoreleasepool {
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"]);
539 }
540
541 @autoreleasepool {
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]);
548 }
549
550 @autoreleasepool {
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);
558 }
559
560 @autoreleasepool {
561 JSContext *context = [[JSContext alloc] init];
562 TestObject* testObject = [TestObject testObject];
563 testObject.six = 6;
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);
568 }
569
570 @autoreleasepool {
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);
577 }
578
579 @autoreleasepool {
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"]);
584 }
585
586 @autoreleasepool {
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"]);
591 }
592
593 @autoreleasepool {
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);
599 }
600
601 @autoreleasepool {
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"]);
607 }
608
609 @autoreleasepool {
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);
615 }
616
617 @autoreleasepool {
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);
624 }
625
626 @autoreleasepool {
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]);
634 }
635
636 @autoreleasepool {
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]);
642 }
643
644 @autoreleasepool {
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]);
651 }
652
653 @autoreleasepool {
654 JSContext *context = [[JSContext alloc] init];
655 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
656 NSDictionary *expectedDict = @{
657 @"foo" : [NSNumber numberWithInt:1],
658 @"bar" : @{
659 @"baz": [NSNumber numberWithInt:2]
660 }
661 };
662 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
663 };
664 [context evaluateScript:@"var myDict = { \
665 'foo': 1, \
666 'bar': {'baz': 2} \
667 }; \
668 handleTheDictionary(myDict);"];
669
670 context[@"handleTheArray"] = ^(NSArray *array) {
671 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
672 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
673 };
674 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
675 }
676
677 @autoreleasepool {
678 JSContext *context = [[JSContext alloc] init];
679 TestObject *testObject = [TestObject testObject];
680 @autoreleasepool {
681 context[@"testObject"] = testObject;
682 [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"];
683 [context evaluateScript:@"testObject = undefined"];
684 }
685
686 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
687
688 @autoreleasepool {
689 context[@"testObject"] = testObject;
690 }
691 }
692
693 @autoreleasepool {
694 JSContext *context = [[JSContext alloc] init];
695 TextXYZ *testXYZ = [[TextXYZ alloc] init];
696
697 @autoreleasepool {
698 context[@"testXYZ"] = testXYZ;
699
700 [context evaluateScript:@" \
701 didClick = false; \
702 testXYZ.onclick = function() { \
703 didClick = true; \
704 }; \
705 \
706 testXYZ.weakOnclick = function() { \
707 return 'foo'; \
708 }; \
709 "];
710 }
711
712 @autoreleasepool {
713 [testXYZ click];
714 JSValue *result = [context evaluateScript:@"didClick"];
715 checkResult(@"Event handler onclick", [result toBool]);
716 }
717
718 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
719
720 @autoreleasepool {
721 JSValue *result = [context evaluateScript:@"testXYZ.onclick"];
722 checkResult(@"onclick still around after GC", !([result isNull] || [result isUndefined]));
723 }
724
725
726 @autoreleasepool {
727 JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"];
728 checkResult(@"weakOnclick not around after GC", [result isNull] || [result isUndefined]);
729 }
730
731 @autoreleasepool {
732 [context evaluateScript:@" \
733 didClick = false; \
734 testXYZ = null; \
735 "];
736 }
737
738 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
739
740 @autoreleasepool {
741 [testXYZ click];
742 JSValue *result = [context evaluateScript:@"didClick"];
743 checkResult(@"Event handler onclick doesn't fire", ![result toBool]);
744 }
745 }
746
747 @autoreleasepool {
748 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
749 TestObject *testObject = [TestObject testObject];
750 JSManagedValue *weakValue;
751 @autoreleasepool {
752 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
753 context[@"testObject"] = testObject;
754 weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]];
755 }
756
757 @autoreleasepool {
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]);
763 }
764 }
765
766 @autoreleasepool {
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];
774 lastNode = newNode;
775 }
776
777 @autoreleasepool {
778 context[@"root"] = root;
779 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
780 TinyDOMNode *lastNode = nil;
781 while (head) {
782 lastNode = head;
783 head = [lastNode childAtIndex:0];
784 }
785 return lastNode;
786 };
787 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
788 }
789
790 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
791
792 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
793 checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
794
795 [TinyDOMNode clearSharedVirtualMachine];
796 }
797
798 @autoreleasepool {
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];
806 lastNode = newNode;
807 }
808
809 @autoreleasepool {
810 context[@"root"] = root;
811 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
812 TinyDOMNode *lastNode = nil;
813 while (head) {
814 lastNode = head;
815 head = [lastNode childAtIndex:0];
816 }
817 return lastNode;
818 };
819 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
820
821 [root appendChild:[root childAtIndex:0]];
822 [root removeChildAtIndex:0];
823 }
824
825 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
826
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);
829
830 [TinyDOMNode clearSharedVirtualMachine];
831 }
832
833 @autoreleasepool {
834 JSContext *context = [[JSContext alloc] init];
835 JSValue *o = [JSValue valueWithNewObjectInContext:context];
836 o[@"foo"] = @"foo";
837 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
838
839 checkResult(@"JSValue correctly protected its internal value", [[o[@"foo"] toString] isEqualToString:@"foo"]);
840 }
841
842 @autoreleasepool {
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);
848 }
849
850 @autoreleasepool {
851 TestObject *testObject = [TestObject testObject];
852 JSManagedValue *managedTestObject;
853 @autoreleasepool {
854 JSContext *context = [[JSContext alloc] init];
855 context[@"testObject"] = testObject;
856 managedTestObject = [JSManagedValue managedValueWithValue:context[@"testObject"]];
857 [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject];
858 }
859 }
860 }
861
862 #else
863
864 void testObjectiveCAPI()
865 {
866 }
867
868 #endif