[Content Extensions] Add memory reporting.
[WebKit-https.git] / Source / JavaScriptCore / API / tests / testapi.mm
1 /*
2  * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import <JavaScriptCore/JavaScriptCore.h>
27
28 #import "CurrentThisInsideBlockGetterTest.h"
29 #import "DateTests.h"
30 #import "JSExportTests.h"
31 #import "Regress141275.h"
32 #import "Regress141809.h"
33
34 #import <pthread.h>
35
36 extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
37 extern "C" void JSSynchronousEdenCollectForDebugging(JSContextRef);
38
39 extern "C" bool _Block_has_signature(id);
40 extern "C" const char * _Block_signature(id);
41
42 extern int failed;
43 extern "C" void testObjectiveCAPI(void);
44 extern "C" void checkResult(NSString *, bool);
45
46 #if JSC_OBJC_API_ENABLED
47
48 @interface UnexportedObject : NSObject
49 @end
50
51 @implementation UnexportedObject
52 @end
53
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>
69 - (id)init;
70 @property int variable;
71 @property (readonly) int six;
72 @property CGPoint point;
73 + (NSString *)classTest;
74 + (NSString *)parentTest;
75 - (NSString *)getString;
76 JSExportAs(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
118 bool testXYZTested = false;
119
120 @protocol TextXYZ <JSExport>
121 - (id)initWithString:(NSString*)string;
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;
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 }
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 }
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>
195 @end
196
197 @implementation TinyDOMNode {
198     NSMutableArray *m_children;
199     JSVirtualMachine *m_sharedVirtualMachine;
200 }
201
202 - (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
203 {
204     self = [super init];
205     if (!self)
206         return nil;
207
208     m_children = [[NSMutableArray alloc] initWithCapacity:0];
209     m_sharedVirtualMachine = virtualMachine;
210 #if !__has_feature(objc_arc)
211     [m_sharedVirtualMachine retain];
212 #endif
213
214     return self;
215 }
216
217 - (void)appendChild:(TinyDOMNode *)child
218 {
219     [m_sharedVirtualMachine addManagedReference:child withOwner:self];
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;
239     [m_sharedVirtualMachine removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
240     [m_children removeObjectAtIndex:index];
241 }
242
243 @end
244
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;
288 - (int)initialize;
289 @end
290
291 @protocol InitB <JSExport>
292 - (id)initWithA:(int)a b:(int)b;
293 @end
294
295 @protocol InitC <JSExport>
296 - (id)_init;
297 @end
298
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
308 @interface ClassCPrime : ClassB<InitA, InitC>
309 @end
310
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 }
332 - (int)initialize
333 {
334     return 42;
335 }
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
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
386 @implementation ClassD
387
388 - (id)initWithA:(int)a
389 {
390     self = nil;
391     return [[ClassE alloc] initWithA:a];
392 }
393 - (int)initialize
394 {
395     return 0;
396 }
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
414
415 static 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
461 extern "C" void checkResult(NSString *description, bool passed)
462 {
463     NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
464     if (!passed)
465         failed = 1;
466 }
467
468 static 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
477 static 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
487 void testObjectiveCAPI()
488 {
489     NSLog(@"Testing Objective-C API");
490     @autoreleasepool {
491         JSVirtualMachine* vm = [[JSVirtualMachine alloc] init];
492         JSContext* context = [[JSContext alloc] initWithVirtualMachine:vm];
493         [context evaluateScript:@"bad"];
494     }
495
496     @autoreleasepool {
497         JSContext *context = [[JSContext alloc] init];
498         JSValue *result = [context evaluateScript:@"2 + 2"];
499         checkResult(@"2 + 2", [result isNumber] && [result toInt32] == 4);
500     }
501
502     @autoreleasepool {
503         JSContext *context = [[JSContext alloc] init];
504         NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]];
505         checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]);
506     }
507
508     @autoreleasepool {
509         JSContext *context = [[JSContext alloc] init];
510         context[@"message"] = @"Hello";
511         JSValue *result = [context evaluateScript:@"message + ', World!'"];
512         checkResult(@"Hello, World!", [result isString] && [result isEqualToObject:@"Hello, World!"]);
513     }
514
515     @autoreleasepool {
516         JSContext *context = [[JSContext alloc] init];
517         checkResult(@"Promise is not exposed", [context[@"Promise"] isUndefined]);
518         JSValue *result = [context evaluateScript:@"typeof Promise"];
519         checkResult(@"typeof Promise is 'undefined'", [result isString] && [result isEqualToObject:@"undefined"]);
520     }
521
522     @autoreleasepool {
523         JSContext *context = [[JSContext alloc] init];
524         JSValue *result = [context evaluateScript:@"({ x:42 })"];
525         checkResult(@"({ x:42 })", [result isObject] && [result[@"x"] isEqualToObject:@42]);
526         id obj = [result toObject];
527         checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]);
528         id num = (NSDictionary *)obj[@"x"];
529         checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]);
530     }
531
532     @autoreleasepool {
533         JSContext *context = [[JSContext alloc] init];
534         JSValue *result = [context evaluateScript:@"[ ]"];
535         checkResult(@"[ ]", [result isArray]);
536     }
537
538     @autoreleasepool {
539         JSContext *context = [[JSContext alloc] init];
540         JSValue *result = [context evaluateScript:@"new Date"];
541         checkResult(@"new Date", [result isDate]);
542     }
543
544     @autoreleasepool {
545         JSCollection* myPrivateProperties = [[JSCollection alloc] init];
546
547         @autoreleasepool {
548             JSContext* context = [[JSContext alloc] init];
549             TestObject* rootObject = [TestObject testObject];
550             context[@"root"] = rootObject;
551             [context.virtualMachine addManagedReference:myPrivateProperties withOwner:rootObject];
552             [myPrivateProperties setValue:[JSValue valueWithBool:true inContext:context] forKey:@"is_ham"];
553             [myPrivateProperties setValue:[JSValue valueWithObject:@"hello!" inContext:context] forKey:@"message"];
554             [myPrivateProperties setValue:[JSValue valueWithInt32:42 inContext:context] forKey:@"my_number"];
555             [myPrivateProperties setValue:[JSValue valueWithNullInContext:context] forKey:@"definitely_null"];
556             [myPrivateProperties setValue:[JSValue valueWithUndefinedInContext:context] forKey:@"not_sure_if_undefined"];
557
558             JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
559
560             JSValue *isHam = [myPrivateProperties valueForKey:@"is_ham"];
561             JSValue *message = [myPrivateProperties valueForKey:@"message"];
562             JSValue *myNumber = [myPrivateProperties valueForKey:@"my_number"];
563             JSValue *definitelyNull = [myPrivateProperties valueForKey:@"definitely_null"];
564             JSValue *notSureIfUndefined = [myPrivateProperties valueForKey:@"not_sure_if_undefined"];
565             checkResult(@"is_ham is true", [isHam isBoolean] && [isHam toBool]);
566             checkResult(@"message is hello!", [message isString] && [@"hello!" isEqualToString:[message toString]]);
567             checkResult(@"my_number is 42", [myNumber isNumber] && [myNumber toInt32] == 42);
568             checkResult(@"definitely_null is null", [definitelyNull isNull]);
569             checkResult(@"not_sure_if_undefined is undefined", [notSureIfUndefined isUndefined]);
570         }
571
572         checkResult(@"is_ham is nil", ![myPrivateProperties valueForKey:@"is_ham"]);
573         checkResult(@"message is nil", ![myPrivateProperties valueForKey:@"message"]);
574         checkResult(@"my_number is 42", ![myPrivateProperties valueForKey:@"my_number"]);
575         checkResult(@"definitely_null is null", ![myPrivateProperties valueForKey:@"definitely_null"]);
576         checkResult(@"not_sure_if_undefined is undefined", ![myPrivateProperties valueForKey:@"not_sure_if_undefined"]);
577     }
578
579     @autoreleasepool {
580         JSContext *context = [[JSContext alloc] init];
581         JSValue *message = [JSValue valueWithObject:@"hello" inContext:context];
582         TestObject *rootObject = [TestObject testObject];
583         JSCollection *collection = [[JSCollection alloc] init];
584         context[@"root"] = rootObject;
585         @autoreleasepool {
586             JSValue *jsCollection = [JSValue valueWithObject:collection inContext:context];
587             JSManagedValue *weakCollection = [JSManagedValue managedValueWithValue:jsCollection andOwner:rootObject];
588             [context.virtualMachine addManagedReference:weakCollection withOwner:message];
589             JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
590         }
591     }
592
593     @autoreleasepool {
594         JSContext *context = [[JSContext alloc] init];
595         __block int result;
596         context[@"blockCallback"] = ^(int value){
597             result = value;
598         };
599         [context evaluateScript:@"blockCallback(42)"];
600         checkResult(@"blockCallback", result == 42);
601     }
602
603     if (blockSignatureContainsClass()) {
604         @autoreleasepool {
605             JSContext *context = [[JSContext alloc] init];
606             __block bool result = false;
607             context[@"blockCallback"] = ^(NSString *value){
608                 result = [@"42" isEqualToString:value] == YES;
609             };
610             [context evaluateScript:@"blockCallback(42)"];
611             checkResult(@"blockCallback(NSString *)", result);
612         }
613     } else
614         NSLog(@"Skipping 'blockCallback(NSString *)' test case");
615
616     @autoreleasepool {
617         JSContext *context = [[JSContext alloc] init];
618         checkResult(@"!context.exception", !context.exception);
619         [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
620         checkResult(@"context.exception", context.exception);
621     }
622
623     @autoreleasepool {
624         JSContext *context = [[JSContext alloc] init];
625         __block bool caught = false;
626         context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
627             (void)context;
628             (void)exception;
629             caught = true;
630         };
631         [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
632         checkResult(@"JSContext.exceptionHandler", caught);
633     }
634
635     @autoreleasepool {
636         JSContext *context = [[JSContext alloc] init];
637         __block int expectedExceptionLineNumber = 1;
638         __block bool sawExpectedExceptionLineNumber = false;
639         context.exceptionHandler = ^(JSContext *, JSValue *exception) {
640             sawExpectedExceptionLineNumber = [exception[@"line"] toInt32] == expectedExceptionLineNumber;
641         };
642         [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
643         checkResult(@"evaluteScript exception on line 1", sawExpectedExceptionLineNumber);
644
645         expectedExceptionLineNumber = 2;
646         sawExpectedExceptionLineNumber = false;
647         [context evaluateScript:@"// Line 1\n!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
648         checkResult(@"evaluteScript exception on line 2", sawExpectedExceptionLineNumber);
649     }
650
651     @autoreleasepool {
652         JSContext *context = [[JSContext alloc] init];
653         __block bool emptyExceptionSourceURL = false;
654         context.exceptionHandler = ^(JSContext *, JSValue *exception) {
655             emptyExceptionSourceURL = [exception[@"sourceURL"] isUndefined];
656         };
657         [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
658         checkResult(@"evaluteScript: exception has no sourceURL", emptyExceptionSourceURL);
659
660         __block NSString *exceptionSourceURL = nil;
661         context.exceptionHandler = ^(JSContext *, JSValue *exception) {
662             exceptionSourceURL = [exception[@"sourceURL"] toString];
663         };
664         NSURL *url = [NSURL fileURLWithPath:@"/foo/bar.js"];
665         [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()" withSourceURL:url];
666         checkResult(@"evaluateScript:withSourceURL: exception has expected sourceURL", [exceptionSourceURL isEqualToString:[url absoluteString]]);
667     }
668
669     @autoreleasepool {
670         JSContext *context = [[JSContext alloc] init];
671         context[@"callback"] = ^{
672             JSContext *context = [JSContext currentContext];
673             context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context];
674         };
675         JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
676         checkResult(@"Explicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
677         checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception);
678     }
679
680     @autoreleasepool {
681         JSContext *context = [[JSContext alloc] init];
682         context[@"callback"] = ^{
683             JSContext *context = [JSContext currentContext];
684             [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
685         };
686         JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
687         checkResult(@"Implicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
688         checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception);
689     }
690
691     @autoreleasepool {
692         JSContext *context = [[JSContext alloc] init];
693         [context evaluateScript:
694             @"function sum(array) { \
695                 var result = 0; \
696                 for (var i in array) \
697                     result += array[i]; \
698                 return result; \
699             }"];
700         JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context];
701         JSValue *sumFunction = context[@"sum"];
702         JSValue *result = [sumFunction callWithArguments:@[ array ]];
703         checkResult(@"sum([13, 2, 7])", [result toInt32] == 22);
704     }
705
706     @autoreleasepool {
707         JSContext *context = [[JSContext alloc] init];
708         JSValue *mulAddFunction = [context evaluateScript:
709             @"(function(array, object) { \
710                 var result = []; \
711                 for (var i in array) \
712                     result.push(array[i] * object.x + object.y); \
713                 return result; \
714             })"];
715         JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]];
716         checkResult(@"mulAddFunction", [result isObject] && [[result toString] isEqual:@"43,44,46"]);
717     }
718
719     @autoreleasepool {
720         JSContext *context = [[JSContext alloc] init];        
721         JSValue *array = [JSValue valueWithNewArrayInContext:context];
722         checkResult(@"arrayLengthEmpty", [[array[@"length"] toNumber] unsignedIntegerValue] == 0);
723         JSValue *value1 = [JSValue valueWithInt32:42 inContext:context];
724         JSValue *value2 = [JSValue valueWithInt32:24 inContext:context];
725         NSUInteger lowIndex = 5;
726         NSUInteger maxLength = UINT_MAX;
727
728         [array setValue:value1 atIndex:lowIndex];
729         checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1));
730
731         [array setValue:value1 atIndex:(maxLength - 1)];
732         checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
733
734         [array setValue:value2 atIndex:maxLength];
735         checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
736
737         [array setValue:value2 atIndex:(maxLength + 1)];
738         checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
739
740         if (sizeof(NSUInteger) == 8)
741             checkResult(@"valueAtIndex:0 is undefined", [[array valueAtIndex:0] isUndefined]);
742         else
743             checkResult(@"valueAtIndex:0", [[array valueAtIndex:0] toInt32] == 24);
744         checkResult(@"valueAtIndex:lowIndex", [[array valueAtIndex:lowIndex] toInt32] == 42);
745         checkResult(@"valueAtIndex:maxLength - 1", [[array valueAtIndex:(maxLength - 1)] toInt32] == 42);
746         checkResult(@"valueAtIndex:maxLength", [[array valueAtIndex:maxLength] toInt32] == 24);
747         checkResult(@"valueAtIndex:maxLength + 1", [[array valueAtIndex:(maxLength + 1)] toInt32] == 24);
748     }
749
750     @autoreleasepool {
751         JSContext *context = [[JSContext alloc] init];
752         JSValue *object = [JSValue valueWithNewObjectInContext:context];
753
754         object[@"point"] = @{ @"x":@1, @"y":@2 };
755         object[@"point"][@"x"] = @3;
756         CGPoint point = [object[@"point"] toPoint];
757         checkResult(@"toPoint", point.x == 3 && point.y == 2);
758
759         object[@{ @"toString":^{ return @"foo"; } }] = @"bar";
760         checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
761
762         object[[@"foobar" substringToIndex:3]] = @"bar";
763         checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
764     }
765
766     @autoreleasepool {
767         JSContext *context = [[JSContext alloc] init];
768         TextXYZ *testXYZ = [[TextXYZ alloc] init];
769         context[@"testXYZ"] = testXYZ;
770         testXYZ.x = 3;
771         testXYZ.y = 4;
772         testXYZ.z = 5;
773         [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"];
774         [context evaluateScript:@"testXYZ.test('test')"];
775         checkResult(@"TextXYZ - testXYZTested", testXYZTested);
776         JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"];
777         checkResult(@"TextXYZ - result", [result isEqualToObject:@"13,4,undefined"]);
778     }
779
780     @autoreleasepool {
781         JSContext *context = [[JSContext alloc] init];
782         [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{
783             JSPropertyDescriptorGetKey:^{
784                 return [JSContext currentThis][@"x"];
785             }
786         }];
787         JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context];
788         int result = [object [@"getterProperty"] toInt32];
789         checkResult(@"getterProperty", result == 101);
790     }
791
792     @autoreleasepool {
793         JSContext *context = [[JSContext alloc] init];
794         context[@"concatenate"] = ^{
795             NSArray *arguments = [JSContext currentArguments];
796             if (![arguments count])
797                 return @"";
798             NSString *message = [arguments[0] description];
799             for (NSUInteger index = 1; index < [arguments count]; ++index)
800                 message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]];
801             return message;
802         };
803         JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"];
804         checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]);
805     }
806
807     @autoreleasepool {
808         JSContext *context = [[JSContext alloc] init];
809         context[@"foo"] = @YES;
810         checkResult(@"@YES is boolean", [context[@"foo"] isBoolean]);
811         JSValue *result = [context evaluateScript:@"typeof foo"];
812         checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]);
813     }
814
815     @autoreleasepool {
816         JSContext *context = [[JSContext alloc] init];
817         JSValue *result = [context evaluateScript:@"String(console)"];
818         checkResult(@"String(console)", [result isEqualToObject:@"[object Console]"]);
819         result = [context evaluateScript:@"typeof console.log"];
820         checkResult(@"typeof console.log", [result isEqualToObject:@"function"]);
821     }
822
823     @autoreleasepool {
824         JSContext *context = [[JSContext alloc] init];
825         TestObject* testObject = [TestObject testObject];
826         context[@"testObject"] = testObject;
827         JSValue *result = [context evaluateScript:@"String(testObject)"];
828         checkResult(@"String(testObject)", [result isEqualToObject:@"[object TestObject]"]);
829     }
830
831     @autoreleasepool {
832         JSContext *context = [[JSContext alloc] init];
833         TestObject* testObject = [TestObject testObject];
834         context[@"testObject"] = testObject;
835         JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"];
836         checkResult(@"String(testObject.__proto__)", [result isEqualToObject:@"[object TestObjectPrototype]"]);
837     }
838
839     @autoreleasepool {
840         JSContext *context = [[JSContext alloc] init];
841         context[@"TestObject"] = [TestObject class];
842         JSValue *result = [context evaluateScript:@"String(TestObject)"];
843         checkResult(@"String(TestObject)", [result isEqualToObject:@"function TestObject() {\n    [native code]\n}"]);
844     }
845
846     @autoreleasepool {
847         JSContext *context = [[JSContext alloc] init];
848         JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context];
849         checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]);
850     }
851
852     @autoreleasepool {
853         JSContext *context = [[JSContext alloc] init];
854         context[@"TestObject"] = [TestObject class];
855         JSValue *result = [context evaluateScript:@"TestObject.parentTest()"];
856         checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]);
857     }
858
859     @autoreleasepool {
860         JSContext *context = [[JSContext alloc] init];
861         TestObject* testObject = [TestObject testObject];
862         context[@"testObjectA"] = testObject;
863         context[@"testObjectB"] = testObject;
864         JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"];
865         checkResult(@"testObjectA == testObjectB", [result isBoolean] && [result toBool]);
866     }
867
868     @autoreleasepool {
869         JSContext *context = [[JSContext alloc] init];
870         TestObject* testObject = [TestObject testObject];
871         context[@"testObject"] = testObject;
872         testObject.point = (CGPoint){3,4};
873         JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"];
874         checkResult(@"testObject.point - result", [result isEqualToObject:@"{\"x\":3,\"y\":4}"]);
875         checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14);
876     }
877
878     @autoreleasepool {
879         JSContext *context = [[JSContext alloc] init];
880         TestObject* testObject = [TestObject testObject];
881         testObject.six = 6;
882         context[@"testObject"] = testObject;
883         context[@"mul"] = ^(int x, int y){ return x * y; };
884         JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"];
885         checkResult(@"mul(testObject.six, 7)", [result isNumber] && [result toInt32] == 42);
886     }
887
888     @autoreleasepool {
889         JSContext *context = [[JSContext alloc] init];
890         TestObject* testObject = [TestObject testObject];
891         context[@"testObject"] = testObject;
892         context[@"testObject"][@"variable"] = @4;
893         [context evaluateScript:@"++testObject.variable"];
894         checkResult(@"++testObject.variable", testObject.variable == 5);
895     }
896
897     @autoreleasepool {
898         JSContext *context = [[JSContext alloc] init];
899         context[@"point"] = @{ @"x":@6, @"y":@7 };
900         JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
901         checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
902     }
903
904     @autoreleasepool {
905         JSContext *context = [[JSContext alloc] init];
906         context[@"point"] = @{ @"x":@6, @"y":@7 };
907         JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
908         checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
909     }
910
911     @autoreleasepool {
912         JSContext *context = [[JSContext alloc] init];
913         TestObject* testObject = [TestObject testObject];
914         context[@"testObject"] = testObject;
915         JSValue *result = [context evaluateScript:@"testObject.getString()"];
916         checkResult(@"testObject.getString()", [result isString] && [result toInt32] == 42);
917     }
918
919     @autoreleasepool {
920         JSContext *context = [[JSContext alloc] init];
921         TestObject* testObject = [TestObject testObject];
922         context[@"testObject"] = testObject;
923         JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"];
924         checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]);
925     }
926
927     @autoreleasepool {
928         JSContext *context = [[JSContext alloc] init];
929         TestObject* testObject = [TestObject testObject];
930         context[@"testObject"] = testObject;
931         JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"];
932         checkResult(@"testObject.getString.call(testObject)", [result isString] && [result toInt32] == 42);
933     }
934
935     @autoreleasepool {
936         JSContext *context = [[JSContext alloc] init];
937         TestObject* testObject = [TestObject testObject];
938         context[@"testObject"] = testObject;
939         checkResult(@"testObject.getString.call({}) pre", !context.exception);
940         [context evaluateScript:@"testObject.getString.call({})"];
941         checkResult(@"testObject.getString.call({}) post", context.exception);
942     }
943
944     @autoreleasepool {
945         JSContext *context = [[JSContext alloc] init];
946         TestObject* testObject = [TestObject testObject];
947         context[@"testObject"] = testObject;
948         JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"];
949         checkResult(@"testObject.callback", [result isNumber] && [result toInt32] == 42);
950         result = [context evaluateScript:@"testObject.bogusCallback"];
951         checkResult(@"testObject.bogusCallback == undefined", [result isUndefined]);
952     }
953
954     @autoreleasepool {
955         JSContext *context = [[JSContext alloc] init];
956         TestObject *testObject = [TestObject testObject];
957         context[@"testObject"] = testObject;
958         JSValue *result = [context evaluateScript:@"Function.prototype.toString.call(testObject.callback)"];
959         checkResult(@"Function.prototype.toString", !context.exception && ![result isUndefined]);
960     }
961
962     @autoreleasepool {
963         JSContext *context1 = [[JSContext alloc] init];
964         JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine];
965         JSValue *value = [JSValue valueWithDouble:42 inContext:context2];
966         context1[@"passValueBetweenContexts"] = value;
967         JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"];
968         checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]);
969     }
970
971     @autoreleasepool {
972         JSContext *context = [[JSContext alloc] init];
973         context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
974             NSDictionary *expectedDict = @{
975                 @"foo" : [NSNumber numberWithInt:1],
976                 @"bar" : @{
977                     @"baz": [NSNumber numberWithInt:2]
978                 }
979             };
980             checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
981         };
982         [context evaluateScript:@"var myDict = { \
983             'foo': 1, \
984             'bar': {'baz': 2} \
985         }; \
986         handleTheDictionary(myDict);"];
987
988         context[@"handleTheArray"] = ^(NSArray *array) {
989             NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
990             checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
991         };
992         [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
993     }
994
995     @autoreleasepool {
996         JSContext *context = [[JSContext alloc] init];
997         TestObject *testObject = [TestObject testObject];
998         @autoreleasepool {
999             context[@"testObject"] = testObject;
1000             [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"];
1001             [context evaluateScript:@"testObject = undefined"];
1002         }
1003         
1004         JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1005
1006         @autoreleasepool {
1007             context[@"testObject"] = testObject;
1008         }
1009     }
1010
1011     @autoreleasepool {
1012         JSContext *context = [[JSContext alloc] init];
1013         TextXYZ *testXYZ = [[TextXYZ alloc] init];
1014
1015         @autoreleasepool {
1016             context[@"testXYZ"] = testXYZ;
1017
1018             [context evaluateScript:@" \
1019                 didClick = false; \
1020                 testXYZ.onclick = function() { \
1021                     didClick = true; \
1022                 }; \
1023                  \
1024                 testXYZ.weakOnclick = function() { \
1025                     return 'foo'; \
1026                 }; \
1027             "];
1028         }
1029
1030         @autoreleasepool {
1031             [testXYZ click];
1032             JSValue *result = [context evaluateScript:@"didClick"];
1033             checkResult(@"Event handler onclick", [result toBool]);
1034         }
1035
1036         JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1037
1038         @autoreleasepool {
1039             JSValue *result = [context evaluateScript:@"testXYZ.onclick"];
1040             checkResult(@"onclick still around after GC", !([result isNull] || [result isUndefined]));
1041         }
1042
1043
1044         @autoreleasepool {
1045             JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"];
1046             checkResult(@"weakOnclick not around after GC", [result isNull] || [result isUndefined]);
1047         }
1048
1049         @autoreleasepool {
1050             [context evaluateScript:@" \
1051                 didClick = false; \
1052                 testXYZ = null; \
1053             "];
1054         }
1055
1056         JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1057
1058         @autoreleasepool {
1059             [testXYZ click];
1060             JSValue *result = [context evaluateScript:@"didClick"];
1061             checkResult(@"Event handler onclick doesn't fire", ![result toBool]);
1062         }
1063     }
1064
1065     @autoreleasepool {
1066         JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
1067         TestObject *testObject = [TestObject testObject];
1068         JSManagedValue *weakValue;
1069         @autoreleasepool {
1070             JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
1071             context[@"testObject"] = testObject;
1072             weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]];
1073         }
1074
1075         @autoreleasepool {
1076             JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
1077             context[@"testObject"] = testObject;
1078             JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1079             checkResult(@"weak value == nil", ![weakValue value]);
1080             checkResult(@"root is still alive", ![context[@"testObject"] isUndefined]);
1081         }
1082     }
1083
1084     @autoreleasepool {
1085         JSContext *context = [[JSContext alloc] init];
1086         TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
1087         TinyDOMNode *lastNode = root;
1088         for (NSUInteger i = 0; i < 3; i++) {
1089             TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
1090             [lastNode appendChild:newNode];
1091             lastNode = newNode;
1092         }
1093
1094         @autoreleasepool {
1095             context[@"root"] = root;
1096             context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
1097                 TinyDOMNode *lastNode = nil;
1098                 while (head) {
1099                     lastNode = head;
1100                     head = [lastNode childAtIndex:0];
1101                 }
1102                 return lastNode;
1103             };
1104             [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
1105         }
1106
1107         JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1108
1109         JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
1110         checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
1111     }
1112
1113     @autoreleasepool {
1114         JSContext *context = [[JSContext alloc] init];
1115         TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
1116         TinyDOMNode *lastNode = root;
1117         for (NSUInteger i = 0; i < 3; i++) {
1118             TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
1119             [lastNode appendChild:newNode];
1120             lastNode = newNode;
1121         }
1122
1123         @autoreleasepool {
1124             context[@"root"] = root;
1125             context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
1126                 TinyDOMNode *lastNode = nil;
1127                 while (head) {
1128                     lastNode = head;
1129                     head = [lastNode childAtIndex:0];
1130                 }
1131                 return lastNode;
1132             };
1133             [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
1134
1135             [root appendChild:[root childAtIndex:0]];
1136             [root removeChildAtIndex:0];
1137         }
1138
1139         JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1140
1141         JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
1142         checkResult(@"duplicate calls to addManagedReference don't cause things to die", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
1143     }
1144
1145     @autoreleasepool {
1146         JSContext *context = [[JSContext alloc] init];
1147         JSValue *o = [JSValue valueWithNewObjectInContext:context];
1148         o[@"foo"] = @"foo";
1149         JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1150
1151         checkResult(@"JSValue correctly protected its internal value", [[o[@"foo"] toString] isEqualToString:@"foo"]);
1152     }
1153
1154     @autoreleasepool {
1155         JSContext *context = [[JSContext alloc] init];
1156         TestObject *testObject = [TestObject testObject];
1157         context[@"testObject"] = testObject;
1158         [context evaluateScript:@"testObject.__lookupGetter__('variable').call({})"];
1159         checkResult(@"Make sure we throw an exception when calling getter on incorrect |this|", context.exception);
1160     }
1161
1162     @autoreleasepool {
1163         TestObject *testObject = [TestObject testObject];
1164         JSManagedValue *managedTestObject;
1165         @autoreleasepool {
1166             JSContext *context = [[JSContext alloc] init];
1167             context[@"testObject"] = testObject;
1168             managedTestObject = [JSManagedValue managedValueWithValue:context[@"testObject"]];
1169             [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject];
1170         }
1171     }
1172
1173     @autoreleasepool {
1174         JSContext *context = [[JSContext alloc] init];
1175         TestObject *testObject = [TestObject testObject];
1176         context[@"testObject"] = testObject;
1177         JSManagedValue *managedValue = nil;
1178         @autoreleasepool {
1179             JSValue *object = [JSValue valueWithNewObjectInContext:context];
1180             managedValue = [JSManagedValue managedValueWithValue:object andOwner:testObject];
1181             [context.virtualMachine addManagedReference:managedValue withOwner:testObject];
1182         }
1183         JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1184     }
1185
1186     @autoreleasepool {
1187         JSContext *context = [[JSContext alloc] init];
1188         context[@"MyClass"] = ^{
1189             JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1190             JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1191             JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1192             JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyClass"][@"prototype"] JSValueRef]);
1193             return newThis;
1194         };
1195
1196         context[@"MyOtherClass"] = ^{
1197             JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1198             JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1199             JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1200             JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyOtherClass"][@"prototype"] JSValueRef]);
1201             return newThis;
1202         };
1203
1204         context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
1205             NSLog(@"EXCEPTION: %@", [exception toString]);
1206             context.exception = nil;
1207         };
1208
1209         JSValue *constructor1 = context[@"MyClass"];
1210         JSValue *constructor2 = context[@"MyOtherClass"];
1211
1212         JSValue *value1 = [context evaluateScript:@"new MyClass()"];
1213         checkResult(@"value1 instanceof MyClass", [value1 isInstanceOf:constructor1]);
1214         checkResult(@"!(value1 instanceof MyOtherClass)", ![value1 isInstanceOf:constructor2]);
1215         checkResult(@"MyClass.prototype.constructor === MyClass", [[context evaluateScript:@"MyClass.prototype.constructor === MyClass"] toBool]);
1216         checkResult(@"MyClass instanceof Function", [[context evaluateScript:@"MyClass instanceof Function"] toBool]);
1217
1218         JSValue *value2 = [context evaluateScript:@"new MyOtherClass()"];
1219         checkResult(@"value2 instanceof MyOtherClass", [value2 isInstanceOf:constructor2]);
1220         checkResult(@"!(value2 instanceof MyClass)", ![value2 isInstanceOf:constructor1]);
1221         checkResult(@"MyOtherClass.prototype.constructor === MyOtherClass", [[context evaluateScript:@"MyOtherClass.prototype.constructor === MyOtherClass"] toBool]);
1222         checkResult(@"MyOtherClass instanceof Function", [[context evaluateScript:@"MyOtherClass instanceof Function"] toBool]);
1223     }
1224
1225     @autoreleasepool {
1226         JSContext *context = [[JSContext alloc] init];
1227         context[@"MyClass"] = ^{
1228             NSLog(@"I'm intentionally not returning anything.");
1229         };
1230         JSValue *result = [context evaluateScript:@"new MyClass()"];
1231         checkResult(@"result === undefined", [result isUndefined]);
1232         checkResult(@"exception.message is correct'", context.exception 
1233             && [@"Objective-C blocks called as constructors must return an object." isEqualToString:[context.exception[@"message"] toString]]);
1234     }
1235
1236     @autoreleasepool {
1237         checkResult(@"[JSContext currentThis] == nil outside of callback", ![JSContext currentThis]);
1238         checkResult(@"[JSContext currentArguments] == nil outside of callback", ![JSContext currentArguments]);
1239         if ([JSContext currentCallee])
1240             checkResult(@"[JSContext currentCallee] == nil outside of callback", ![JSContext currentCallee]);
1241     }
1242
1243     if ([JSContext currentCallee]) {
1244         @autoreleasepool {
1245             JSContext *context = [[JSContext alloc] init];
1246             context[@"testFunction"] = ^{
1247                 checkResult(@"testFunction.foo === 42", [[JSContext currentCallee][@"foo"] toInt32] == 42);
1248             };
1249             context[@"testFunction"][@"foo"] = @42;
1250             [context[@"testFunction"] callWithArguments:nil];
1251
1252             context[@"TestConstructor"] = ^{
1253                 JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]];
1254                 JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef];
1255                 JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL);
1256                 JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentCallee][@"prototype"] JSValueRef]);
1257                 return newThis;
1258             };
1259             checkResult(@"(new TestConstructor) instanceof TestConstructor", [context evaluateScript:@"(new TestConstructor) instanceof TestConstructor"]);
1260         }
1261     }
1262
1263     @autoreleasepool {
1264         JSContext *context = [[JSContext alloc] init];
1265         context[@"TestObject"] = [TestObject class];
1266         JSValue *testObject = [context evaluateScript:@"(new TestObject())"];
1267         checkResult(@"testObject instanceof TestObject", [testObject isInstanceOf:context[@"TestObject"]]);
1268
1269         context[@"TextXYZ"] = [TextXYZ class];
1270         JSValue *textObject = [context evaluateScript:@"(new TextXYZ(\"Called TextXYZ constructor!\"))"];
1271         checkResult(@"textObject instanceof TextXYZ", [textObject isInstanceOf:context[@"TextXYZ"]]);
1272     }
1273
1274     @autoreleasepool {
1275         JSContext *context = [[JSContext alloc] init];
1276         context[@"ClassA"] = [ClassA class];
1277         context[@"ClassB"] = [ClassB class];
1278         context[@"ClassC"] = [ClassC class]; // Should print error message about too many inits found.
1279         context[@"ClassCPrime"] = [ClassCPrime class]; // Ditto.
1280
1281         JSValue *a = [context evaluateScript:@"(new ClassA(42))"];
1282         checkResult(@"a instanceof ClassA", [a isInstanceOf:context[@"ClassA"]]);
1283         checkResult(@"a.initialize() is callable", [[a invokeMethod:@"initialize" withArguments:@[]] toInt32] == 42);
1284
1285         JSValue *b = [context evaluateScript:@"(new ClassB(42, 53))"];
1286         checkResult(@"b instanceof ClassB", [b isInstanceOf:context[@"ClassB"]]);
1287
1288         JSValue *canConstructClassC = [context evaluateScript:@"(function() { \
1289             try { \
1290                 (new ClassC(1, 2)); \
1291                 return true; \
1292             } catch(e) { \
1293                 return false; \
1294             } \
1295         })()"];
1296         checkResult(@"shouldn't be able to construct ClassC", ![canConstructClassC toBool]);
1297         JSValue *canConstructClassCPrime = [context evaluateScript:@"(function() { \
1298             try { \
1299                 (new ClassCPrime(1)); \
1300                 return true; \
1301             } catch(e) { \
1302                 return false; \
1303             } \
1304         })()"];
1305         checkResult(@"shouldn't be able to construct ClassCPrime", ![canConstructClassCPrime toBool]);
1306     }
1307
1308     @autoreleasepool {
1309         JSContext *context = [[JSContext alloc] init];
1310         context[@"ClassD"] = [ClassD class];
1311         context[@"ClassE"] = [ClassE class];
1312        
1313         JSValue *d = [context evaluateScript:@"(new ClassD())"];
1314         checkResult(@"Returning instance of ClassE from ClassD's init has correct class", [d isInstanceOf:context[@"ClassE"]]);
1315     }
1316
1317     @autoreleasepool {
1318         JSContext *context = [[JSContext alloc] init];
1319         while (!evilAllocationObjectWasDealloced) {
1320             @autoreleasepool {
1321                 EvilAllocationObject *evilObject = [[EvilAllocationObject alloc] initWithContext:context];
1322                 context[@"evilObject"] = evilObject;
1323                 context[@"evilObject"] = nil;
1324             }
1325         }
1326         checkResult(@"EvilAllocationObject was successfully dealloced without crashing", evilAllocationObjectWasDealloced);
1327     }
1328
1329     @autoreleasepool {
1330         JSContext *context = [[JSContext alloc] init];
1331         checkResult(@"default context.name is nil", context.name == nil);
1332         NSString *name1 = @"Name1";
1333         NSString *name2 = @"Name2";
1334         context.name = name1;
1335         NSString *fetchedName1 = context.name;
1336         context.name = name2;
1337         NSString *fetchedName2 = context.name;
1338         context.name = nil;
1339         NSString *fetchedName3 = context.name;
1340         checkResult(@"fetched context.name was expected", [fetchedName1 isEqualToString:name1]);
1341         checkResult(@"fetched context.name was expected", [fetchedName2 isEqualToString:name2]);
1342         checkResult(@"fetched context.name was expected", ![fetchedName1 isEqualToString:fetchedName2]);
1343         checkResult(@"fetched context.name was expected", fetchedName3 == nil);
1344     }
1345
1346     @autoreleasepool {
1347         JSContext *context = [[JSContext alloc] init];
1348         context[@"UnexportedObject"] = [UnexportedObject class];
1349         context[@"makeObject"] = ^{
1350             return [[UnexportedObject alloc] init];
1351         };
1352         JSValue *result = [context evaluateScript:@"(makeObject() instanceof UnexportedObject)"];
1353         checkResult(@"makeObject() instanceof UnexportedObject", [result isBoolean] && [result toBool]);
1354     }
1355
1356     @autoreleasepool {
1357         JSContext *context = [[JSContext alloc] init];
1358         [[JSValue valueWithInt32:42 inContext:context] toDictionary];
1359         [[JSValue valueWithInt32:42 inContext:context] toArray];
1360     }
1361
1362     @autoreleasepool {
1363         JSContext *context = [[JSContext alloc] init];
1364
1365         // Create the root, make it reachable from JS, and force an EdenCollection
1366         // so that we scan the external object graph.
1367         TestObject *root = [TestObject testObject];
1368         @autoreleasepool {
1369             context[@"root"] = root;
1370         }
1371         JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]);
1372
1373         // Create a new Obj-C object only reachable via the external object graph
1374         // through the object we already scanned during the EdenCollection.
1375         TestObject *child = [TestObject testObject];
1376         [context.virtualMachine addManagedReference:child withOwner:root];
1377
1378         // Create a new managed JSValue that will only be kept alive if we properly rescan
1379         // the external object graph.
1380         JSManagedValue *managedJSObject = nil;
1381         @autoreleasepool {
1382             JSValue *jsObject = [JSValue valueWithObject:@"hello" inContext:context];
1383             managedJSObject = [JSManagedValue managedValueWithValue:jsObject];
1384             [context.virtualMachine addManagedReference:managedJSObject withOwner:child];
1385         }
1386
1387         // Force another EdenCollection. It should rescan the new part of the external object graph.
1388         JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]);
1389         
1390         // Check that the managed JSValue is still alive.
1391         checkResult(@"EdenCollection doesn't reclaim new managed values", [managedJSObject value] != nil);
1392     }
1393
1394     @autoreleasepool {
1395         JSContext *context = [[JSContext alloc] init];
1396         
1397         pthread_t threadID;
1398         pthread_create(&threadID, NULL, &threadMain, (__bridge void*)context);
1399         pthread_join(threadID, nullptr);
1400         JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
1401
1402         checkResult(@"Did not crash after entering the VM from another thread", true);
1403     }
1404     
1405     currentThisInsideBlockGetterTest();
1406     runDateTests();
1407     runJSExportTests();
1408     runRegress141275();
1409     runRegress141809();
1410 }
1411
1412 #else
1413
1414 void testObjectiveCAPI()
1415 {
1416 }
1417
1418 #endif // JSC_OBJC_API_ENABLED