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