]> git.saurik.com Git - apple/javascriptcore.git/blame - API/tests/testapi.mm
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / API / tests / testapi.mm
CommitLineData
93a37866 1/*
40a37d08 2 * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
93a37866
A
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
81345200 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
93a37866
A
24 */
25
26#import <JavaScriptCore/JavaScriptCore.h>
27
81345200
A
28#import "CurrentThisInsideBlockGetterTest.h"
29#import "DateTests.h"
30#import "JSExportTests.h"
ed1e77d3 31#import "Regress141275.h"
40a37d08
A
32#import "Regress141809.h"
33
34#import <pthread.h>
81345200 35
93a37866 36extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
81345200 37extern "C" void JSSynchronousEdenCollectForDebugging(JSContextRef);
93a37866
A
38
39extern "C" bool _Block_has_signature(id);
40extern "C" const char * _Block_signature(id);
41
42extern int failed;
43extern "C" void testObjectiveCAPI(void);
81345200 44extern "C" void checkResult(NSString *, bool);
93a37866
A
45
46#if JSC_OBJC_API_ENABLED
47
12899fa2
A
48@interface UnexportedObject : NSObject
49@end
50
51@implementation UnexportedObject
52@end
53
93a37866
A
54@protocol ParentObject <JSExport>
55@end
56
57@interface ParentObject : NSObject<ParentObject>
58+ (NSString *)parentTest;
59@end
60
61@implementation ParentObject
62+ (NSString *)parentTest
63{
64 return [self description];
65}
66@end
67
68@protocol TestObject <JSExport>
12899fa2 69- (id)init;
93a37866
A
70@property int variable;
71@property (readonly) int six;
72@property CGPoint point;
73+ (NSString *)classTest;
74+ (NSString *)parentTest;
75- (NSString *)getString;
76JSExportAs(testArgumentTypes,
77- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
78);
79- (void)callback:(JSValue *)function;
80- (void)bogusCallback:(void(^)(int))function;
81@end
82
83@interface TestObject : ParentObject <TestObject>
84@property int six;
85+ (id)testObject;
86@end
87
88@implementation TestObject
89@synthesize variable;
90@synthesize six;
91@synthesize point;
92+ (id)testObject
93{
94 return [[TestObject alloc] init];
95}
96+ (NSString *)classTest
97{
98 return @"classTest - okay";
99}
100- (NSString *)getString
101{
102 return @"42";
103}
104- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
105{
106 return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]];
107}
108- (void)callback:(JSValue *)function
109{
110 [function callWithArguments:[NSArray arrayWithObject:[NSNumber numberWithInt:42]]];
111}
112- (void)bogusCallback:(void(^)(int))function
113{
114 function(42);
115}
116@end
117
118bool testXYZTested = false;
119
120@protocol TextXYZ <JSExport>
12899fa2 121- (id)initWithString:(NSString*)string;
93a37866
A
122@property int x;
123@property (readonly) int y;
124@property (assign) JSValue *onclick;
125@property (assign) JSValue *weakOnclick;
126- (void)test:(NSString *)message;
127@end
128
129@interface TextXYZ : NSObject <TextXYZ>
130@property int x;
131@property int y;
132@property int z;
133- (void)click;
134@end
135
136@implementation TextXYZ {
137 JSManagedValue *m_weakOnclickHandler;
138 JSManagedValue *m_onclickHandler;
139}
140@synthesize x;
141@synthesize y;
142@synthesize z;
12899fa2
A
143- (id)initWithString:(NSString*)string
144{
145 self = [super init];
146 if (!self)
147 return nil;
148
149 NSLog(@"%@", string);
150
151 return self;
152}
93a37866
A
153- (void)test:(NSString *)message
154{
155 testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5;
156}
157- (void)setWeakOnclick:(JSValue *)value
158{
159 m_weakOnclickHandler = [JSManagedValue managedValueWithValue:value];
160}
161
162- (void)setOnclick:(JSValue *)value
163{
164 m_onclickHandler = [JSManagedValue managedValueWithValue:value];
165 [value.context.virtualMachine addManagedReference:m_onclickHandler withOwner:self];
166}
167- (JSValue *)weakOnclick
168{
169 return [m_weakOnclickHandler value];
170}
171- (JSValue *)onclick
172{
173 return [m_onclickHandler value];
174}
175- (void)click
176{
177 if (!m_onclickHandler)
178 return;
179
180 JSValue *function = [m_onclickHandler value];
181 [function callWithArguments:[NSArray array]];
182}
93a37866
A
183@end
184
185@class TinyDOMNode;
186
187@protocol TinyDOMNode <JSExport>
188- (void)appendChild:(TinyDOMNode *)child;
189- (NSUInteger)numberOfChildren;
190- (TinyDOMNode *)childAtIndex:(NSUInteger)index;
191- (void)removeChildAtIndex:(NSUInteger)index;
192@end
193
194@interface TinyDOMNode : NSObject<TinyDOMNode>
93a37866
A
195@end
196
197@implementation TinyDOMNode {
198 NSMutableArray *m_children;
81345200 199 JSVirtualMachine *m_sharedVirtualMachine;
93a37866
A
200}
201
81345200 202- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
93a37866
A
203{
204 self = [super init];
205 if (!self)
206 return nil;
207
208 m_children = [[NSMutableArray alloc] initWithCapacity:0];
81345200 209 m_sharedVirtualMachine = virtualMachine;
93a37866 210#if !__has_feature(objc_arc)
81345200 211 [m_sharedVirtualMachine retain];
93a37866 212#endif
81345200
A
213
214 return self;
93a37866
A
215}
216
217- (void)appendChild:(TinyDOMNode *)child
218{
81345200 219 [m_sharedVirtualMachine addManagedReference:child withOwner:self];
93a37866
A
220 [m_children addObject:child];
221}
222
223- (NSUInteger)numberOfChildren
224{
225 return [m_children count];
226}
227
228- (TinyDOMNode *)childAtIndex:(NSUInteger)index
229{
230 if (index >= [m_children count])
231 return nil;
232 return [m_children objectAtIndex:index];
233}
234
235- (void)removeChildAtIndex:(NSUInteger)index
236{
237 if (index >= [m_children count])
238 return;
81345200 239 [m_sharedVirtualMachine removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
93a37866
A
240 [m_children removeObjectAtIndex:index];
241}
242
243@end
244
12899fa2
A
245@interface JSCollection : NSObject
246- (void)setValue:(JSValue *)value forKey:(NSString *)key;
247- (JSValue *)valueForKey:(NSString *)key;
248@end
249
250@implementation JSCollection {
251 NSMutableDictionary *_dict;
252}
253- (id)init
254{
255 self = [super init];
256 if (!self)
257 return nil;
258
259 _dict = [[NSMutableDictionary alloc] init];
260
261 return self;
262}
263
264- (void)setValue:(JSValue *)value forKey:(NSString *)key
265{
266 JSManagedValue *oldManagedValue = [_dict objectForKey:key];
267 if (oldManagedValue) {
268 JSValue* oldValue = [oldManagedValue value];
269 if (oldValue)
270 [oldValue.context.virtualMachine removeManagedReference:oldManagedValue withOwner:self];
271 }
272 JSManagedValue *managedValue = [JSManagedValue managedValueWithValue:value];
273 [value.context.virtualMachine addManagedReference:managedValue withOwner:self];
274 [_dict setObject:managedValue forKey:key];
275}
276
277- (JSValue *)valueForKey:(NSString *)key
278{
279 JSManagedValue *managedValue = [_dict objectForKey:key];
280 if (!managedValue)
281 return nil;
282 return [managedValue value];
283}
284@end
285
286@protocol InitA <JSExport>
287- (id)initWithA:(int)a;
81345200 288- (int)initialize;
12899fa2
A
289@end
290
291@protocol InitB <JSExport>
292- (id)initWithA:(int)a b:(int)b;
293@end
294
81345200
A
295@protocol InitC <JSExport>
296- (id)_init;
297@end
298
12899fa2
A
299@interface ClassA : NSObject<InitA>
300@end
301
302@interface ClassB : ClassA<InitB>
303@end
304
305@interface ClassC : ClassB<InitA, InitB>
306@end
307
81345200
A
308@interface ClassCPrime : ClassB<InitA, InitC>
309@end
310
12899fa2
A
311@interface ClassD : NSObject<InitA>
312- (id)initWithA:(int)a;
313@end
314
315@interface ClassE : ClassD
316- (id)initWithA:(int)a;
317@end
318
319@implementation ClassA {
320 int _a;
321}
322- (id)initWithA:(int)a
323{
324 self = [super init];
325 if (!self)
326 return nil;
327
328 _a = a;
329
330 return self;
331}
81345200
A
332- (int)initialize
333{
334 return 42;
335}
12899fa2
A
336@end
337
338@implementation ClassB {
339 int _b;
340}
341- (id)initWithA:(int)a b:(int)b
342{
343 self = [super initWithA:a];
344 if (!self)
345 return nil;
346
347 _b = b;
348
349 return self;
350}
351@end
352
353@implementation ClassC {
354 int _c;
355}
356- (id)initWithA:(int)a
357{
358 return [self initWithA:a b:0];
359}
360- (id)initWithA:(int)a b:(int)b
361{
362 self = [super initWithA:a b:b];
363 if (!self)
364 return nil;
365
366 _c = a + b;
367
368 return self;
369}
370@end
371
81345200
A
372@implementation ClassCPrime
373- (id)initWithA:(int)a
374{
375 self = [super initWithA:a b:0];
376 if (!self)
377 return nil;
378 return self;
379}
380- (id)_init
381{
382 return [self initWithA:42];
383}
384@end
385
12899fa2
A
386@implementation ClassD
387
388- (id)initWithA:(int)a
389{
390 self = nil;
391 return [[ClassE alloc] initWithA:a];
392}
81345200
A
393- (int)initialize
394{
395 return 0;
396}
12899fa2
A
397@end
398
399@implementation ClassE {
400 int _a;
401}
402
403- (id)initWithA:(int)a
404{
405 self = [super init];
406 if (!self)
407 return nil;
408
409 _a = a;
410
411 return self;
412}
413@end
81345200
A
414
415static bool evilAllocationObjectWasDealloced = false;
416
417@interface EvilAllocationObject : NSObject
418- (JSValue *)doEvilThingsWithContext:(JSContext *)context;
419@end
420
421@implementation EvilAllocationObject {
422 JSContext *m_context;
423}
424- (id)initWithContext:(JSContext *)context
425{
426 self = [super init];
427 if (!self)
428 return nil;
429
430 m_context = context;
431
432 return self;
433}
434- (void)dealloc
435{
436 [self doEvilThingsWithContext:m_context];
437 evilAllocationObjectWasDealloced = true;
438#if !__has_feature(objc_arc)
439 [super dealloc];
440#endif
441}
442
443- (JSValue *)doEvilThingsWithContext:(JSContext *)context
444{
445 JSValue *result = [context evaluateScript:@" \
446 (function() { \
447 var a = []; \
448 var sum = 0; \
449 for (var i = 0; i < 10000; ++i) { \
450 sum += i; \
451 a[i] = sum; \
452 } \
453 return sum; \
454 })()"];
455
456 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
457 return result;
458}
459@end
460
461extern "C" void checkResult(NSString *description, bool passed)
93a37866
A
462{
463 NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
464 if (!passed)
465 failed = 1;
466}
467
468static bool blockSignatureContainsClass()
469{
470 static bool containsClass = ^{
471 id block = ^(NSString *string){ return string; };
472 return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
473 }();
474 return containsClass;
475}
476
40a37d08
A
477static void* threadMain(void* contextPtr)
478{
479 JSContext *context = (__bridge JSContext*)contextPtr;
480
481 // Do something to enter the VM.
482 TestObject *testObject = [TestObject testObject];
483 context[@"testObject"] = testObject;
484 pthread_exit(nullptr);
485}
486
ed1e77d3
A
487// This test is flaky. Since GC marks C stack and registers as roots conservatively,
488// objects not referenced logically can be accidentally marked and alive.
489// To avoid this situation as possible as we can,
490// 1. run this test first before stack is polluted,
491// 2. extract this test as a function to suppress stack height.
492static void testWeakValue()
93a37866 493{
ed1e77d3
A
494 @autoreleasepool {
495 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
496 TestObject *testObject = [TestObject testObject];
497 JSManagedValue *weakValue;
498 @autoreleasepool {
499 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
500 context[@"testObject"] = testObject;
501 weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]];
502 }
503
504 @autoreleasepool {
505 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
506 context[@"testObject"] = testObject;
507 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
508 checkResult(@"weak value == nil", ![weakValue value]);
509 checkResult(@"root is still alive", !context[@"testObject"].isUndefined);
510 }
511 }
512}
93a37866 513
ed1e77d3
A
514static void testObjectiveCAPIMain()
515{
81345200
A
516 @autoreleasepool {
517 JSVirtualMachine* vm = [[JSVirtualMachine alloc] init];
518 JSContext* context = [[JSContext alloc] initWithVirtualMachine:vm];
519 [context evaluateScript:@"bad"];
520 }
521
93a37866
A
522 @autoreleasepool {
523 JSContext *context = [[JSContext alloc] init];
524 JSValue *result = [context evaluateScript:@"2 + 2"];
ed1e77d3 525 checkResult(@"2 + 2", result.isNumber && [result toInt32] == 4);
93a37866
A
526 }
527
528 @autoreleasepool {
529 JSContext *context = [[JSContext alloc] init];
530 NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]];
531 checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]);
532 }
533
534 @autoreleasepool {
535 JSContext *context = [[JSContext alloc] init];
536 context[@"message"] = @"Hello";
537 JSValue *result = [context evaluateScript:@"message + ', World!'"];
ed1e77d3
A
538 checkResult(@"Hello, World!", result.isString && [result isEqualToObject:@"Hello, World!"]);
539 }
540
541 @autoreleasepool {
542 JSContext *context = [[JSContext alloc] init];
543 checkResult(@"Promise is not exposed", [context[@"Promise"] isUndefined]);
544 JSValue *result = [context evaluateScript:@"typeof Promise"];
545 checkResult(@"typeof Promise is 'undefined'", result.isString && [result isEqualToObject:@"undefined"]);
93a37866
A
546 }
547
548 @autoreleasepool {
549 JSContext *context = [[JSContext alloc] init];
550 JSValue *result = [context evaluateScript:@"({ x:42 })"];
ed1e77d3 551 checkResult(@"({ x:42 })", result.isObject && [result[@"x"] isEqualToObject:@42]);
93a37866
A
552 id obj = [result toObject];
553 checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]);
554 id num = (NSDictionary *)obj[@"x"];
555 checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]);
556 }
557
ed1e77d3
A
558 @autoreleasepool {
559 JSContext *context = [[JSContext alloc] init];
560 JSValue *result = [context evaluateScript:@"[ ]"];
561 checkResult(@"[ ]", result.isArray);
562 }
563
564 @autoreleasepool {
565 JSContext *context = [[JSContext alloc] init];
566 JSValue *result = [context evaluateScript:@"new Date"];
567 checkResult(@"new Date", result.isDate);
568 }
569
12899fa2
A
570 @autoreleasepool {
571 JSCollection* myPrivateProperties = [[JSCollection alloc] init];
572
573 @autoreleasepool {
574 JSContext* context = [[JSContext alloc] init];
575 TestObject* rootObject = [TestObject testObject];
576 context[@"root"] = rootObject;
577 [context.virtualMachine addManagedReference:myPrivateProperties withOwner:rootObject];
578 [myPrivateProperties setValue:[JSValue valueWithBool:true inContext:context] forKey:@"is_ham"];
579 [myPrivateProperties setValue:[JSValue valueWithObject:@"hello!" inContext:context] forKey:@"message"];
580 [myPrivateProperties setValue:[JSValue valueWithInt32:42 inContext:context] forKey:@"my_number"];
581 [myPrivateProperties setValue:[JSValue valueWithNullInContext:context] forKey:@"definitely_null"];
582 [myPrivateProperties setValue:[JSValue valueWithUndefinedInContext:context] forKey:@"not_sure_if_undefined"];
583
584 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
585
586 JSValue *isHam = [myPrivateProperties valueForKey:@"is_ham"];
587 JSValue *message = [myPrivateProperties valueForKey:@"message"];
588 JSValue *myNumber = [myPrivateProperties valueForKey:@"my_number"];
589 JSValue *definitelyNull = [myPrivateProperties valueForKey:@"definitely_null"];
590 JSValue *notSureIfUndefined = [myPrivateProperties valueForKey:@"not_sure_if_undefined"];
ed1e77d3
A
591 checkResult(@"is_ham is true", isHam.isBoolean && [isHam toBool]);
592 checkResult(@"message is hello!", message.isString && [@"hello!" isEqualToString:[message toString]]);
593 checkResult(@"my_number is 42", myNumber.isNumber && [myNumber toInt32] == 42);
594 checkResult(@"definitely_null is null", definitelyNull.isNull);
595 checkResult(@"not_sure_if_undefined is undefined", notSureIfUndefined.isUndefined);
12899fa2
A
596 }
597
598 checkResult(@"is_ham is nil", ![myPrivateProperties valueForKey:@"is_ham"]);
599 checkResult(@"message is nil", ![myPrivateProperties valueForKey:@"message"]);
600 checkResult(@"my_number is 42", ![myPrivateProperties valueForKey:@"my_number"]);
601 checkResult(@"definitely_null is null", ![myPrivateProperties valueForKey:@"definitely_null"]);
602 checkResult(@"not_sure_if_undefined is undefined", ![myPrivateProperties valueForKey:@"not_sure_if_undefined"]);
603 }
604
81345200
A
605 @autoreleasepool {
606 JSContext *context = [[JSContext alloc] init];
607 JSValue *message = [JSValue valueWithObject:@"hello" inContext:context];
608 TestObject *rootObject = [TestObject testObject];
609 JSCollection *collection = [[JSCollection alloc] init];
610 context[@"root"] = rootObject;
611 @autoreleasepool {
612 JSValue *jsCollection = [JSValue valueWithObject:collection inContext:context];
613 JSManagedValue *weakCollection = [JSManagedValue managedValueWithValue:jsCollection andOwner:rootObject];
614 [context.virtualMachine addManagedReference:weakCollection withOwner:message];
615 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
616 }
617 }
618
93a37866
A
619 @autoreleasepool {
620 JSContext *context = [[JSContext alloc] init];
621 __block int result;
622 context[@"blockCallback"] = ^(int value){
623 result = value;
624 };
625 [context evaluateScript:@"blockCallback(42)"];
626 checkResult(@"blockCallback", result == 42);
627 }
628
629 if (blockSignatureContainsClass()) {
630 @autoreleasepool {
631 JSContext *context = [[JSContext alloc] init];
632 __block bool result = false;
633 context[@"blockCallback"] = ^(NSString *value){
634 result = [@"42" isEqualToString:value] == YES;
635 };
636 [context evaluateScript:@"blockCallback(42)"];
637 checkResult(@"blockCallback(NSString *)", result);
638 }
639 } else
640 NSLog(@"Skipping 'blockCallback(NSString *)' test case");
641
642 @autoreleasepool {
643 JSContext *context = [[JSContext alloc] init];
644 checkResult(@"!context.exception", !context.exception);
645 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
646 checkResult(@"context.exception", context.exception);
647 }
648
649 @autoreleasepool {
650 JSContext *context = [[JSContext alloc] init];
651 __block bool caught = false;
652 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
653 (void)context;
654 (void)exception;
655 caught = true;
656 };
657 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
658 checkResult(@"JSContext.exceptionHandler", caught);
659 }
660
81345200
A
661 @autoreleasepool {
662 JSContext *context = [[JSContext alloc] init];
663 __block int expectedExceptionLineNumber = 1;
664 __block bool sawExpectedExceptionLineNumber = false;
665 context.exceptionHandler = ^(JSContext *, JSValue *exception) {
666 sawExpectedExceptionLineNumber = [exception[@"line"] toInt32] == expectedExceptionLineNumber;
667 };
668 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
669 checkResult(@"evaluteScript exception on line 1", sawExpectedExceptionLineNumber);
670
671 expectedExceptionLineNumber = 2;
672 sawExpectedExceptionLineNumber = false;
673 [context evaluateScript:@"// Line 1\n!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
674 checkResult(@"evaluteScript exception on line 2", sawExpectedExceptionLineNumber);
675 }
676
677 @autoreleasepool {
678 JSContext *context = [[JSContext alloc] init];
679 __block bool emptyExceptionSourceURL = false;
680 context.exceptionHandler = ^(JSContext *, JSValue *exception) {
ed1e77d3 681 emptyExceptionSourceURL = exception[@"sourceURL"].isUndefined;
81345200
A
682 };
683 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
684 checkResult(@"evaluteScript: exception has no sourceURL", emptyExceptionSourceURL);
685
686 __block NSString *exceptionSourceURL = nil;
687 context.exceptionHandler = ^(JSContext *, JSValue *exception) {
688 exceptionSourceURL = [exception[@"sourceURL"] toString];
689 };
690 NSURL *url = [NSURL fileURLWithPath:@"/foo/bar.js"];
691 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()" withSourceURL:url];
692 checkResult(@"evaluateScript:withSourceURL: exception has expected sourceURL", [exceptionSourceURL isEqualToString:[url absoluteString]]);
693 }
694
93a37866
A
695 @autoreleasepool {
696 JSContext *context = [[JSContext alloc] init];
697 context[@"callback"] = ^{
698 JSContext *context = [JSContext currentContext];
699 context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context];
700 };
701 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
702 checkResult(@"Explicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
703 checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception);
704 }
705
706 @autoreleasepool {
707 JSContext *context = [[JSContext alloc] init];
708 context[@"callback"] = ^{
709 JSContext *context = [JSContext currentContext];
710 [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
711 };
712 JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
713 checkResult(@"Implicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
714 checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception);
715 }
716
717 @autoreleasepool {
718 JSContext *context = [[JSContext alloc] init];
719 [context evaluateScript:
720 @"function sum(array) { \
721 var result = 0; \
722 for (var i in array) \
723 result += array[i]; \
724 return result; \
725 }"];
726 JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context];
727 JSValue *sumFunction = context[@"sum"];
728 JSValue *result = [sumFunction callWithArguments:@[ array ]];
729 checkResult(@"sum([13, 2, 7])", [result toInt32] == 22);
730 }
731
732 @autoreleasepool {
733 JSContext *context = [[JSContext alloc] init];
734 JSValue *mulAddFunction = [context evaluateScript:
735 @"(function(array, object) { \
736 var result = []; \
737 for (var i in array) \
738 result.push(array[i] * object.x + object.y); \
739 return result; \
740 })"];
741 JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]];
ed1e77d3 742 checkResult(@"mulAddFunction", result.isObject && [[result toString] isEqual:@"43,44,46"]);
93a37866
A
743 }
744
745 @autoreleasepool {
746 JSContext *context = [[JSContext alloc] init];
747 JSValue *array = [JSValue valueWithNewArrayInContext:context];
748 checkResult(@"arrayLengthEmpty", [[array[@"length"] toNumber] unsignedIntegerValue] == 0);
749 JSValue *value1 = [JSValue valueWithInt32:42 inContext:context];
750 JSValue *value2 = [JSValue valueWithInt32:24 inContext:context];
751 NSUInteger lowIndex = 5;
752 NSUInteger maxLength = UINT_MAX;
753
754 [array setValue:value1 atIndex:lowIndex];
755 checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1));
756
757 [array setValue:value1 atIndex:(maxLength - 1)];
758 checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
759
760 [array setValue:value2 atIndex:maxLength];
761 checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
762
763 [array setValue:value2 atIndex:(maxLength + 1)];
764 checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
765
766 if (sizeof(NSUInteger) == 8)
ed1e77d3 767 checkResult(@"valueAtIndex:0 is undefined", [array valueAtIndex:0].isUndefined);
93a37866
A
768 else
769 checkResult(@"valueAtIndex:0", [[array valueAtIndex:0] toInt32] == 24);
770 checkResult(@"valueAtIndex:lowIndex", [[array valueAtIndex:lowIndex] toInt32] == 42);
771 checkResult(@"valueAtIndex:maxLength - 1", [[array valueAtIndex:(maxLength - 1)] toInt32] == 42);
772 checkResult(@"valueAtIndex:maxLength", [[array valueAtIndex:maxLength] toInt32] == 24);
773 checkResult(@"valueAtIndex:maxLength + 1", [[array valueAtIndex:(maxLength + 1)] toInt32] == 24);
774 }
775
776 @autoreleasepool {
777 JSContext *context = [[JSContext alloc] init];
778 JSValue *object = [JSValue valueWithNewObjectInContext:context];
779
780 object[@"point"] = @{ @"x":@1, @"y":@2 };
781 object[@"point"][@"x"] = @3;
782 CGPoint point = [object[@"point"] toPoint];
783 checkResult(@"toPoint", point.x == 3 && point.y == 2);
784
785 object[@{ @"toString":^{ return @"foo"; } }] = @"bar";
786 checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
787
788 object[[@"foobar" substringToIndex:3]] = @"bar";
789 checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
790 }
791
93a37866
A
792 @autoreleasepool {
793 JSContext *context = [[JSContext alloc] init];
794 TextXYZ *testXYZ = [[TextXYZ alloc] init];
795 context[@"testXYZ"] = testXYZ;
796 testXYZ.x = 3;
797 testXYZ.y = 4;
798 testXYZ.z = 5;
799 [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"];
800 [context evaluateScript:@"testXYZ.test('test')"];
801 checkResult(@"TextXYZ - testXYZTested", testXYZTested);
802 JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"];
803 checkResult(@"TextXYZ - result", [result isEqualToObject:@"13,4,undefined"]);
804 }
805
806 @autoreleasepool {
807 JSContext *context = [[JSContext alloc] init];
808 [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{
809 JSPropertyDescriptorGetKey:^{
810 return [JSContext currentThis][@"x"];
811 }
812 }];
813 JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context];
814 int result = [object [@"getterProperty"] toInt32];
815 checkResult(@"getterProperty", result == 101);
816 }
817
818 @autoreleasepool {
819 JSContext *context = [[JSContext alloc] init];
820 context[@"concatenate"] = ^{
821 NSArray *arguments = [JSContext currentArguments];
822 if (![arguments count])
823 return @"";
824 NSString *message = [arguments[0] description];
825 for (NSUInteger index = 1; index < [arguments count]; ++index)
826 message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]];
827 return message;
828 };
829 JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"];
830 checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]);
831 }
832
833 @autoreleasepool {
834 JSContext *context = [[JSContext alloc] init];
835 context[@"foo"] = @YES;
836 checkResult(@"@YES is boolean", [context[@"foo"] isBoolean]);
837 JSValue *result = [context evaluateScript:@"typeof foo"];
838 checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]);
839 }
840
81345200
A
841 @autoreleasepool {
842 JSContext *context = [[JSContext alloc] init];
843 JSValue *result = [context evaluateScript:@"String(console)"];
844 checkResult(@"String(console)", [result isEqualToObject:@"[object Console]"]);
845 result = [context evaluateScript:@"typeof console.log"];
846 checkResult(@"typeof console.log", [result isEqualToObject:@"function"]);
847 }
848
93a37866
A
849 @autoreleasepool {
850 JSContext *context = [[JSContext alloc] init];
851 TestObject* testObject = [TestObject testObject];
852 context[@"testObject"] = testObject;
853 JSValue *result = [context evaluateScript:@"String(testObject)"];
854 checkResult(@"String(testObject)", [result isEqualToObject:@"[object TestObject]"]);
855 }
856
857 @autoreleasepool {
858 JSContext *context = [[JSContext alloc] init];
859 TestObject* testObject = [TestObject testObject];
860 context[@"testObject"] = testObject;
861 JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"];
862 checkResult(@"String(testObject.__proto__)", [result isEqualToObject:@"[object TestObjectPrototype]"]);
863 }
864
865 @autoreleasepool {
866 JSContext *context = [[JSContext alloc] init];
867 context[@"TestObject"] = [TestObject class];
868 JSValue *result = [context evaluateScript:@"String(TestObject)"];
12899fa2 869 checkResult(@"String(TestObject)", [result isEqualToObject:@"function TestObject() {\n [native code]\n}"]);
93a37866
A
870 }
871
872 @autoreleasepool {
873 JSContext *context = [[JSContext alloc] init];
874 JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context];
875 checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]);
876 }
877
878 @autoreleasepool {
879 JSContext *context = [[JSContext alloc] init];
880 context[@"TestObject"] = [TestObject class];
881 JSValue *result = [context evaluateScript:@"TestObject.parentTest()"];
882 checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]);
883 }
884
885 @autoreleasepool {
886 JSContext *context = [[JSContext alloc] init];
887 TestObject* testObject = [TestObject testObject];
888 context[@"testObjectA"] = testObject;
889 context[@"testObjectB"] = testObject;
890 JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"];
ed1e77d3 891 checkResult(@"testObjectA == testObjectB", result.isBoolean && [result toBool]);
93a37866
A
892 }
893
894 @autoreleasepool {
895 JSContext *context = [[JSContext alloc] init];
896 TestObject* testObject = [TestObject testObject];
897 context[@"testObject"] = testObject;
898 testObject.point = (CGPoint){3,4};
899 JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"];
900 checkResult(@"testObject.point - result", [result isEqualToObject:@"{\"x\":3,\"y\":4}"]);
901 checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14);
902 }
903
904 @autoreleasepool {
905 JSContext *context = [[JSContext alloc] init];
906 TestObject* testObject = [TestObject testObject];
907 testObject.six = 6;
908 context[@"testObject"] = testObject;
909 context[@"mul"] = ^(int x, int y){ return x * y; };
910 JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"];
ed1e77d3 911 checkResult(@"mul(testObject.six, 7)", result.isNumber && [result toInt32] == 42);
93a37866
A
912 }
913
914 @autoreleasepool {
915 JSContext *context = [[JSContext alloc] init];
916 TestObject* testObject = [TestObject testObject];
917 context[@"testObject"] = testObject;
918 context[@"testObject"][@"variable"] = @4;
919 [context evaluateScript:@"++testObject.variable"];
920 checkResult(@"++testObject.variable", testObject.variable == 5);
921 }
922
923 @autoreleasepool {
924 JSContext *context = [[JSContext alloc] init];
925 context[@"point"] = @{ @"x":@6, @"y":@7 };
926 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
927 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
928 }
929
930 @autoreleasepool {
931 JSContext *context = [[JSContext alloc] init];
932 context[@"point"] = @{ @"x":@6, @"y":@7 };
933 JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
934 checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
935 }
936
937 @autoreleasepool {
938 JSContext *context = [[JSContext alloc] init];
939 TestObject* testObject = [TestObject testObject];
940 context[@"testObject"] = testObject;
941 JSValue *result = [context evaluateScript:@"testObject.getString()"];
ed1e77d3 942 checkResult(@"testObject.getString()", result.isString && [result toInt32] == 42);
93a37866
A
943 }
944
945 @autoreleasepool {
946 JSContext *context = [[JSContext alloc] init];
947 TestObject* testObject = [TestObject testObject];
948 context[@"testObject"] = testObject;
949 JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"];
950 checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]);
951 }
952
953 @autoreleasepool {
954 JSContext *context = [[JSContext alloc] init];
955 TestObject* testObject = [TestObject testObject];
956 context[@"testObject"] = testObject;
957 JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"];
ed1e77d3 958 checkResult(@"testObject.getString.call(testObject)", result.isString && [result toInt32] == 42);
93a37866
A
959 }
960
961 @autoreleasepool {
962 JSContext *context = [[JSContext alloc] init];
963 TestObject* testObject = [TestObject testObject];
964 context[@"testObject"] = testObject;
965 checkResult(@"testObject.getString.call({}) pre", !context.exception);
966 [context evaluateScript:@"testObject.getString.call({})"];
967 checkResult(@"testObject.getString.call({}) post", context.exception);
968 }
969
970 @autoreleasepool {
971 JSContext *context = [[JSContext alloc] init];
972 TestObject* testObject = [TestObject testObject];
973 context[@"testObject"] = testObject;
974 JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"];
ed1e77d3 975 checkResult(@"testObject.callback", result.isNumber && [result toInt32] == 42);
93a37866 976 result = [context evaluateScript:@"testObject.bogusCallback"];
ed1e77d3 977 checkResult(@"testObject.bogusCallback == undefined", result.isUndefined);
93a37866
A
978 }
979
980 @autoreleasepool {
981 JSContext *context = [[JSContext alloc] init];
982 TestObject *testObject = [TestObject testObject];
983 context[@"testObject"] = testObject;
984 JSValue *result = [context evaluateScript:@"Function.prototype.toString.call(testObject.callback)"];
ed1e77d3 985 checkResult(@"Function.prototype.toString", !context.exception && !result.isUndefined);
93a37866
A
986 }
987
988 @autoreleasepool {
989 JSContext *context1 = [[JSContext alloc] init];
990 JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine];
991 JSValue *value = [JSValue valueWithDouble:42 inContext:context2];
992 context1[@"passValueBetweenContexts"] = value;
993 JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"];
994 checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]);
995 }
996
997 @autoreleasepool {
998 JSContext *context = [[JSContext alloc] init];
999 context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
1000 NSDictionary *expectedDict = @{
1001 @"foo" : [NSNumber numberWithInt:1],
1002 @"bar" : @{
1003 @"baz": [NSNumber numberWithInt:2]
1004 }
1005 };
1006 checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
1007 };
1008 [context evaluateScript:@"var myDict = { \
1009 'foo': 1, \
1010 'bar': {'baz': 2} \
1011 }; \
1012 handleTheDictionary(myDict);"];
1013
1014 context[@"handleTheArray"] = ^(NSArray *array) {
1015 NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
1016 checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
1017 };
1018 [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
1019 }
1020
1021 @autoreleasepool {
1022 JSContext *context = [[JSContext alloc] init];
1023 TestObject *testObject = [TestObject testObject];
1024 @autoreleasepool {
1025 context[@"testObject"] = testObject;
1026 [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"];
1027 [context evaluateScript:@"testObject = undefined"];
1028 }
1029
1030 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1031
1032 @autoreleasepool {
1033 context[@"testObject"] = testObject;
1034 }
1035 }
1036
1037 @autoreleasepool {
1038 JSContext *context = [[JSContext alloc] init];
1039 TextXYZ *testXYZ = [[TextXYZ alloc] init];
1040
1041 @autoreleasepool {
1042 context[@"testXYZ"] = testXYZ;
1043
1044 [context evaluateScript:@" \
1045 didClick = false; \
1046 testXYZ.onclick = function() { \
1047 didClick = true; \
1048 }; \
1049 \
1050 testXYZ.weakOnclick = function() { \
1051 return 'foo'; \
1052 }; \
1053 "];
1054 }
1055
1056 @autoreleasepool {
1057 [testXYZ click];
1058 JSValue *result = [context evaluateScript:@"didClick"];
1059 checkResult(@"Event handler onclick", [result toBool]);
1060 }
1061
1062 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1063
1064 @autoreleasepool {
1065 JSValue *result = [context evaluateScript:@"testXYZ.onclick"];
ed1e77d3 1066 checkResult(@"onclick still around after GC", !(result.isNull || result.isUndefined));
93a37866
A
1067 }
1068
1069
1070 @autoreleasepool {
1071 JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"];
ed1e77d3 1072 checkResult(@"weakOnclick not around after GC", result.isNull || result.isUndefined);
93a37866
A
1073 }
1074
1075 @autoreleasepool {
1076 [context evaluateScript:@" \
1077 didClick = false; \
1078 testXYZ = null; \
1079 "];
1080 }
1081
1082 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1083
1084 @autoreleasepool {
1085 [testXYZ click];
1086 JSValue *result = [context evaluateScript:@"didClick"];
1087 checkResult(@"Event handler onclick doesn't fire", ![result toBool]);
1088 }
1089 }
1090
93a37866 1091 @autoreleasepool {
81345200
A
1092 JSContext *context = [[JSContext alloc] init];
1093 TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
93a37866
A
1094 TinyDOMNode *lastNode = root;
1095 for (NSUInteger i = 0; i < 3; i++) {
81345200 1096 TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
93a37866
A
1097 [lastNode appendChild:newNode];
1098 lastNode = newNode;
1099 }
1100
1101 @autoreleasepool {
1102 context[@"root"] = root;
1103 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
1104 TinyDOMNode *lastNode = nil;
1105 while (head) {
1106 lastNode = head;
1107 head = [lastNode childAtIndex:0];
1108 }
1109 return lastNode;
1110 };
1111 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
1112 }
1113
1114 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1115
1116 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
ed1e77d3 1117 checkResult(@"My custom property == 42", myCustomProperty.isNumber && [myCustomProperty toInt32] == 42);
93a37866
A
1118 }
1119
1120 @autoreleasepool {
81345200
A
1121 JSContext *context = [[JSContext alloc] init];
1122 TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
93a37866
A
1123 TinyDOMNode *lastNode = root;
1124 for (NSUInteger i = 0; i < 3; i++) {
81345200 1125 TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
93a37866
A
1126 [lastNode appendChild:newNode];
1127 lastNode = newNode;
1128 }
1129
1130 @autoreleasepool {
1131 context[@"root"] = root;
1132 context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
1133 TinyDOMNode *lastNode = nil;
1134 while (head) {
1135 lastNode = head;
1136 head = [lastNode childAtIndex:0];
1137 }
1138 return lastNode;
1139 };
1140 [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
1141
1142 [root appendChild:[root childAtIndex:0]];
1143 [root removeChildAtIndex:0];
1144 }
1145
1146 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1147
1148 JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
ed1e77d3 1149 checkResult(@"duplicate calls to addManagedReference don't cause things to die", myCustomProperty.isNumber && [myCustomProperty toInt32] == 42);
93a37866
A
1150 }
1151
1152 @autoreleasepool {
1153 JSContext *context = [[JSContext alloc] init];
1154 JSValue *o = [JSValue valueWithNewObjectInContext:context];
1155 o[@"foo"] = @"foo";
1156 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1157
1158 checkResult(@"JSValue correctly protected its internal value", [[o[@"foo"] toString] isEqualToString:@"foo"]);
1159 }
1160
1161 @autoreleasepool {
1162 JSContext *context = [[JSContext alloc] init];
1163 TestObject *testObject = [TestObject testObject];
1164 context[@"testObject"] = testObject;
1165 [context evaluateScript:@"testObject.__lookupGetter__('variable').call({})"];
1166 checkResult(@"Make sure we throw an exception when calling getter on incorrect |this|", context.exception);
1167 }
1168
1169 @autoreleasepool {
1170 TestObject *testObject = [TestObject testObject];
1171 JSManagedValue *managedTestObject;
1172 @autoreleasepool {
1173 JSContext *context = [[JSContext alloc] init];
1174 context[@"testObject"] = testObject;
1175 managedTestObject = [JSManagedValue managedValueWithValue:context[@"testObject"]];
1176 [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject];
1177 }
1178 }
12899fa2
A
1179
1180 @autoreleasepool {
1181 JSContext *context = [[JSContext alloc] init];
81345200
A
1182 TestObject *testObject = [TestObject testObject];
1183 context[@"testObject"] = testObject;
1184 JSManagedValue *managedValue = nil;
1185 @autoreleasepool {
1186 JSValue *object = [JSValue valueWithNewObjectInContext:context];
1187 managedValue = [JSManagedValue managedValueWithValue:object andOwner:testObject];
1188 [context.virtualMachine addManagedReference:managedValue withOwner:testObject];
1189 }
1190 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
12899fa2
A
1191 }
1192
1193 @autoreleasepool {
1194 JSContext *context = [[JSContext alloc] init];
1195 context[@"MyClass"] = ^{
1196 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1197 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1198 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1199 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyClass"][@"prototype"] JSValueRef]);
1200 return newThis;
1201 };
1202
1203 context[@"MyOtherClass"] = ^{
1204 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1205 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1206 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1207 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyOtherClass"][@"prototype"] JSValueRef]);
1208 return newThis;
1209 };
1210
1211 context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
1212 NSLog(@"EXCEPTION: %@", [exception toString]);
1213 context.exception = nil;
1214 };
1215
1216 JSValue *constructor1 = context[@"MyClass"];
1217 JSValue *constructor2 = context[@"MyOtherClass"];
1218
1219 JSValue *value1 = [context evaluateScript:@"new MyClass()"];
1220 checkResult(@"value1 instanceof MyClass", [value1 isInstanceOf:constructor1]);
1221 checkResult(@"!(value1 instanceof MyOtherClass)", ![value1 isInstanceOf:constructor2]);
1222 checkResult(@"MyClass.prototype.constructor === MyClass", [[context evaluateScript:@"MyClass.prototype.constructor === MyClass"] toBool]);
1223 checkResult(@"MyClass instanceof Function", [[context evaluateScript:@"MyClass instanceof Function"] toBool]);
1224
1225 JSValue *value2 = [context evaluateScript:@"new MyOtherClass()"];
1226 checkResult(@"value2 instanceof MyOtherClass", [value2 isInstanceOf:constructor2]);
1227 checkResult(@"!(value2 instanceof MyClass)", ![value2 isInstanceOf:constructor1]);
1228 checkResult(@"MyOtherClass.prototype.constructor === MyOtherClass", [[context evaluateScript:@"MyOtherClass.prototype.constructor === MyOtherClass"] toBool]);
1229 checkResult(@"MyOtherClass instanceof Function", [[context evaluateScript:@"MyOtherClass instanceof Function"] toBool]);
1230 }
1231
1232 @autoreleasepool {
1233 JSContext *context = [[JSContext alloc] init];
1234 context[@"MyClass"] = ^{
1235 NSLog(@"I'm intentionally not returning anything.");
1236 };
1237 JSValue *result = [context evaluateScript:@"new MyClass()"];
ed1e77d3 1238 checkResult(@"result === undefined", result.isUndefined);
81345200
A
1239 checkResult(@"exception.message is correct'", context.exception
1240 && [@"Objective-C blocks called as constructors must return an object." isEqualToString:[context.exception[@"message"] toString]]);
1241 }
1242
1243 @autoreleasepool {
1244 checkResult(@"[JSContext currentThis] == nil outside of callback", ![JSContext currentThis]);
1245 checkResult(@"[JSContext currentArguments] == nil outside of callback", ![JSContext currentArguments]);
1246 if ([JSContext currentCallee])
1247 checkResult(@"[JSContext currentCallee] == nil outside of callback", ![JSContext currentCallee]);
1248 }
1249
1250 if ([JSContext currentCallee]) {
1251 @autoreleasepool {
1252 JSContext *context = [[JSContext alloc] init];
1253 context[@"testFunction"] = ^{
1254 checkResult(@"testFunction.foo === 42", [[JSContext currentCallee][@"foo"] toInt32] == 42);
1255 };
1256 context[@"testFunction"][@"foo"] = @42;
1257 [context[@"testFunction"] callWithArguments:nil];
1258
1259 context[@"TestConstructor"] = ^{
1260 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1261 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1262 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1263 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentCallee][@"prototype"] JSValueRef]);
1264 return newThis;
1265 };
1266 checkResult(@"(new TestConstructor) instanceof TestConstructor", [context evaluateScript:@"(new TestConstructor) instanceof TestConstructor"]);
1267 }
1268 }
1269
12899fa2
A
1270 @autoreleasepool {
1271 JSContext *context = [[JSContext alloc] init];
1272 context[@"TestObject"] = [TestObject class];
1273 JSValue *testObject = [context evaluateScript:@"(new TestObject())"];
1274 checkResult(@"testObject instanceof TestObject", [testObject isInstanceOf:context[@"TestObject"]]);
1275
1276 context[@"TextXYZ"] = [TextXYZ class];
1277 JSValue *textObject = [context evaluateScript:@"(new TextXYZ(\"Called TextXYZ constructor!\"))"];
1278 checkResult(@"textObject instanceof TextXYZ", [textObject isInstanceOf:context[@"TextXYZ"]]);
1279 }
1280
1281 @autoreleasepool {
1282 JSContext *context = [[JSContext alloc] init];
1283 context[@"ClassA"] = [ClassA class];
1284 context[@"ClassB"] = [ClassB class];
1285 context[@"ClassC"] = [ClassC class]; // Should print error message about too many inits found.
81345200 1286 context[@"ClassCPrime"] = [ClassCPrime class]; // Ditto.
12899fa2
A
1287
1288 JSValue *a = [context evaluateScript:@"(new ClassA(42))"];
1289 checkResult(@"a instanceof ClassA", [a isInstanceOf:context[@"ClassA"]]);
81345200 1290 checkResult(@"a.initialize() is callable", [[a invokeMethod:@"initialize" withArguments:@[]] toInt32] == 42);
12899fa2
A
1291
1292 JSValue *b = [context evaluateScript:@"(new ClassB(42, 53))"];
1293 checkResult(@"b instanceof ClassB", [b isInstanceOf:context[@"ClassB"]]);
1294
1295 JSValue *canConstructClassC = [context evaluateScript:@"(function() { \
1296 try { \
1297 (new ClassC(1, 2)); \
1298 return true; \
1299 } catch(e) { \
1300 return false; \
1301 } \
1302 })()"];
1303 checkResult(@"shouldn't be able to construct ClassC", ![canConstructClassC toBool]);
81345200
A
1304 JSValue *canConstructClassCPrime = [context evaluateScript:@"(function() { \
1305 try { \
1306 (new ClassCPrime(1)); \
1307 return true; \
1308 } catch(e) { \
1309 return false; \
1310 } \
1311 })()"];
1312 checkResult(@"shouldn't be able to construct ClassCPrime", ![canConstructClassCPrime toBool]);
12899fa2
A
1313 }
1314
1315 @autoreleasepool {
1316 JSContext *context = [[JSContext alloc] init];
1317 context[@"ClassD"] = [ClassD class];
1318 context[@"ClassE"] = [ClassE class];
1319
1320 JSValue *d = [context evaluateScript:@"(new ClassD())"];
1321 checkResult(@"Returning instance of ClassE from ClassD's init has correct class", [d isInstanceOf:context[@"ClassE"]]);
1322 }
1323
81345200
A
1324 @autoreleasepool {
1325 JSContext *context = [[JSContext alloc] init];
1326 while (!evilAllocationObjectWasDealloced) {
1327 @autoreleasepool {
1328 EvilAllocationObject *evilObject = [[EvilAllocationObject alloc] initWithContext:context];
1329 context[@"evilObject"] = evilObject;
1330 context[@"evilObject"] = nil;
1331 }
1332 }
1333 checkResult(@"EvilAllocationObject was successfully dealloced without crashing", evilAllocationObjectWasDealloced);
1334 }
1335
1336 @autoreleasepool {
1337 JSContext *context = [[JSContext alloc] init];
1338 checkResult(@"default context.name is nil", context.name == nil);
1339 NSString *name1 = @"Name1";
1340 NSString *name2 = @"Name2";
1341 context.name = name1;
1342 NSString *fetchedName1 = context.name;
1343 context.name = name2;
1344 NSString *fetchedName2 = context.name;
1345 context.name = nil;
1346 NSString *fetchedName3 = context.name;
1347 checkResult(@"fetched context.name was expected", [fetchedName1 isEqualToString:name1]);
1348 checkResult(@"fetched context.name was expected", [fetchedName2 isEqualToString:name2]);
1349 checkResult(@"fetched context.name was expected", ![fetchedName1 isEqualToString:fetchedName2]);
1350 checkResult(@"fetched context.name was expected", fetchedName3 == nil);
12899fa2 1351 }
81345200
A
1352
1353 @autoreleasepool {
1354 JSContext *context = [[JSContext alloc] init];
1355 context[@"UnexportedObject"] = [UnexportedObject class];
1356 context[@"makeObject"] = ^{
1357 return [[UnexportedObject alloc] init];
1358 };
1359 JSValue *result = [context evaluateScript:@"(makeObject() instanceof UnexportedObject)"];
ed1e77d3 1360 checkResult(@"makeObject() instanceof UnexportedObject", result.isBoolean && [result toBool]);
81345200
A
1361 }
1362
1363 @autoreleasepool {
1364 JSContext *context = [[JSContext alloc] init];
1365 [[JSValue valueWithInt32:42 inContext:context] toDictionary];
1366 [[JSValue valueWithInt32:42 inContext:context] toArray];
1367 }
1368
1369 @autoreleasepool {
1370 JSContext *context = [[JSContext alloc] init];
1371
1372 // Create the root, make it reachable from JS, and force an EdenCollection
1373 // so that we scan the external object graph.
1374 TestObject *root = [TestObject testObject];
1375 @autoreleasepool {
1376 context[@"root"] = root;
1377 }
1378 JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]);
1379
1380 // Create a new Obj-C object only reachable via the external object graph
1381 // through the object we already scanned during the EdenCollection.
1382 TestObject *child = [TestObject testObject];
1383 [context.virtualMachine addManagedReference:child withOwner:root];
1384
1385 // Create a new managed JSValue that will only be kept alive if we properly rescan
1386 // the external object graph.
1387 JSManagedValue *managedJSObject = nil;
1388 @autoreleasepool {
1389 JSValue *jsObject = [JSValue valueWithObject:@"hello" inContext:context];
1390 managedJSObject = [JSManagedValue managedValueWithValue:jsObject];
1391 [context.virtualMachine addManagedReference:managedJSObject withOwner:child];
1392 }
1393
1394 // Force another EdenCollection. It should rescan the new part of the external object graph.
1395 JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]);
1396
1397 // Check that the managed JSValue is still alive.
1398 checkResult(@"EdenCollection doesn't reclaim new managed values", [managedJSObject value] != nil);
1399 }
1400
40a37d08
A
1401 @autoreleasepool {
1402 JSContext *context = [[JSContext alloc] init];
1403
1404 pthread_t threadID;
1405 pthread_create(&threadID, NULL, &threadMain, (__bridge void*)context);
1406 pthread_join(threadID, nullptr);
1407 JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1408
1409 checkResult(@"Did not crash after entering the VM from another thread", true);
1410 }
1411
81345200
A
1412 currentThisInsideBlockGetterTest();
1413 runDateTests();
1414 runJSExportTests();
ed1e77d3 1415 runRegress141275();
40a37d08 1416 runRegress141809();
93a37866
A
1417}
1418
ed1e77d3
A
1419@protocol NumberProtocol <JSExport>
1420
1421@property (nonatomic) NSInteger number;
1422
1423@end
1424
1425@interface NumberObject : NSObject <NumberProtocol>
1426
1427@property (nonatomic) NSInteger number;
1428
1429@end
1430
1431@implementation NumberObject
1432
1433@end
1434
1435// Check that negative NSIntegers retain the correct value when passed into JS code.
1436static void checkNegativeNSIntegers()
1437{
1438 NumberObject *container = [[NumberObject alloc] init];
1439 container.number = -1;
1440 JSContext *context = [[JSContext alloc] init];
1441 context[@"container"] = container;
1442 NSString *jsID = @"var getContainerNumber = function() { return container.number }";
1443 [context evaluateScript:jsID];
1444 JSValue *jsFunction = context[@"getContainerNumber"];
1445 JSValue *result = [jsFunction callWithArguments:@[]];
1446
1447 checkResult(@"Negative number maintained its original value", [[result toString] isEqualToString:@"-1"]);
1448}
1449
1450void testObjectiveCAPI()
1451{
1452 NSLog(@"Testing Objective-C API");
1453 checkNegativeNSIntegers();
1454 testWeakValue();
1455 testObjectiveCAPIMain();
1456}
1457
93a37866
A
1458#else
1459
1460void testObjectiveCAPI()
1461{
1462}
1463
81345200 1464#endif // JSC_OBJC_API_ENABLED