]> git.saurik.com Git - apple/javascriptcore.git/blame - API/tests/testapi.mm
JavaScriptCore-1218.35.tar.gz
[apple/javascriptcore.git] / API / tests / testapi.mm
CommitLineData
93a37866
A
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
28extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
29
30extern "C" bool _Block_has_signature(id);
31extern "C" const char * _Block_signature(id);
32
33extern int failed;
34extern "C" void testObjectiveCAPI(void);
35
36#if JSC_OBJC_API_ENABLED
37
12899fa2
A
38@interface UnexportedObject : NSObject
39@end
40
41@implementation UnexportedObject
42@end
43
93a37866
A
44@protocol ParentObject <JSExport>
45@end
46
47@interface ParentObject : NSObject<ParentObject>
48+ (NSString *)parentTest;
49@end
50
51@implementation ParentObject
52+ (NSString *)parentTest
53{
54 return [self description];
55}
56@end
57
58@protocol TestObject <JSExport>
12899fa2 59- (id)init;
93a37866
A
60@property int variable;
61@property (readonly) int six;
62@property CGPoint point;
63+ (NSString *)classTest;
64+ (NSString *)parentTest;
65- (NSString *)getString;
66JSExportAs(testArgumentTypes,
67- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
68);
69- (void)callback:(JSValue *)function;
70- (void)bogusCallback:(void(^)(int))function;
71@end
72
73@interface TestObject : ParentObject <TestObject>
74@property int six;
75+ (id)testObject;
76@end
77
78@implementation TestObject
79@synthesize variable;
80@synthesize six;
81@synthesize point;
82+ (id)testObject
83{
84 return [[TestObject alloc] init];
85}
86+ (NSString *)classTest
87{
88 return @"classTest - okay";
89}
90- (NSString *)getString
91{
92 return @"42";
93}
94- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
95{
96 return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]];
97}
98- (void)callback:(JSValue *)function
99{
100 [function callWithArguments:[NSArray arrayWithObject:[NSNumber numberWithInt:42]]];
101}
102- (void)bogusCallback:(void(^)(int))function
103{
104 function(42);
105}
106@end
107
108bool testXYZTested = false;
109
110@protocol TextXYZ <JSExport>
12899fa2 111- (id)initWithString:(NSString*)string;
93a37866
A
112@property int x;
113@property (readonly) int y;
114@property (assign) JSValue *onclick;
115@property (assign) JSValue *weakOnclick;
116- (void)test:(NSString *)message;
117@end
118
119@interface TextXYZ : NSObject <TextXYZ>
120@property int x;
121@property int y;
122@property int z;
123- (void)click;
124@end
125
126@implementation TextXYZ {
127 JSManagedValue *m_weakOnclickHandler;
128 JSManagedValue *m_onclickHandler;
129}
130@synthesize x;
131@synthesize y;
132@synthesize z;
12899fa2
A
133- (id)initWithString:(NSString*)string
134{
135 self = [super init];
136 if (!self)
137 return nil;
138
139 NSLog(@"%@", string);
140
141 return self;
142}
93a37866
A
143- (void)test:(NSString *)message
144{
145 testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5;
146}
147- (void)setWeakOnclick:(JSValue *)value
148{
149 m_weakOnclickHandler = [JSManagedValue managedValueWithValue:value];
150}
151
152- (void)setOnclick:(JSValue *)value
153{
154 m_onclickHandler = [JSManagedValue managedValueWithValue:value];
155 [value.context.virtualMachine addManagedReference:m_onclickHandler withOwner:self];
156}
157- (JSValue *)weakOnclick
158{
159 return [m_weakOnclickHandler value];
160}
161- (JSValue *)onclick
162{
163 return [m_onclickHandler value];
164}
165- (void)click
166{
167 if (!m_onclickHandler)
168 return;
169
170 JSValue *function = [m_onclickHandler value];
171 [function callWithArguments:[NSArray array]];
172}
173- (void)dealloc
174{
175 [[m_onclickHandler value].context.virtualMachine removeManagedReference:m_onclickHandler withOwner:self];
176}
177@end
178
179@class TinyDOMNode;
180
181@protocol TinyDOMNode <JSExport>
182- (void)appendChild:(TinyDOMNode *)child;
183- (NSUInteger)numberOfChildren;
184- (TinyDOMNode *)childAtIndex:(NSUInteger)index;
185- (void)removeChildAtIndex:(NSUInteger)index;
186@end
187
188@interface TinyDOMNode : NSObject<TinyDOMNode>
189+ (JSVirtualMachine *)sharedVirtualMachine;
190+ (void)clearSharedVirtualMachine;
191@end
192
193@implementation TinyDOMNode {
194 NSMutableArray *m_children;
195}
196
197static JSVirtualMachine *sharedInstance = nil;
198
199+ (JSVirtualMachine *)sharedVirtualMachine
200{
201 if (!sharedInstance)
202 sharedInstance = [[JSVirtualMachine alloc] init];
203 return sharedInstance;
204}
205
206+ (void)clearSharedVirtualMachine
207{
208 sharedInstance = nil;
209}
210
211- (id)init
212{
213 self = [super init];
214 if (!self)
215 return nil;
216
217 m_children = [[NSMutableArray alloc] initWithCapacity:0];
218
219 return self;
220}
221
222- (void)dealloc
223{
224 NSEnumerator *enumerator = [m_children objectEnumerator];
225 id nextChild;
226 while ((nextChild = [enumerator nextObject]))
227 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:nextChild withOwner:self];
228
229#if !__has_feature(objc_arc)
230 [super dealloc];
231#endif
232}
233
234- (void)appendChild:(TinyDOMNode *)child
235{
236 [[TinyDOMNode sharedVirtualMachine] addManagedReference:child withOwner:self];
237 [m_children addObject:child];
238}
239
240- (NSUInteger)numberOfChildren
241{
242 return [m_children count];
243}
244
245- (TinyDOMNode *)childAtIndex:(NSUInteger)index
246{
247 if (index >= [m_children count])
248 return nil;
249 return [m_children objectAtIndex:index];
250}
251
252- (void)removeChildAtIndex:(NSUInteger)index
253{
254 if (index >= [m_children count])
255 return;
256 [[TinyDOMNode sharedVirtualMachine] removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
257 [m_children removeObjectAtIndex:index];
258}
259
260@end
261
12899fa2
A
262@interface JSCollection : NSObject
263- (void)setValue:(JSValue *)value forKey:(NSString *)key;
264- (JSValue *)valueForKey:(NSString *)key;
265@end
266
267@implementation JSCollection {
268 NSMutableDictionary *_dict;
269}
270- (id)init
271{
272 self = [super init];
273 if (!self)
274 return nil;
275
276 _dict = [[NSMutableDictionary alloc] init];
277
278 return self;
279}
280
281- (void)setValue:(JSValue *)value forKey:(NSString *)key
282{
283 JSManagedValue *oldManagedValue = [_dict objectForKey:key];
284 if (oldManagedValue) {
285 JSValue* oldValue = [oldManagedValue value];
286 if (oldValue)
287 [oldValue.context.virtualMachine removeManagedReference:oldManagedValue withOwner:self];
288 }
289 JSManagedValue *managedValue = [JSManagedValue managedValueWithValue:value];
290 [value.context.virtualMachine addManagedReference:managedValue withOwner:self];
291 [_dict setObject:managedValue forKey:key];
292}
293
294- (JSValue *)valueForKey:(NSString *)key
295{
296 JSManagedValue *managedValue = [_dict objectForKey:key];
297 if (!managedValue)
298 return nil;
299 return [managedValue value];
300}
301@end
302
303@protocol InitA <JSExport>
304- (id)initWithA:(int)a;
305@end
306
307@protocol InitB <JSExport>
308- (id)initWithA:(int)a b:(int)b;
309@end
310
311@interface ClassA : NSObject<InitA>
312@end
313
314@interface ClassB : ClassA<InitB>
315@end
316
317@interface ClassC : ClassB<InitA, InitB>
318@end
319
320@interface ClassD : NSObject<InitA>
321- (id)initWithA:(int)a;
322@end
323
324@interface ClassE : ClassD
325- (id)initWithA:(int)a;
326@end
327
328@implementation ClassA {
329 int _a;
330}
331- (id)initWithA:(int)a
332{
333 self = [super init];
334 if (!self)
335 return nil;
336
337 _a = a;
338
339 return self;
340}
341@end
342
343@implementation ClassB {
344 int _b;
345}
346- (id)initWithA:(int)a b:(int)b
347{
348 self = [super initWithA:a];
349 if (!self)
350 return nil;
351
352 _b = b;
353
354 return self;
355}
356@end
357
358@implementation ClassC {
359 int _c;
360}
361- (id)initWithA:(int)a
362{
363 return [self initWithA:a b:0];
364}
365- (id)initWithA:(int)a b:(int)b
366{
367 self = [super initWithA:a b:b];
368 if (!self)
369 return nil;
370
371 _c = a + b;
372
373 return self;
374}
375@end
376
377@implementation ClassD
378
379- (id)initWithA:(int)a
380{
381 self = nil;
382 return [[ClassE alloc] initWithA:a];
383}
384@end
385
386@implementation ClassE {
387 int _a;
388}
389
390- (id)initWithA:(int)a
391{
392 self = [super init];
393 if (!self)
394 return nil;
395
396 _a = a;
397
398 return self;
399}
400@end
93a37866
A
401static void checkResult(NSString *description, bool passed)
402{
403 NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
404 if (!passed)
405 failed = 1;
406}
407
408static bool blockSignatureContainsClass()
409{
410 static bool containsClass = ^{
411 id block = ^(NSString *string){ return string; };
412 return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
413 }();
414 return containsClass;
415}
416
417void testObjectiveCAPI()
418{
419 NSLog(@"Testing Objective-C API");
420
421 @autoreleasepool {
422 JSContext *context = [[JSContext alloc] init];
423 JSValue *result = [context evaluateScript:@"2 + 2"];
424 checkResult(@"2 + 2", [result isNumber] && [result toInt32] == 4);
425 }
426
427 @autoreleasepool {
428 JSContext *context = [[JSContext alloc] init];
429 NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]];
430 checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]);
431 }
432
433 @autoreleasepool {
434 JSContext *context = [[JSContext alloc] init];
435 context[@"message"] = @"Hello";
436 JSValue *result = [context evaluateScript:@"message + ', World!'"];
437 checkResult(@"Hello, World!", [result isString] && [result isEqualToObject:@"Hello, World!"]);
438 }
439
440 @autoreleasepool {
441 JSContext *context = [[JSContext alloc] init];
442 JSValue *result = [context evaluateScript:@"({ x:42 })"];
443 checkResult(@"({ x:42 })", [result isObject] && [result[@"x"] isEqualToObject:@42]);
444 id obj = [result toObject];
445 checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]);
446 id num = (NSDictionary *)obj[@"x"];
447 checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]);
448 }
449
12899fa2
A
450 @autoreleasepool {
451 JSCollection* myPrivateProperties = [[JSCollection alloc] init];
452
453 @autoreleasepool {
454 JSContext* context = [[JSContext alloc] init];
455 TestObject* rootObject = [TestObject testObject];
456 context[@"root"] = rootObject;
457 [context.virtualMachine addManagedReference:myPrivateProperties withOwner:rootObject];
458 [myPrivateProperties setValue:[JSValue valueWithBool:true inContext:context] forKey:@"is_ham"];
459 [myPrivateProperties setValue:[JSValue valueWithObject:@"hello!" inContext:context] forKey:@"message"];
460 [myPrivateProperties setValue:[JSValue valueWithInt32:42 inContext:context] forKey:@"my_number"];
461 [myPrivateProperties setValue:[JSValue valueWithNullInContext:context] forKey:@"definitely_null"];
462 [myPrivateProperties setValue:[JSValue valueWithUndefinedInContext:context] forKey:@"not_sure_if_undefined"];
463
464 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
465
466 JSValue *isHam = [myPrivateProperties valueForKey:@"is_ham"];
467 JSValue *message = [myPrivateProperties valueForKey:@"message"];
468 JSValue *myNumber = [myPrivateProperties valueForKey:@"my_number"];
469 JSValue *definitelyNull = [myPrivateProperties valueForKey:@"definitely_null"];
470 JSValue *notSureIfUndefined = [myPrivateProperties valueForKey:@"not_sure_if_undefined"];
471 checkResult(@"is_ham is true", [isHam isBoolean] && [isHam toBool]);
472 checkResult(@"message is hello!", [message isString] && [@"hello!" isEqualToString:[message toString]]);
473 checkResult(@"my_number is 42", [myNumber isNumber] && [myNumber toInt32] == 42);
474 checkResult(@"definitely_null is null", [definitelyNull isNull]);
475 checkResult(@"not_sure_if_undefined is undefined", [notSureIfUndefined isUndefined]);
476 }
477
478 checkResult(@"is_ham is nil", ![myPrivateProperties valueForKey:@"is_ham"]);
479 checkResult(@"message is nil", ![myPrivateProperties valueForKey:@"message"]);
480 checkResult(@"my_number is 42", ![myPrivateProperties valueForKey:@"my_number"]);
481 checkResult(@"definitely_null is null", ![myPrivateProperties valueForKey:@"definitely_null"]);
482 checkResult(@"not_sure_if_undefined is undefined", ![myPrivateProperties valueForKey:@"not_sure_if_undefined"]);
483 }
484
93a37866
A
485 @autoreleasepool {
486 JSContext *context = [[JSContext alloc] init];
487 __block int result;
488 context[@"blockCallback"] = ^(int value){
489 result = value;
490 };
491 [context evaluateScript:@"blockCallback(42)"];
492 checkResult(@"blockCallback", result == 42);
493 }
494
495 if (blockSignatureContainsClass()) {
496 @autoreleasepool {
497 JSContext *context = [[JSContext alloc] init];
498 __block bool result = false;
499 context[@"blockCallback"] = ^(NSString *value){
500 result = [@"42" isEqualToString:value] == YES;
501 };
502 [context evaluateScript:@"blockCallback(42)"];
503 checkResult(@"blockCallback(NSString *)", result);
504 }
505 } else
506 NSLog(@"Skipping 'blockCallback(NSString *)' test case");
507
508 @autoreleasepool {
509 JSContext *context = [[JSContext alloc] init];
510 checkResult(@"!context.exception", !context.exception);
511 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
512 checkResult(@"context.exception", context.exception);
513 }
514
515 @autoreleasepool {
516 JSContext *context = [[JSContext alloc] init];
517 __block bool caught = false;
518 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
519 (void)context;
520 (void)exception;
521 caught = true;
522 };
523 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
524 checkResult(@"JSContext.exceptionHandler", caught);
525 }
526
527 @autoreleasepool {
528 JSContext *context = [[JSContext alloc] init];
529 context[@"callback"] = ^{
530 JSContext *context = [JSContext currentContext];
531 context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context];
532 };
533 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
534 checkResult(@"Explicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
535 checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception);
536 }
537
538 @autoreleasepool {
539 JSContext *context = [[JSContext alloc] init];
540 context[@"callback"] = ^{
541 JSContext *context = [JSContext currentContext];
542 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
543 };
544 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
545 checkResult(@"Implicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
546 checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception);
547 }
548
549 @autoreleasepool {
550 JSContext *context = [[JSContext alloc] init];
551 [context evaluateScript:
552 @"function sum(array) { \
553 var result = 0; \
554 for (var i in array) \
555 result += array[i]; \
556 return result; \
557 }"];
558 JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context];
559 JSValue *sumFunction = context[@"sum"];
560 JSValue *result = [sumFunction callWithArguments:@[ array ]];
561 checkResult(@"sum([13, 2, 7])", [result toInt32] == 22);
562 }
563
564 @autoreleasepool {
565 JSContext *context = [[JSContext alloc] init];
566 JSValue *mulAddFunction = [context evaluateScript:
567 @"(function(array, object) { \
568 var result = []; \
569 for (var i in array) \
570 result.push(array[i] * object.x + object.y); \
571 return result; \
572 })"];
573 JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]];
574 checkResult(@"mulAddFunction", [result isObject] && [[result toString] isEqual:@"43,44,46"]);
575 }
576
577 @autoreleasepool {
578 JSContext *context = [[JSContext alloc] init];
579 JSValue *array = [JSValue valueWithNewArrayInContext:context];
580 checkResult(@"arrayLengthEmpty", [[array[@"length"] toNumber] unsignedIntegerValue] == 0);
581 JSValue *value1 = [JSValue valueWithInt32:42 inContext:context];
582 JSValue *value2 = [JSValue valueWithInt32:24 inContext:context];
583 NSUInteger lowIndex = 5;
584 NSUInteger maxLength = UINT_MAX;
585
586 [array setValue:value1 atIndex:lowIndex];
587 checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1));
588
589 [array setValue:value1 atIndex:(maxLength - 1)];
590 checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
591
592 [array setValue:value2 atIndex:maxLength];
593 checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
594
595 [array setValue:value2 atIndex:(maxLength + 1)];
596 checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
597
598 if (sizeof(NSUInteger) == 8)
599 checkResult(@"valueAtIndex:0 is undefined", [[array valueAtIndex:0] isUndefined]);
600 else
601 checkResult(@"valueAtIndex:0", [[array valueAtIndex:0] toInt32] == 24);
602 checkResult(@"valueAtIndex:lowIndex", [[array valueAtIndex:lowIndex] toInt32] == 42);
603 checkResult(@"valueAtIndex:maxLength - 1", [[array valueAtIndex:(maxLength - 1)] toInt32] == 42);
604 checkResult(@"valueAtIndex:maxLength", [[array valueAtIndex:maxLength] toInt32] == 24);
605 checkResult(@"valueAtIndex:maxLength + 1", [[array valueAtIndex:(maxLength + 1)] toInt32] == 24);
606 }
607
608 @autoreleasepool {
609 JSContext *context = [[JSContext alloc] init];
610 JSValue *object = [JSValue valueWithNewObjectInContext:context];
611
612 object[@"point"] = @{ @"x":@1, @"y":@2 };
613 object[@"point"][@"x"] = @3;
614 CGPoint point = [object[@"point"] toPoint];
615 checkResult(@"toPoint", point.x == 3 && point.y == 2);
616
617 object[@{ @"toString":^{ return @"foo"; } }] = @"bar";
618 checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
619
620 object[[@"foobar" substringToIndex:3]] = @"bar";
621 checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
622 }
623
624 @autoreleasepool {
625 JSContext *context = [[JSContext alloc] init];
626 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
627 NSDictionary *expectedDict = @{
628 @"foo" : [NSNumber numberWithInt:1],
629 @"bar" : @{
630 @"baz": [NSNumber numberWithInt:2]
631 }
632 };
633 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
634 };
635 [context evaluateScript:@"var myDict = { \
636 'foo': 1, \
637 'bar': {'baz': 2} \
638 }; \
639 handleTheDictionary(myDict);"];
640
641 context[@"handleTheArray"] = ^(NSArray *array) {
642 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
643 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
644 };
645 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
646 }
647
648 @autoreleasepool {
649 JSContext *context = [[JSContext alloc] init];
650 TextXYZ *testXYZ = [[TextXYZ alloc] init];
651 context[@"testXYZ"] = testXYZ;
652 testXYZ.x = 3;
653 testXYZ.y = 4;
654 testXYZ.z = 5;
655 [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"];
656 [context evaluateScript:@"testXYZ.test('test')"];
657 checkResult(@"TextXYZ - testXYZTested", testXYZTested);
658 JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"];
659 checkResult(@"TextXYZ - result", [result isEqualToObject:@"13,4,undefined"]);
660 }
661
662 @autoreleasepool {
663 JSContext *context = [[JSContext alloc] init];
664 [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{
665 JSPropertyDescriptorGetKey:^{
666 return [JSContext currentThis][@"x"];
667 }
668 }];
669 JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context];
670 int result = [object [@"getterProperty"] toInt32];
671 checkResult(@"getterProperty", result == 101);
672 }
673
674 @autoreleasepool {
675 JSContext *context = [[JSContext alloc] init];
676 context[@"concatenate"] = ^{
677 NSArray *arguments = [JSContext currentArguments];
678 if (![arguments count])
679 return @"";
680 NSString *message = [arguments[0] description];
681 for (NSUInteger index = 1; index < [arguments count]; ++index)
682 message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]];
683 return message;
684 };
685 JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"];
686 checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]);
687 }
688
689 @autoreleasepool {
690 JSContext *context = [[JSContext alloc] init];
691 context[@"foo"] = @YES;
692 checkResult(@"@YES is boolean", [context[@"foo"] isBoolean]);
693 JSValue *result = [context evaluateScript:@"typeof foo"];
694 checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]);
695 }
696
697 @autoreleasepool {
698 JSContext *context = [[JSContext alloc] init];
699 TestObject* testObject = [TestObject testObject];
700 context[@"testObject"] = testObject;
701 JSValue *result = [context evaluateScript:@"String(testObject)"];
702 checkResult(@"String(testObject)", [result isEqualToObject:@"[object TestObject]"]);
703 }
704
705 @autoreleasepool {
706 JSContext *context = [[JSContext alloc] init];
707 TestObject* testObject = [TestObject testObject];
708 context[@"testObject"] = testObject;
709 JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"];
710 checkResult(@"String(testObject.__proto__)", [result isEqualToObject:@"[object TestObjectPrototype]"]);
711 }
712
713 @autoreleasepool {
714 JSContext *context = [[JSContext alloc] init];
715 context[@"TestObject"] = [TestObject class];
716 JSValue *result = [context evaluateScript:@"String(TestObject)"];
12899fa2 717 checkResult(@"String(TestObject)", [result isEqualToObject:@"function TestObject() {\n [native code]\n}"]);
93a37866
A
718 }
719
720 @autoreleasepool {
721 JSContext *context = [[JSContext alloc] init];
722 JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context];
723 checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]);
724 }
725
726 @autoreleasepool {
727 JSContext *context = [[JSContext alloc] init];
728 context[@"TestObject"] = [TestObject class];
729 JSValue *result = [context evaluateScript:@"TestObject.parentTest()"];
730 checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]);
731 }
732
733 @autoreleasepool {
734 JSContext *context = [[JSContext alloc] init];
735 TestObject* testObject = [TestObject testObject];
736 context[@"testObjectA"] = testObject;
737 context[@"testObjectB"] = testObject;
738 JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"];
739 checkResult(@"testObjectA == testObjectB", [result isBoolean] && [result toBool]);
740 }
741
742 @autoreleasepool {
743 JSContext *context = [[JSContext alloc] init];
744 TestObject* testObject = [TestObject testObject];
745 context[@"testObject"] = testObject;
746 testObject.point = (CGPoint){3,4};
747 JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"];
748 checkResult(@"testObject.point - result", [result isEqualToObject:@"{\"x\":3,\"y\":4}"]);
749 checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14);
750 }
751
752 @autoreleasepool {
753 JSContext *context = [[JSContext alloc] init];
754 TestObject* testObject = [TestObject testObject];
755 testObject.six = 6;
756 context[@"testObject"] = testObject;
757 context[@"mul"] = ^(int x, int y){ return x * y; };
758 JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"];
759 checkResult(@"mul(testObject.six, 7)", [result isNumber] && [result toInt32] == 42);
760 }
761
762 @autoreleasepool {
763 JSContext *context = [[JSContext alloc] init];
764 TestObject* testObject = [TestObject testObject];
765 context[@"testObject"] = testObject;
766 context[@"testObject"][@"variable"] = @4;
767 [context evaluateScript:@"++testObject.variable"];
768 checkResult(@"++testObject.variable", testObject.variable == 5);
769 }
770
771 @autoreleasepool {
772 JSContext *context = [[JSContext alloc] init];
773 context[@"point"] = @{ @"x":@6, @"y":@7 };
774 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
775 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
776 }
777
778 @autoreleasepool {
779 JSContext *context = [[JSContext alloc] init];
780 context[@"point"] = @{ @"x":@6, @"y":@7 };
781 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
782 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
783 }
784
785 @autoreleasepool {
786 JSContext *context = [[JSContext alloc] init];
787 TestObject* testObject = [TestObject testObject];
788 context[@"testObject"] = testObject;
789 JSValue *result = [context evaluateScript:@"testObject.getString()"];
790 checkResult(@"testObject.getString()", [result isString] && [result toInt32] == 42);
791 }
792
793 @autoreleasepool {
794 JSContext *context = [[JSContext alloc] init];
795 TestObject* testObject = [TestObject testObject];
796 context[@"testObject"] = testObject;
797 JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"];
798 checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]);
799 }
800
801 @autoreleasepool {
802 JSContext *context = [[JSContext alloc] init];
803 TestObject* testObject = [TestObject testObject];
804 context[@"testObject"] = testObject;
805 JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"];
806 checkResult(@"testObject.getString.call(testObject)", [result isString] && [result toInt32] == 42);
807 }
808
809 @autoreleasepool {
810 JSContext *context = [[JSContext alloc] init];
811 TestObject* testObject = [TestObject testObject];
812 context[@"testObject"] = testObject;
813 checkResult(@"testObject.getString.call({}) pre", !context.exception);
814 [context evaluateScript:@"testObject.getString.call({})"];
815 checkResult(@"testObject.getString.call({}) post", context.exception);
816 }
817
818 @autoreleasepool {
819 JSContext *context = [[JSContext alloc] init];
820 TestObject* testObject = [TestObject testObject];
821 context[@"testObject"] = testObject;
822 JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"];
823 checkResult(@"testObject.callback", [result isNumber] && [result toInt32] == 42);
824 result = [context evaluateScript:@"testObject.bogusCallback"];
825 checkResult(@"testObject.bogusCallback == undefined", [result isUndefined]);
826 }
827
828 @autoreleasepool {
829 JSContext *context = [[JSContext alloc] init];
830 TestObject *testObject = [TestObject testObject];
831 context[@"testObject"] = testObject;
832 JSValue *result = [context evaluateScript:@"Function.prototype.toString.call(testObject.callback)"];
833 checkResult(@"Function.prototype.toString", !context.exception && ![result isUndefined]);
834 }
835
836 @autoreleasepool {
837 JSContext *context1 = [[JSContext alloc] init];
838 JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine];
839 JSValue *value = [JSValue valueWithDouble:42 inContext:context2];
840 context1[@"passValueBetweenContexts"] = value;
841 JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"];
842 checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]);
843 }
844
845 @autoreleasepool {
846 JSContext *context = [[JSContext alloc] init];
847 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
848 NSDictionary *expectedDict = @{
849 @"foo" : [NSNumber numberWithInt:1],
850 @"bar" : @{
851 @"baz": [NSNumber numberWithInt:2]
852 }
853 };
854 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
855 };
856 [context evaluateScript:@"var myDict = { \
857 'foo': 1, \
858 'bar': {'baz': 2} \
859 }; \
860 handleTheDictionary(myDict);"];
861
862 context[@"handleTheArray"] = ^(NSArray *array) {
863 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
864 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
865 };
866 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
867 }
868
869 @autoreleasepool {
870 JSContext *context = [[JSContext alloc] init];
871 TestObject *testObject = [TestObject testObject];
872 @autoreleasepool {
873 context[@"testObject"] = testObject;
874 [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"];
875 [context evaluateScript:@"testObject = undefined"];
876 }
877
878 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
879
880 @autoreleasepool {
881 context[@"testObject"] = testObject;
882 }
883 }
884
885 @autoreleasepool {
886 JSContext *context = [[JSContext alloc] init];
887 TextXYZ *testXYZ = [[TextXYZ alloc] init];
888
889 @autoreleasepool {
890 context[@"testXYZ"] = testXYZ;
891
892 [context evaluateScript:@" \
893 didClick = false; \
894 testXYZ.onclick = function() { \
895 didClick = true; \
896 }; \
897 \
898 testXYZ.weakOnclick = function() { \
899 return 'foo'; \
900 }; \
901 "];
902 }
903
904 @autoreleasepool {
905 [testXYZ click];
906 JSValue *result = [context evaluateScript:@"didClick"];
907 checkResult(@"Event handler onclick", [result toBool]);
908 }
909
910 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
911
912 @autoreleasepool {
913 JSValue *result = [context evaluateScript:@"testXYZ.onclick"];
914 checkResult(@"onclick still around after GC", !([result isNull] || [result isUndefined]));
915 }
916
917
918 @autoreleasepool {
919 JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"];
920 checkResult(@"weakOnclick not around after GC", [result isNull] || [result isUndefined]);
921 }
922
923 @autoreleasepool {
924 [context evaluateScript:@" \
925 didClick = false; \
926 testXYZ = null; \
927 "];
928 }
929
930 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
931
932 @autoreleasepool {
933 [testXYZ click];
934 JSValue *result = [context evaluateScript:@"didClick"];
935 checkResult(@"Event handler onclick doesn't fire", ![result toBool]);
936 }
937 }
938
939 @autoreleasepool {
940 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
941 TestObject *testObject = [TestObject testObject];
942 JSManagedValue *weakValue;
943 @autoreleasepool {
944 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
945 context[@"testObject"] = testObject;
946 weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]];
947 }
948
949 @autoreleasepool {
950 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
951 context[@"testObject"] = testObject;
952 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
953 checkResult(@"weak value == nil", ![weakValue value]);
954 checkResult(@"root is still alive", ![context[@"testObject"] isUndefined]);
955 }
956 }
957
958 @autoreleasepool {
959 JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
960 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
961 TinyDOMNode *root = [[TinyDOMNode alloc] init];
962 TinyDOMNode *lastNode = root;
963 for (NSUInteger i = 0; i < 3; i++) {
964 TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
965 [lastNode appendChild:newNode];
966 lastNode = newNode;
967 }
968
969 @autoreleasepool {
970 context[@"root"] = root;
971 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
972 TinyDOMNode *lastNode = nil;
973 while (head) {
974 lastNode = head;
975 head = [lastNode childAtIndex:0];
976 }
977 return lastNode;
978 };
979 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
980 }
981
982 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
983
984 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
985 checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
986
987 [TinyDOMNode clearSharedVirtualMachine];
988 }
989
990 @autoreleasepool {
991 JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
992 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
993 TinyDOMNode *root = [[TinyDOMNode alloc] init];
994 TinyDOMNode *lastNode = root;
995 for (NSUInteger i = 0; i < 3; i++) {
996 TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
997 [lastNode appendChild:newNode];
998 lastNode = newNode;
999 }
1000
1001 @autoreleasepool {
1002 context[@"root"] = root;
1003 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
1004 TinyDOMNode *lastNode = nil;
1005 while (head) {
1006 lastNode = head;
1007 head = [lastNode childAtIndex:0];
1008 }
1009 return lastNode;
1010 };
1011 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
1012
1013 [root appendChild:[root childAtIndex:0]];
1014 [root removeChildAtIndex:0];
1015 }
1016
1017 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1018
1019 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
1020 checkResult(@"duplicate calls to addManagedReference don't cause things to die", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
1021
1022 [TinyDOMNode clearSharedVirtualMachine];
1023 }
1024
1025 @autoreleasepool {
1026 JSContext *context = [[JSContext alloc] init];
1027 JSValue *o = [JSValue valueWithNewObjectInContext:context];
1028 o[@"foo"] = @"foo";
1029 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1030
1031 checkResult(@"JSValue correctly protected its internal value", [[o[@"foo"] toString] isEqualToString:@"foo"]);
1032 }
1033
1034 @autoreleasepool {
1035 JSContext *context = [[JSContext alloc] init];
1036 TestObject *testObject = [TestObject testObject];
1037 context[@"testObject"] = testObject;
1038 [context evaluateScript:@"testObject.__lookupGetter__('variable').call({})"];
1039 checkResult(@"Make sure we throw an exception when calling getter on incorrect |this|", context.exception);
1040 }
1041
1042 @autoreleasepool {
1043 TestObject *testObject = [TestObject testObject];
1044 JSManagedValue *managedTestObject;
1045 @autoreleasepool {
1046 JSContext *context = [[JSContext alloc] init];
1047 context[@"testObject"] = testObject;
1048 managedTestObject = [JSManagedValue managedValueWithValue:context[@"testObject"]];
1049 [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject];
1050 }
1051 }
12899fa2
A
1052
1053 @autoreleasepool {
1054 JSContext *context = [[JSContext alloc] init];
1055 context[@"UnexportedObject"] = [UnexportedObject class];
1056 context[@"makeObject"] = ^{
1057 return [[UnexportedObject alloc] init];
1058 };
1059 JSValue *result = [context evaluateScript:@"(makeObject() instanceof UnexportedObject)"];
1060 checkResult(@"makeObject() instanceof UnexportedObject", [result isBoolean] && [result toBool]);
1061 }
1062
1063 @autoreleasepool {
1064 JSContext *context = [[JSContext alloc] init];
1065 context[@"MyClass"] = ^{
1066 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1067 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1068 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1069 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyClass"][@"prototype"] JSValueRef]);
1070 return newThis;
1071 };
1072
1073 context[@"MyOtherClass"] = ^{
1074 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1075 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1076 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1077 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyOtherClass"][@"prototype"] JSValueRef]);
1078 return newThis;
1079 };
1080
1081 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
1082 NSLog(@"EXCEPTION: %@", [exception toString]);
1083 context.exception = nil;
1084 };
1085
1086 JSValue *constructor1 = context[@"MyClass"];
1087 JSValue *constructor2 = context[@"MyOtherClass"];
1088
1089 JSValue *value1 = [context evaluateScript:@"new MyClass()"];
1090 checkResult(@"value1 instanceof MyClass", [value1 isInstanceOf:constructor1]);
1091 checkResult(@"!(value1 instanceof MyOtherClass)", ![value1 isInstanceOf:constructor2]);
1092 checkResult(@"MyClass.prototype.constructor === MyClass", [[context evaluateScript:@"MyClass.prototype.constructor === MyClass"] toBool]);
1093 checkResult(@"MyClass instanceof Function", [[context evaluateScript:@"MyClass instanceof Function"] toBool]);
1094
1095 JSValue *value2 = [context evaluateScript:@"new MyOtherClass()"];
1096 checkResult(@"value2 instanceof MyOtherClass", [value2 isInstanceOf:constructor2]);
1097 checkResult(@"!(value2 instanceof MyClass)", ![value2 isInstanceOf:constructor1]);
1098 checkResult(@"MyOtherClass.prototype.constructor === MyOtherClass", [[context evaluateScript:@"MyOtherClass.prototype.constructor === MyOtherClass"] toBool]);
1099 checkResult(@"MyOtherClass instanceof Function", [[context evaluateScript:@"MyOtherClass instanceof Function"] toBool]);
1100 }
1101
1102 @autoreleasepool {
1103 JSContext *context = [[JSContext alloc] init];
1104 context[@"MyClass"] = ^{
1105 NSLog(@"I'm intentionally not returning anything.");
1106 };
1107 JSValue *result = [context evaluateScript:@"new MyClass()"];
1108 @autoreleasepool {
1109 JSContext *context = [[JSContext alloc] init];
1110 context[@"TestObject"] = [TestObject class];
1111 JSValue *testObject = [context evaluateScript:@"(new TestObject())"];
1112 checkResult(@"testObject instanceof TestObject", [testObject isInstanceOf:context[@"TestObject"]]);
1113
1114 context[@"TextXYZ"] = [TextXYZ class];
1115 JSValue *textObject = [context evaluateScript:@"(new TextXYZ(\"Called TextXYZ constructor!\"))"];
1116 checkResult(@"textObject instanceof TextXYZ", [textObject isInstanceOf:context[@"TextXYZ"]]);
1117 }
1118
1119 @autoreleasepool {
1120 JSContext *context = [[JSContext alloc] init];
1121 context[@"ClassA"] = [ClassA class];
1122 context[@"ClassB"] = [ClassB class];
1123 context[@"ClassC"] = [ClassC class]; // Should print error message about too many inits found.
1124
1125 JSValue *a = [context evaluateScript:@"(new ClassA(42))"];
1126 checkResult(@"a instanceof ClassA", [a isInstanceOf:context[@"ClassA"]]);
1127
1128 JSValue *b = [context evaluateScript:@"(new ClassB(42, 53))"];
1129 checkResult(@"b instanceof ClassB", [b isInstanceOf:context[@"ClassB"]]);
1130
1131 JSValue *canConstructClassC = [context evaluateScript:@"(function() { \
1132 try { \
1133 (new ClassC(1, 2)); \
1134 return true; \
1135 } catch(e) { \
1136 return false; \
1137 } \
1138 })()"];
1139 checkResult(@"shouldn't be able to construct ClassC", ![canConstructClassC toBool]);
1140 }
1141
1142 @autoreleasepool {
1143 JSContext *context = [[JSContext alloc] init];
1144 context[@"ClassD"] = [ClassD class];
1145 context[@"ClassE"] = [ClassE class];
1146
1147 JSValue *d = [context evaluateScript:@"(new ClassD())"];
1148 checkResult(@"Returning instance of ClassE from ClassD's init has correct class", [d isInstanceOf:context[@"ClassE"]]);
1149 }
1150
1151 checkResult(@"result === undefined", [result isUndefined]);
1152 checkResult(@"exception.message is correct'", context.exception
1153 && [@"Objective-C blocks called as constructors must return an object." isEqualToString:[context.exception[@"message"] toString]]);
1154 }
93a37866
A
1155}
1156
1157#else
1158
1159void testObjectiveCAPI()
1160{
1161}
1162
1163#endif