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