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