1 /* 2 * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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 #include "JavaScriptCore.h" 27 #include "JSBasePrivate.h" 28 #include "JSContextRefPrivate.h" 29 #include "JSObjectRefPrivate.h" 30 #include <math.h> 31 #define ASSERT_DISABLED 0 32 #include <wtf/Assertions.h> 33 #include <wtf/UnusedParam.h> 34 35 #if OS(WINDOWS) 36 #include <windows.h> 37 #endif 38 39 #if COMPILER(MSVC) 40 41 #include <wtf/MathExtras.h> 42 43 static double nan(const char*) 44 { 45 return std::numeric_limits<double>::quiet_NaN(); 46 } 47 48 #endif 49 50 static JSGlobalContextRef context; 51 static int failed; 52 static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue) 53 { 54 if (JSValueToBoolean(context, value) != expectedValue) { 55 fprintf(stderr, "assertEqualsAsBoolean failed: %p, %d\n", value, expectedValue); 56 failed = 1; 57 } 58 } 59 60 static void assertEqualsAsNumber(JSValueRef value, double expectedValue) 61 { 62 double number = JSValueToNumber(context, value, NULL); 63 64 // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function, 65 // causing a build break with -Wshorten-64-to-32 enabled. The issue is known by the appropriate team. 66 // After that's resolved, we can remove these casts 67 if (number != expectedValue && !(isnan((float)number) && isnan((float)expectedValue))) { 68 fprintf(stderr, "assertEqualsAsNumber failed: %p, %lf\n", value, expectedValue); 69 failed = 1; 70 } 71 } 72 73 static void assertEqualsAsUTF8String(JSValueRef value, const char* expectedValue) 74 { 75 JSStringRef valueAsString = JSValueToStringCopy(context, value, NULL); 76 77 size_t jsSize = JSStringGetMaximumUTF8CStringSize(valueAsString); 78 char* jsBuffer = (char*)malloc(jsSize); 79 JSStringGetUTF8CString(valueAsString, jsBuffer, jsSize); 80 81 unsigned i; 82 for (i = 0; jsBuffer[i]; i++) { 83 if (jsBuffer[i] != expectedValue[i]) { 84 fprintf(stderr, "assertEqualsAsUTF8String failed at character %d: %c(%d) != %c(%d)\n", i, jsBuffer[i], jsBuffer[i], expectedValue[i], expectedValue[i]); 85 failed = 1; 86 } 87 } 88 89 if (jsSize < strlen(jsBuffer) + 1) { 90 fprintf(stderr, "assertEqualsAsUTF8String failed: jsSize was too small\n"); 91 failed = 1; 92 } 93 94 free(jsBuffer); 95 JSStringRelease(valueAsString); 96 } 97 98 static void assertEqualsAsCharactersPtr(JSValueRef value, const char* expectedValue) 99 { 100 JSStringRef valueAsString = JSValueToStringCopy(context, value, NULL); 101 102 size_t jsLength = JSStringGetLength(valueAsString); 103 const JSChar* jsBuffer = JSStringGetCharactersPtr(valueAsString); 104 105 CFStringRef expectedValueAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, 106 expectedValue, 107 kCFStringEncodingUTF8); 108 CFIndex cfLength = CFStringGetLength(expectedValueAsCFString); 109 UniChar* cfBuffer = (UniChar*)malloc(cfLength * sizeof(UniChar)); 110 CFStringGetCharacters(expectedValueAsCFString, CFRangeMake(0, cfLength), cfBuffer); 111 CFRelease(expectedValueAsCFString); 112 113 if (memcmp(jsBuffer, cfBuffer, cfLength * sizeof(UniChar)) != 0) { 114 fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsBuffer != cfBuffer\n"); 115 failed = 1; 116 } 117 118 if (jsLength != (size_t)cfLength) { 119 fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsLength(%ld) != cfLength(%ld)\n", jsLength, cfLength); 120 failed = 1; 121 } 122 123 free(cfBuffer); 124 JSStringRelease(valueAsString); 125 } 126 127 static bool timeZoneIsPST() 128 { 129 char timeZoneName[70]; 130 struct tm gtm; 131 memset(>m, 0, sizeof(gtm)); 132 strftime(timeZoneName, sizeof(timeZoneName), "%Z", >m); 133 134 return 0 == strcmp("PST", timeZoneName); 135 } 136 137 static JSValueRef jsGlobalValue; // non-stack value for testing JSValueProtect() 138 139 /* MyObject pseudo-class */ 140 141 static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName) 142 { 143 UNUSED_PARAM(context); 144 UNUSED_PARAM(object); 145 146 if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne") 147 || JSStringIsEqualToUTF8CString(propertyName, "cantFind") 148 || JSStringIsEqualToUTF8CString(propertyName, "throwOnGet") 149 || JSStringIsEqualToUTF8CString(propertyName, "myPropertyName") 150 || JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie") 151 || JSStringIsEqualToUTF8CString(propertyName, "0")) { 152 return true; 153 } 154 155 return false; 156 } 157 158 static JSValueRef MyObject_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) 159 { 160 UNUSED_PARAM(context); 161 UNUSED_PARAM(object); 162 163 if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")) { 164 return JSValueMakeNumber(context, 1); 165 } 166 167 if (JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")) { 168 return JSValueMakeNumber(context, 1); 169 } 170 171 if (JSStringIsEqualToUTF8CString(propertyName, "cantFind")) { 172 return JSValueMakeUndefined(context); 173 } 174 175 if (JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie")) { 176 return 0; 177 } 178 179 if (JSStringIsEqualToUTF8CString(propertyName, "throwOnGet")) { 180 return JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception); 181 } 182 183 if (JSStringIsEqualToUTF8CString(propertyName, "0")) { 184 *exception = JSValueMakeNumber(context, 1); 185 return JSValueMakeNumber(context, 1); 186 } 187 188 return JSValueMakeNull(context); 189 } 190 191 static bool MyObject_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) 192 { 193 UNUSED_PARAM(context); 194 UNUSED_PARAM(object); 195 UNUSED_PARAM(value); 196 UNUSED_PARAM(exception); 197 198 if (JSStringIsEqualToUTF8CString(propertyName, "cantSet")) 199 return true; // pretend we set the property in order to swallow it 200 201 if (JSStringIsEqualToUTF8CString(propertyName, "throwOnSet")) { 202 JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception); 203 } 204 205 return false; 206 } 207 208 static bool MyObject_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) 209 { 210 UNUSED_PARAM(context); 211 UNUSED_PARAM(object); 212 213 if (JSStringIsEqualToUTF8CString(propertyName, "cantDelete")) 214 return true; 215 216 if (JSStringIsEqualToUTF8CString(propertyName, "throwOnDelete")) { 217 JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception); 218 return false; 219 } 220 221 return false; 222 } 223 224 static void MyObject_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames) 225 { 226 UNUSED_PARAM(context); 227 UNUSED_PARAM(object); 228 229 JSStringRef propertyName; 230 231 propertyName = JSStringCreateWithUTF8CString("alwaysOne"); 232 JSPropertyNameAccumulatorAddName(propertyNames, propertyName); 233 JSStringRelease(propertyName); 234 235 propertyName = JSStringCreateWithUTF8CString("myPropertyName"); 236 JSPropertyNameAccumulatorAddName(propertyNames, propertyName); 237 JSStringRelease(propertyName); 238 } 239 240 static JSValueRef MyObject_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 241 { 242 UNUSED_PARAM(context); 243 UNUSED_PARAM(object); 244 UNUSED_PARAM(thisObject); 245 UNUSED_PARAM(exception); 246 247 if (argumentCount > 0 && JSValueIsString(context, arguments[0]) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, arguments[0], 0), "throwOnCall")) { 248 JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception); 249 return JSValueMakeUndefined(context); 250 } 251 252 if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0))) 253 return JSValueMakeNumber(context, 1); 254 255 return JSValueMakeUndefined(context); 256 } 257 258 static JSObjectRef MyObject_callAsConstructor(JSContextRef context, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 259 { 260 UNUSED_PARAM(context); 261 UNUSED_PARAM(object); 262 263 if (argumentCount > 0 && JSValueIsString(context, arguments[0]) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, arguments[0], 0), "throwOnConstruct")) { 264 JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception); 265 return object; 266 } 267 268 if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0))) 269 return JSValueToObject(context, JSValueMakeNumber(context, 1), exception); 270 271 return JSValueToObject(context, JSValueMakeNumber(context, 0), exception); 272 } 273 274 static bool MyObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception) 275 { 276 UNUSED_PARAM(context); 277 UNUSED_PARAM(constructor); 278 279 if (JSValueIsString(context, possibleValue) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, possibleValue, 0), "throwOnHasInstance")) { 280 JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), constructor, JSStringCreateWithUTF8CString("test script"), 1, exception); 281 return false; 282 } 283 284 JSStringRef numberString = JSStringCreateWithUTF8CString("Number"); 285 JSObjectRef numberConstructor = JSValueToObject(context, JSObjectGetProperty(context, JSContextGetGlobalObject(context), numberString, exception), exception); 286 JSStringRelease(numberString); 287 288 return JSValueIsInstanceOfConstructor(context, possibleValue, numberConstructor, exception); 289 } 290 291 static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception) 292 { 293 UNUSED_PARAM(object); 294 UNUSED_PARAM(exception); 295 296 switch (type) { 297 case kJSTypeNumber: 298 return JSValueMakeNumber(context, 1); 299 case kJSTypeString: 300 { 301 JSStringRef string = JSStringCreateWithUTF8CString("MyObjectAsString"); 302 JSValueRef result = JSValueMakeString(context, string); 303 JSStringRelease(string); 304 return result; 305 } 306 default: 307 break; 308 } 309 310 // string conversion -- forward to default object class 311 return JSValueMakeNull(context); 312 } 313 314 static JSStaticValue evilStaticValues[] = { 315 { "nullGetSet", 0, 0, kJSPropertyAttributeNone }, 316 { 0, 0, 0, 0 } 317 }; 318 319 static JSStaticFunction evilStaticFunctions[] = { 320 { "nullCall", 0, kJSPropertyAttributeNone }, 321 { 0, 0, 0 } 322 }; 323 324 JSClassDefinition MyObject_definition = { 325 0, 326 kJSClassAttributeNone, 327 328 "MyObject", 329 NULL, 330 331 evilStaticValues, 332 evilStaticFunctions, 333 334 NULL, 335 NULL, 336 MyObject_hasProperty, 337 MyObject_getProperty, 338 MyObject_setProperty, 339 MyObject_deleteProperty, 340 MyObject_getPropertyNames, 341 MyObject_callAsFunction, 342 MyObject_callAsConstructor, 343 MyObject_hasInstance, 344 MyObject_convertToType, 345 }; 346 347 static JSClassRef MyObject_class(JSContextRef context) 348 { 349 UNUSED_PARAM(context); 350 351 static JSClassRef jsClass; 352 if (!jsClass) 353 jsClass = JSClassCreate(&MyObject_definition); 354 355 return jsClass; 356 } 357 358 static bool EvilExceptionObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception) 359 { 360 UNUSED_PARAM(context); 361 UNUSED_PARAM(constructor); 362 363 JSStringRef hasInstanceName = JSStringCreateWithUTF8CString("hasInstance"); 364 JSValueRef hasInstance = JSObjectGetProperty(context, constructor, hasInstanceName, exception); 365 JSStringRelease(hasInstanceName); 366 if (!hasInstance) 367 return false; 368 JSObjectRef function = JSValueToObject(context, hasInstance, exception); 369 JSValueRef result = JSObjectCallAsFunction(context, function, constructor, 1, &possibleValue, exception); 370 return result && JSValueToBoolean(context, result); 371 } 372 373 static JSValueRef EvilExceptionObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception) 374 { 375 UNUSED_PARAM(object); 376 UNUSED_PARAM(exception); 377 JSStringRef funcName; 378 switch (type) { 379 case kJSTypeNumber: 380 funcName = JSStringCreateWithUTF8CString("toNumber"); 381 break; 382 case kJSTypeString: 383 funcName = JSStringCreateWithUTF8CString("toStringExplicit"); 384 break; 385 default: 386 return JSValueMakeNull(context); 387 break; 388 } 389 390 JSValueRef func = JSObjectGetProperty(context, object, funcName, exception); 391 JSStringRelease(funcName); 392 JSObjectRef function = JSValueToObject(context, func, exception); 393 if (!function) 394 return JSValueMakeNull(context); 395 JSValueRef value = JSObjectCallAsFunction(context, function, object, 0, NULL, exception); 396 if (!value) { 397 JSStringRef errorString = JSStringCreateWithUTF8CString("convertToType failed"); 398 JSValueRef errorStringRef = JSValueMakeString(context, errorString); 399 JSStringRelease(errorString); 400 return errorStringRef; 401 } 402 return value; 403 } 404 405 JSClassDefinition EvilExceptionObject_definition = { 406 0, 407 kJSClassAttributeNone, 408 409 "EvilExceptionObject", 410 NULL, 411 412 NULL, 413 NULL, 414 415 NULL, 416 NULL, 417 NULL, 418 NULL, 419 NULL, 420 NULL, 421 NULL, 422 NULL, 423 NULL, 424 EvilExceptionObject_hasInstance, 425 EvilExceptionObject_convertToType, 426 }; 427 428 static JSClassRef EvilExceptionObject_class(JSContextRef context) 429 { 430 UNUSED_PARAM(context); 431 432 static JSClassRef jsClass; 433 if (!jsClass) 434 jsClass = JSClassCreate(&EvilExceptionObject_definition); 435 436 return jsClass; 437 } 438 439 JSClassDefinition EmptyObject_definition = { 440 0, 441 kJSClassAttributeNone, 442 443 NULL, 444 NULL, 445 446 NULL, 447 NULL, 448 449 NULL, 450 NULL, 451 NULL, 452 NULL, 453 NULL, 454 NULL, 455 NULL, 456 NULL, 457 NULL, 458 NULL, 459 NULL, 460 }; 461 462 static JSClassRef EmptyObject_class(JSContextRef context) 463 { 464 UNUSED_PARAM(context); 465 466 static JSClassRef jsClass; 467 if (!jsClass) 468 jsClass = JSClassCreate(&EmptyObject_definition); 469 470 return jsClass; 471 } 472 473 474 static JSValueRef Base_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) 475 { 476 UNUSED_PARAM(object); 477 UNUSED_PARAM(propertyName); 478 UNUSED_PARAM(exception); 479 480 return JSValueMakeNumber(ctx, 1); // distinguish base get form derived get 481 } 482 483 static bool Base_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) 484 { 485 UNUSED_PARAM(object); 486 UNUSED_PARAM(propertyName); 487 UNUSED_PARAM(value); 488 489 *exception = JSValueMakeNumber(ctx, 1); // distinguish base set from derived set 490 return true; 491 } 492 493 static JSValueRef Base_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 494 { 495 UNUSED_PARAM(function); 496 UNUSED_PARAM(thisObject); 497 UNUSED_PARAM(argumentCount); 498 UNUSED_PARAM(arguments); 499 UNUSED_PARAM(exception); 500 501 return JSValueMakeNumber(ctx, 1); // distinguish base call from derived call 502 } 503 504 static JSStaticFunction Base_staticFunctions[] = { 505 { "baseProtoDup", NULL, kJSPropertyAttributeNone }, 506 { "baseProto", Base_callAsFunction, kJSPropertyAttributeNone }, 507 { 0, 0, 0 } 508 }; 509 510 static JSStaticValue Base_staticValues[] = { 511 { "baseDup", Base_get, Base_set, kJSPropertyAttributeNone }, 512 { "baseOnly", Base_get, Base_set, kJSPropertyAttributeNone }, 513 { 0, 0, 0, 0 } 514 }; 515 516 static bool TestInitializeFinalize; 517 static void Base_initialize(JSContextRef context, JSObjectRef object) 518 { 519 UNUSED_PARAM(context); 520 521 if (TestInitializeFinalize) { 522 ASSERT((void*)1 == JSObjectGetPrivate(object)); 523 JSObjectSetPrivate(object, (void*)2); 524 } 525 } 526 527 static unsigned Base_didFinalize; 528 static void Base_finalize(JSObjectRef object) 529 { 530 UNUSED_PARAM(object); 531 if (TestInitializeFinalize) { 532 ASSERT((void*)4 == JSObjectGetPrivate(object)); 533 Base_didFinalize = true; 534 } 535 } 536 537 static JSClassRef Base_class(JSContextRef context) 538 { 539 UNUSED_PARAM(context); 540 541 static JSClassRef jsClass; 542 if (!jsClass) { 543 JSClassDefinition definition = kJSClassDefinitionEmpty; 544 definition.staticValues = Base_staticValues; 545 definition.staticFunctions = Base_staticFunctions; 546 definition.initialize = Base_initialize; 547 definition.finalize = Base_finalize; 548 jsClass = JSClassCreate(&definition); 549 } 550 return jsClass; 551 } 552 553 static JSValueRef Derived_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) 554 { 555 UNUSED_PARAM(object); 556 UNUSED_PARAM(propertyName); 557 UNUSED_PARAM(exception); 558 559 return JSValueMakeNumber(ctx, 2); // distinguish base get form derived get 560 } 561 562 static bool Derived_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) 563 { 564 UNUSED_PARAM(ctx); 565 UNUSED_PARAM(object); 566 UNUSED_PARAM(propertyName); 567 UNUSED_PARAM(value); 568 569 *exception = JSValueMakeNumber(ctx, 2); // distinguish base set from derived set 570 return true; 571 } 572 573 static JSValueRef Derived_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 574 { 575 UNUSED_PARAM(function); 576 UNUSED_PARAM(thisObject); 577 UNUSED_PARAM(argumentCount); 578 UNUSED_PARAM(arguments); 579 UNUSED_PARAM(exception); 580 581 return JSValueMakeNumber(ctx, 2); // distinguish base call from derived call 582 } 583 584 static JSStaticFunction Derived_staticFunctions[] = { 585 { "protoOnly", Derived_callAsFunction, kJSPropertyAttributeNone }, 586 { "protoDup", NULL, kJSPropertyAttributeNone }, 587 { "baseProtoDup", Derived_callAsFunction, kJSPropertyAttributeNone }, 588 { 0, 0, 0 } 589 }; 590 591 static JSStaticValue Derived_staticValues[] = { 592 { "derivedOnly", Derived_get, Derived_set, kJSPropertyAttributeNone }, 593 { "protoDup", Derived_get, Derived_set, kJSPropertyAttributeNone }, 594 { "baseDup", Derived_get, Derived_set, kJSPropertyAttributeNone }, 595 { 0, 0, 0, 0 } 596 }; 597 598 static void Derived_initialize(JSContextRef context, JSObjectRef object) 599 { 600 UNUSED_PARAM(context); 601 602 if (TestInitializeFinalize) { 603 ASSERT((void*)2 == JSObjectGetPrivate(object)); 604 JSObjectSetPrivate(object, (void*)3); 605 } 606 } 607 608 static void Derived_finalize(JSObjectRef object) 609 { 610 if (TestInitializeFinalize) { 611 ASSERT((void*)3 == JSObjectGetPrivate(object)); 612 JSObjectSetPrivate(object, (void*)4); 613 } 614 } 615 616 static JSClassRef Derived_class(JSContextRef context) 617 { 618 static JSClassRef jsClass; 619 if (!jsClass) { 620 JSClassDefinition definition = kJSClassDefinitionEmpty; 621 definition.parentClass = Base_class(context); 622 definition.staticValues = Derived_staticValues; 623 definition.staticFunctions = Derived_staticFunctions; 624 definition.initialize = Derived_initialize; 625 definition.finalize = Derived_finalize; 626 jsClass = JSClassCreate(&definition); 627 } 628 return jsClass; 629 } 630 631 static JSClassRef Derived2_class(JSContextRef context) 632 { 633 static JSClassRef jsClass; 634 if (!jsClass) { 635 JSClassDefinition definition = kJSClassDefinitionEmpty; 636 definition.parentClass = Derived_class(context); 637 jsClass = JSClassCreate(&definition); 638 } 639 return jsClass; 640 } 641 642 static JSValueRef print_callAsFunction(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 643 { 644 UNUSED_PARAM(functionObject); 645 UNUSED_PARAM(thisObject); 646 UNUSED_PARAM(exception); 647 648 ASSERT(JSContextGetGlobalContext(ctx) == context); 649 650 if (argumentCount > 0) { 651 JSStringRef string = JSValueToStringCopy(ctx, arguments[0], NULL); 652 size_t sizeUTF8 = JSStringGetMaximumUTF8CStringSize(string); 653 char* stringUTF8 = (char*)malloc(sizeUTF8); 654 JSStringGetUTF8CString(string, stringUTF8, sizeUTF8); 655 printf("%s\n", stringUTF8); 656 free(stringUTF8); 657 JSStringRelease(string); 658 } 659 660 return JSValueMakeUndefined(ctx); 661 } 662 663 static JSObjectRef myConstructor_callAsConstructor(JSContextRef context, JSObjectRef constructorObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 664 { 665 UNUSED_PARAM(constructorObject); 666 UNUSED_PARAM(exception); 667 668 JSObjectRef result = JSObjectMake(context, NULL, NULL); 669 if (argumentCount > 0) { 670 JSStringRef value = JSStringCreateWithUTF8CString("value"); 671 JSObjectSetProperty(context, result, value, arguments[0], kJSPropertyAttributeNone, NULL); 672 JSStringRelease(value); 673 } 674 675 return result; 676 } 677 678 679 static void globalObject_initialize(JSContextRef context, JSObjectRef object) 680 { 681 UNUSED_PARAM(object); 682 // Ensure that an execution context is passed in 683 ASSERT(context); 684 685 // Ensure that the global object is set to the object that we were passed 686 JSObjectRef globalObject = JSContextGetGlobalObject(context); 687 ASSERT(globalObject); 688 ASSERT(object == globalObject); 689 690 // Ensure that the standard global properties have been set on the global object 691 JSStringRef array = JSStringCreateWithUTF8CString("Array"); 692 JSObjectRef arrayConstructor = JSValueToObject(context, JSObjectGetProperty(context, globalObject, array, NULL), NULL); 693 JSStringRelease(array); 694 695 UNUSED_PARAM(arrayConstructor); 696 ASSERT(arrayConstructor); 697 } 698 699 static JSValueRef globalObject_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) 700 { 701 UNUSED_PARAM(object); 702 UNUSED_PARAM(propertyName); 703 UNUSED_PARAM(exception); 704 705 return JSValueMakeNumber(ctx, 3); 706 } 707 708 static bool globalObject_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) 709 { 710 UNUSED_PARAM(object); 711 UNUSED_PARAM(propertyName); 712 UNUSED_PARAM(value); 713 714 *exception = JSValueMakeNumber(ctx, 3); 715 return true; 716 } 717 718 static JSValueRef globalObject_call(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 719 { 720 UNUSED_PARAM(function); 721 UNUSED_PARAM(thisObject); 722 UNUSED_PARAM(argumentCount); 723 UNUSED_PARAM(arguments); 724 UNUSED_PARAM(exception); 725 726 return JSValueMakeNumber(ctx, 3); 727 } 728 729 static JSValueRef functionGC(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 730 { 731 UNUSED_PARAM(function); 732 UNUSED_PARAM(thisObject); 733 UNUSED_PARAM(argumentCount); 734 UNUSED_PARAM(arguments); 735 UNUSED_PARAM(exception); 736 JSGarbageCollect(context); 737 return JSValueMakeUndefined(context); 738 } 739 740 static JSStaticValue globalObject_staticValues[] = { 741 { "globalStaticValue", globalObject_get, globalObject_set, kJSPropertyAttributeNone }, 742 { 0, 0, 0, 0 } 743 }; 744 745 static JSStaticFunction globalObject_staticFunctions[] = { 746 { "globalStaticFunction", globalObject_call, kJSPropertyAttributeNone }, 747 { "gc", functionGC, kJSPropertyAttributeNone }, 748 { 0, 0, 0 } 749 }; 750 751 static char* createStringWithContentsOfFile(const char* fileName); 752 753 static void testInitializeFinalize() 754 { 755 JSObjectRef o = JSObjectMake(context, Derived_class(context), (void*)1); 756 UNUSED_PARAM(o); 757 ASSERT(JSObjectGetPrivate(o) == (void*)3); 758 } 759 760 static JSValueRef jsNumberValue = NULL; 761 762 static JSObjectRef aHeapRef = NULL; 763 764 static void makeGlobalNumberValue(JSContextRef context) { 765 JSValueRef v = JSValueMakeNumber(context, 420); 766 JSValueProtect(context, v); 767 jsNumberValue = v; 768 v = NULL; 769 } 770 771 static bool assertTrue(bool value, const char* message) 772 { 773 if (!value) { 774 if (message) 775 fprintf(stderr, "assertTrue failed: '%s'\n", message); 776 else 777 fprintf(stderr, "assertTrue failed.\n"); 778 failed = 1; 779 } 780 return value; 781 } 782 783 static bool checkForCycleInPrototypeChain() 784 { 785 bool result = true; 786 JSGlobalContextRef context = JSGlobalContextCreate(0); 787 JSObjectRef object1 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0); 788 JSObjectRef object2 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0); 789 JSObjectRef object3 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0); 790 791 JSObjectSetPrototype(context, object1, JSValueMakeNull(context)); 792 ASSERT(JSValueIsNull(context, JSObjectGetPrototype(context, object1))); 793 794 // object1 -> object1 795 JSObjectSetPrototype(context, object1, object1); 796 result &= assertTrue(JSValueIsNull(context, JSObjectGetPrototype(context, object1)), "It is possible to assign self as a prototype"); 797 798 // object1 -> object2 -> object1 799 JSObjectSetPrototype(context, object2, object1); 800 ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object2), object1)); 801 JSObjectSetPrototype(context, object1, object2); 802 result &= assertTrue(JSValueIsNull(context, JSObjectGetPrototype(context, object1)), "It is possible to close a prototype chain cycle"); 803 804 // object1 -> object2 -> object3 -> object1 805 JSObjectSetPrototype(context, object2, object3); 806 ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object2), object3)); 807 JSObjectSetPrototype(context, object1, object2); 808 ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object1), object2)); 809 JSObjectSetPrototype(context, object3, object1); 810 result &= assertTrue(!JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object3), object1), "It is possible to close a prototype chain cycle"); 811 812 JSValueRef exception; 813 JSStringRef code = JSStringCreateWithUTF8CString("o = { }; p = { }; o.__proto__ = p; p.__proto__ = o"); 814 JSStringRef file = JSStringCreateWithUTF8CString(""); 815 result &= assertTrue(!JSEvaluateScript(context, code, /* thisObject*/ 0, file, 1, &exception) 816 , "An exception should be thrown"); 817 818 JSStringRelease(code); 819 JSStringRelease(file); 820 JSGlobalContextRelease(context); 821 return result; 822 } 823 824 int main(int argc, char* argv[]) 825 { 826 #if OS(WINDOWS) 827 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for 828 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the 829 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>. 830 ::SetErrorMode(0); 831 #endif 832 833 const char *scriptPath = "testapi.js"; 834 if (argc > 1) { 835 scriptPath = argv[1]; 836 } 837 838 // Test garbage collection with a fresh context 839 context = JSGlobalContextCreateInGroup(NULL, NULL); 840 TestInitializeFinalize = true; 841 testInitializeFinalize(); 842 JSGlobalContextRelease(context); 843 TestInitializeFinalize = false; 844 845 ASSERT(Base_didFinalize); 846 847 JSClassDefinition globalObjectClassDefinition = kJSClassDefinitionEmpty; 848 globalObjectClassDefinition.initialize = globalObject_initialize; 849 globalObjectClassDefinition.staticValues = globalObject_staticValues; 850 globalObjectClassDefinition.staticFunctions = globalObject_staticFunctions; 851 globalObjectClassDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; 852 JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition); 853 context = JSGlobalContextCreateInGroup(NULL, globalObjectClass); 854 855 JSGlobalContextRetain(context); 856 JSGlobalContextRelease(context); 857 ASSERT(JSContextGetGlobalContext(context) == context); 858 859 JSReportExtraMemoryCost(context, 0); 860 JSReportExtraMemoryCost(context, 1); 861 JSReportExtraMemoryCost(context, 1024); 862 863 JSObjectRef globalObject = JSContextGetGlobalObject(context); 864 ASSERT(JSValueIsObject(context, globalObject)); 865 866 JSValueRef jsUndefined = JSValueMakeUndefined(context); 867 JSValueRef jsNull = JSValueMakeNull(context); 868 JSValueRef jsTrue = JSValueMakeBoolean(context, true); 869 JSValueRef jsFalse = JSValueMakeBoolean(context, false); 870 JSValueRef jsZero = JSValueMakeNumber(context, 0); 871 JSValueRef jsOne = JSValueMakeNumber(context, 1); 872 JSValueRef jsOneThird = JSValueMakeNumber(context, 1.0 / 3.0); 873 JSObjectRef jsObjectNoProto = JSObjectMake(context, NULL, NULL); 874 JSObjectSetPrototype(context, jsObjectNoProto, JSValueMakeNull(context)); 875 876 // FIXME: test funny utf8 characters 877 JSStringRef jsEmptyIString = JSStringCreateWithUTF8CString(""); 878 JSValueRef jsEmptyString = JSValueMakeString(context, jsEmptyIString); 879 880 JSStringRef jsOneIString = JSStringCreateWithUTF8CString("1"); 881 JSValueRef jsOneString = JSValueMakeString(context, jsOneIString); 882 883 UniChar singleUniChar = 65; // Capital A 884 CFMutableStringRef cfString = 885 CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault, 886 &singleUniChar, 887 1, 888 1, 889 kCFAllocatorNull); 890 891 JSStringRef jsCFIString = JSStringCreateWithCFString(cfString); 892 JSValueRef jsCFString = JSValueMakeString(context, jsCFIString); 893 894 CFStringRef cfEmptyString = CFStringCreateWithCString(kCFAllocatorDefault, "", kCFStringEncodingUTF8); 895 896 JSStringRef jsCFEmptyIString = JSStringCreateWithCFString(cfEmptyString); 897 JSValueRef jsCFEmptyString = JSValueMakeString(context, jsCFEmptyIString); 898 899 CFIndex cfStringLength = CFStringGetLength(cfString); 900 UniChar* buffer = (UniChar*)malloc(cfStringLength * sizeof(UniChar)); 901 CFStringGetCharacters(cfString, 902 CFRangeMake(0, cfStringLength), 903 buffer); 904 JSStringRef jsCFIStringWithCharacters = JSStringCreateWithCharacters((JSChar*)buffer, cfStringLength); 905 JSValueRef jsCFStringWithCharacters = JSValueMakeString(context, jsCFIStringWithCharacters); 906 907 JSStringRef jsCFEmptyIStringWithCharacters = JSStringCreateWithCharacters((JSChar*)buffer, CFStringGetLength(cfEmptyString)); 908 free(buffer); 909 JSValueRef jsCFEmptyStringWithCharacters = JSValueMakeString(context, jsCFEmptyIStringWithCharacters); 910 911 ASSERT(JSValueGetType(context, jsUndefined) == kJSTypeUndefined); 912 ASSERT(JSValueGetType(context, jsNull) == kJSTypeNull); 913 ASSERT(JSValueGetType(context, jsTrue) == kJSTypeBoolean); 914 ASSERT(JSValueGetType(context, jsFalse) == kJSTypeBoolean); 915 ASSERT(JSValueGetType(context, jsZero) == kJSTypeNumber); 916 ASSERT(JSValueGetType(context, jsOne) == kJSTypeNumber); 917 ASSERT(JSValueGetType(context, jsOneThird) == kJSTypeNumber); 918 ASSERT(JSValueGetType(context, jsEmptyString) == kJSTypeString); 919 ASSERT(JSValueGetType(context, jsOneString) == kJSTypeString); 920 ASSERT(JSValueGetType(context, jsCFString) == kJSTypeString); 921 ASSERT(JSValueGetType(context, jsCFStringWithCharacters) == kJSTypeString); 922 ASSERT(JSValueGetType(context, jsCFEmptyString) == kJSTypeString); 923 ASSERT(JSValueGetType(context, jsCFEmptyStringWithCharacters) == kJSTypeString); 924 925 JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL); 926 JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject"); 927 JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone, NULL); 928 JSStringRelease(myObjectIString); 929 930 JSObjectRef EvilExceptionObject = JSObjectMake(context, EvilExceptionObject_class(context), NULL); 931 JSStringRef EvilExceptionObjectIString = JSStringCreateWithUTF8CString("EvilExceptionObject"); 932 JSObjectSetProperty(context, globalObject, EvilExceptionObjectIString, EvilExceptionObject, kJSPropertyAttributeNone, NULL); 933 JSStringRelease(EvilExceptionObjectIString); 934 935 JSObjectRef EmptyObject = JSObjectMake(context, EmptyObject_class(context), NULL); 936 JSStringRef EmptyObjectIString = JSStringCreateWithUTF8CString("EmptyObject"); 937 JSObjectSetProperty(context, globalObject, EmptyObjectIString, EmptyObject, kJSPropertyAttributeNone, NULL); 938 JSStringRelease(EmptyObjectIString); 939 940 JSStringRef lengthStr = JSStringCreateWithUTF8CString("length"); 941 JSObjectRef aStackRef = JSObjectMakeArray(context, 0, 0, 0); 942 aHeapRef = aStackRef; 943 JSObjectSetProperty(context, aHeapRef, lengthStr, JSValueMakeNumber(context, 10), 0, 0); 944 JSStringRef privatePropertyName = JSStringCreateWithUTF8CString("privateProperty"); 945 if (!JSObjectSetPrivateProperty(context, myObject, privatePropertyName, aHeapRef)) { 946 printf("FAIL: Could not set private property.\n"); 947 failed = 1; 948 } else 949 printf("PASS: Set private property.\n"); 950 aStackRef = 0; 951 if (JSObjectSetPrivateProperty(context, aHeapRef, privatePropertyName, aHeapRef)) { 952 printf("FAIL: JSObjectSetPrivateProperty should fail on non-API objects.\n"); 953 failed = 1; 954 } else 955 printf("PASS: Did not allow JSObjectSetPrivateProperty on a non-API object.\n"); 956 if (JSObjectGetPrivateProperty(context, myObject, privatePropertyName) != aHeapRef) { 957 printf("FAIL: Could not retrieve private property.\n"); 958 failed = 1; 959 } else 960 printf("PASS: Retrieved private property.\n"); 961 if (JSObjectGetPrivateProperty(context, aHeapRef, privatePropertyName)) { 962 printf("FAIL: JSObjectGetPrivateProperty should return NULL when called on a non-API object.\n"); 963 failed = 1; 964 } else 965 printf("PASS: JSObjectGetPrivateProperty return NULL.\n"); 966 967 if (JSObjectGetProperty(context, myObject, privatePropertyName, 0) == aHeapRef) { 968 printf("FAIL: Accessed private property through ordinary property lookup.\n"); 969 failed = 1; 970 } else 971 printf("PASS: Cannot access private property through ordinary property lookup.\n"); 972 973 JSGarbageCollect(context); 974 975 for (int i = 0; i < 10000; i++) 976 JSObjectMake(context, 0, 0); 977 978 aHeapRef = JSValueToObject(context, JSObjectGetPrivateProperty(context, myObject, privatePropertyName), 0); 979 if (JSValueToNumber(context, JSObjectGetProperty(context, aHeapRef, lengthStr, 0), 0) != 10) { 980 printf("FAIL: Private property has been collected.\n"); 981 failed = 1; 982 } else 983 printf("PASS: Private property does not appear to have been collected.\n"); 984 JSStringRelease(lengthStr); 985 986 if (!JSObjectSetPrivateProperty(context, myObject, privatePropertyName, 0)) { 987 printf("FAIL: Could not set private property to NULL.\n"); 988 failed = 1; 989 } else 990 printf("PASS: Set private property to NULL.\n"); 991 if (JSObjectGetPrivateProperty(context, myObject, privatePropertyName)) { 992 printf("FAIL: Could not retrieve private property.\n"); 993 failed = 1; 994 } else 995 printf("PASS: Retrieved private property.\n"); 996 997 JSStringRef validJSON = JSStringCreateWithUTF8CString("{\"aProperty\":true}"); 998 JSValueRef jsonObject = JSValueMakeFromJSONString(context, validJSON); 999 JSStringRelease(validJSON); 1000 if (!JSValueIsObject(context, jsonObject)) { 1001 printf("FAIL: Did not parse valid JSON correctly\n"); 1002 failed = 1; 1003 } else 1004 printf("PASS: Parsed valid JSON string.\n"); 1005 JSStringRef propertyName = JSStringCreateWithUTF8CString("aProperty"); 1006 assertEqualsAsBoolean(JSObjectGetProperty(context, JSValueToObject(context, jsonObject, 0), propertyName, 0), true); 1007 JSStringRelease(propertyName); 1008 JSStringRef invalidJSON = JSStringCreateWithUTF8CString("fail!"); 1009 if (JSValueMakeFromJSONString(context, invalidJSON)) { 1010 printf("FAIL: Should return null for invalid JSON data\n"); 1011 failed = 1; 1012 } else 1013 printf("PASS: Correctly returned null for invalid JSON data.\n"); 1014 JSValueRef exception; 1015 JSStringRef str = JSValueCreateJSONString(context, jsonObject, 0, 0); 1016 if (!JSStringIsEqualToUTF8CString(str, "{\"aProperty\":true}")) { 1017 printf("FAIL: Did not correctly serialise with indent of 0.\n"); 1018 failed = 1; 1019 } else 1020 printf("PASS: Correctly serialised with indent of 0.\n"); 1021 JSStringRelease(str); 1022 1023 str = JSValueCreateJSONString(context, jsonObject, 4, 0); 1024 if (!JSStringIsEqualToUTF8CString(str, "{\n \"aProperty\": true\n}")) { 1025 printf("FAIL: Did not correctly serialise with indent of 4.\n"); 1026 failed = 1; 1027 } else 1028 printf("PASS: Correctly serialised with indent of 4.\n"); 1029 JSStringRelease(str); 1030 JSStringRef src = JSStringCreateWithUTF8CString("({get a(){ throw '';}})"); 1031 JSValueRef unstringifiableObj = JSEvaluateScript(context, src, NULL, NULL, 1, NULL); 1032 1033 str = JSValueCreateJSONString(context, unstringifiableObj, 4, 0); 1034 if (str) { 1035 printf("FAIL: Didn't return null when attempting to serialize unserializable value.\n"); 1036 JSStringRelease(str); 1037 failed = 1; 1038 } else 1039 printf("PASS: returned null when attempting to serialize unserializable value.\n"); 1040 1041 str = JSValueCreateJSONString(context, unstringifiableObj, 4, &exception); 1042 if (str) { 1043 printf("FAIL: Didn't return null when attempting to serialize unserializable value.\n"); 1044 JSStringRelease(str); 1045 failed = 1; 1046 } else 1047 printf("PASS: returned null when attempting to serialize unserializable value.\n"); 1048 if (!exception) { 1049 printf("FAIL: Did not set exception on serialisation error\n"); 1050 failed = 1; 1051 } else 1052 printf("PASS: set exception on serialisation error\n"); 1053 // Conversions that throw exceptions 1054 exception = NULL; 1055 ASSERT(NULL == JSValueToObject(context, jsNull, &exception)); 1056 ASSERT(exception); 1057 1058 exception = NULL; 1059 // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function, 1060 // causing a build break with -Wshorten-64-to-32 enabled. The issue is known by the appropriate team. 1061 // After that's resolved, we can remove these casts 1062 ASSERT(isnan((float)JSValueToNumber(context, jsObjectNoProto, &exception))); 1063 ASSERT(exception); 1064 1065 exception = NULL; 1066 ASSERT(!JSValueToStringCopy(context, jsObjectNoProto, &exception)); 1067 ASSERT(exception); 1068 1069 ASSERT(JSValueToBoolean(context, myObject)); 1070 1071 exception = NULL; 1072 ASSERT(!JSValueIsEqual(context, jsObjectNoProto, JSValueMakeNumber(context, 1), &exception)); 1073 ASSERT(exception); 1074 1075 exception = NULL; 1076 JSObjectGetPropertyAtIndex(context, myObject, 0, &exception); 1077 ASSERT(1 == JSValueToNumber(context, exception, NULL)); 1078 1079 assertEqualsAsBoolean(jsUndefined, false); 1080 assertEqualsAsBoolean(jsNull, false); 1081 assertEqualsAsBoolean(jsTrue, true); 1082 assertEqualsAsBoolean(jsFalse, false); 1083 assertEqualsAsBoolean(jsZero, false); 1084 assertEqualsAsBoolean(jsOne, true); 1085 assertEqualsAsBoolean(jsOneThird, true); 1086 assertEqualsAsBoolean(jsEmptyString, false); 1087 assertEqualsAsBoolean(jsOneString, true); 1088 assertEqualsAsBoolean(jsCFString, true); 1089 assertEqualsAsBoolean(jsCFStringWithCharacters, true); 1090 assertEqualsAsBoolean(jsCFEmptyString, false); 1091 assertEqualsAsBoolean(jsCFEmptyStringWithCharacters, false); 1092 1093 assertEqualsAsNumber(jsUndefined, nan("")); 1094 assertEqualsAsNumber(jsNull, 0); 1095 assertEqualsAsNumber(jsTrue, 1); 1096 assertEqualsAsNumber(jsFalse, 0); 1097 assertEqualsAsNumber(jsZero, 0); 1098 assertEqualsAsNumber(jsOne, 1); 1099 assertEqualsAsNumber(jsOneThird, 1.0 / 3.0); 1100 assertEqualsAsNumber(jsEmptyString, 0); 1101 assertEqualsAsNumber(jsOneString, 1); 1102 assertEqualsAsNumber(jsCFString, nan("")); 1103 assertEqualsAsNumber(jsCFStringWithCharacters, nan("")); 1104 assertEqualsAsNumber(jsCFEmptyString, 0); 1105 assertEqualsAsNumber(jsCFEmptyStringWithCharacters, 0); 1106 ASSERT(sizeof(JSChar) == sizeof(UniChar)); 1107 1108 assertEqualsAsCharactersPtr(jsUndefined, "undefined"); 1109 assertEqualsAsCharactersPtr(jsNull, "null"); 1110 assertEqualsAsCharactersPtr(jsTrue, "true"); 1111 assertEqualsAsCharactersPtr(jsFalse, "false"); 1112 assertEqualsAsCharactersPtr(jsZero, "0"); 1113 assertEqualsAsCharactersPtr(jsOne, "1"); 1114 assertEqualsAsCharactersPtr(jsOneThird, "0.3333333333333333"); 1115 assertEqualsAsCharactersPtr(jsEmptyString, ""); 1116 assertEqualsAsCharactersPtr(jsOneString, "1"); 1117 assertEqualsAsCharactersPtr(jsCFString, "A"); 1118 assertEqualsAsCharactersPtr(jsCFStringWithCharacters, "A"); 1119 assertEqualsAsCharactersPtr(jsCFEmptyString, ""); 1120 assertEqualsAsCharactersPtr(jsCFEmptyStringWithCharacters, ""); 1121 1122 assertEqualsAsUTF8String(jsUndefined, "undefined"); 1123 assertEqualsAsUTF8String(jsNull, "null"); 1124 assertEqualsAsUTF8String(jsTrue, "true"); 1125 assertEqualsAsUTF8String(jsFalse, "false"); 1126 assertEqualsAsUTF8String(jsZero, "0"); 1127 assertEqualsAsUTF8String(jsOne, "1"); 1128 assertEqualsAsUTF8String(jsOneThird, "0.3333333333333333"); 1129 assertEqualsAsUTF8String(jsEmptyString, ""); 1130 assertEqualsAsUTF8String(jsOneString, "1"); 1131 assertEqualsAsUTF8String(jsCFString, "A"); 1132 assertEqualsAsUTF8String(jsCFStringWithCharacters, "A"); 1133 assertEqualsAsUTF8String(jsCFEmptyString, ""); 1134 assertEqualsAsUTF8String(jsCFEmptyStringWithCharacters, ""); 1135 1136 ASSERT(JSValueIsStrictEqual(context, jsTrue, jsTrue)); 1137 ASSERT(!JSValueIsStrictEqual(context, jsOne, jsOneString)); 1138 1139 ASSERT(JSValueIsEqual(context, jsOne, jsOneString, NULL)); 1140 ASSERT(!JSValueIsEqual(context, jsTrue, jsFalse, NULL)); 1141 1142 CFStringRef cfJSString = JSStringCopyCFString(kCFAllocatorDefault, jsCFIString); 1143 CFStringRef cfJSEmptyString = JSStringCopyCFString(kCFAllocatorDefault, jsCFEmptyIString); 1144 ASSERT(CFEqual(cfJSString, cfString)); 1145 ASSERT(CFEqual(cfJSEmptyString, cfEmptyString)); 1146 CFRelease(cfJSString); 1147 CFRelease(cfJSEmptyString); 1148 1149 CFRelease(cfString); 1150 CFRelease(cfEmptyString); 1151 1152 jsGlobalValue = JSObjectMake(context, NULL, NULL); 1153 makeGlobalNumberValue(context); 1154 JSValueProtect(context, jsGlobalValue); 1155 JSGarbageCollect(context); 1156 ASSERT(JSValueIsObject(context, jsGlobalValue)); 1157 JSValueUnprotect(context, jsGlobalValue); 1158 JSValueUnprotect(context, jsNumberValue); 1159 1160 JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;"); 1161 JSStringRef badSyntax = JSStringCreateWithUTF8CString("x := 1;"); 1162 ASSERT(JSCheckScriptSyntax(context, goodSyntax, NULL, 0, NULL)); 1163 ASSERT(!JSCheckScriptSyntax(context, badSyntax, NULL, 0, NULL)); 1164 1165 JSValueRef result; 1166 JSValueRef v; 1167 JSObjectRef o; 1168 JSStringRef string; 1169 1170 result = JSEvaluateScript(context, goodSyntax, NULL, NULL, 1, NULL); 1171 ASSERT(result); 1172 ASSERT(JSValueIsEqual(context, result, jsOne, NULL)); 1173 1174 exception = NULL; 1175 result = JSEvaluateScript(context, badSyntax, NULL, NULL, 1, &exception); 1176 ASSERT(!result); 1177 ASSERT(JSValueIsObject(context, exception)); 1178 1179 JSStringRef array = JSStringCreateWithUTF8CString("Array"); 1180 JSObjectRef arrayConstructor = JSValueToObject(context, JSObjectGetProperty(context, globalObject, array, NULL), NULL); 1181 JSStringRelease(array); 1182 result = JSObjectCallAsConstructor(context, arrayConstructor, 0, NULL, NULL); 1183 ASSERT(result); 1184 ASSERT(JSValueIsObject(context, result)); 1185 ASSERT(JSValueIsInstanceOfConstructor(context, result, arrayConstructor, NULL)); 1186 ASSERT(!JSValueIsInstanceOfConstructor(context, JSValueMakeNull(context), arrayConstructor, NULL)); 1187 1188 o = JSValueToObject(context, result, NULL); 1189 exception = NULL; 1190 ASSERT(JSValueIsUndefined(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception))); 1191 ASSERT(!exception); 1192 1193 JSObjectSetPropertyAtIndex(context, o, 0, JSValueMakeNumber(context, 1), &exception); 1194 ASSERT(!exception); 1195 1196 exception = NULL; 1197 ASSERT(1 == JSValueToNumber(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception), &exception)); 1198 ASSERT(!exception); 1199 1200 JSStringRef functionBody; 1201 JSObjectRef function; 1202 1203 exception = NULL; 1204 functionBody = JSStringCreateWithUTF8CString("rreturn Array;"); 1205 JSStringRef line = JSStringCreateWithUTF8CString("line"); 1206 ASSERT(!JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception)); 1207 ASSERT(JSValueIsObject(context, exception)); 1208 v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line, NULL); 1209 assertEqualsAsNumber(v, 1); 1210 JSStringRelease(functionBody); 1211 JSStringRelease(line); 1212 1213 exception = NULL; 1214 functionBody = JSStringCreateWithUTF8CString("return Array;"); 1215 function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception); 1216 JSStringRelease(functionBody); 1217 ASSERT(!exception); 1218 ASSERT(JSObjectIsFunction(context, function)); 1219 v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL); 1220 ASSERT(v); 1221 ASSERT(JSValueIsEqual(context, v, arrayConstructor, NULL)); 1222 1223 exception = NULL; 1224 function = JSObjectMakeFunction(context, NULL, 0, NULL, jsEmptyIString, NULL, 0, &exception); 1225 ASSERT(!exception); 1226 v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, &exception); 1227 ASSERT(v && !exception); 1228 ASSERT(JSValueIsUndefined(context, v)); 1229 1230 exception = NULL; 1231 v = NULL; 1232 JSStringRef foo = JSStringCreateWithUTF8CString("foo"); 1233 JSStringRef argumentNames[] = { foo }; 1234 functionBody = JSStringCreateWithUTF8CString("return foo;"); 1235 function = JSObjectMakeFunction(context, foo, 1, argumentNames, functionBody, NULL, 1, &exception); 1236 ASSERT(function && !exception); 1237 JSValueRef arguments[] = { JSValueMakeNumber(context, 2) }; 1238 v = JSObjectCallAsFunction(context, function, NULL, 1, arguments, &exception); 1239 JSStringRelease(foo); 1240 JSStringRelease(functionBody); 1241 1242 string = JSValueToStringCopy(context, function, NULL); 1243 assertEqualsAsUTF8String(JSValueMakeString(context, string), "function foo(foo) { return foo;\n}"); 1244 JSStringRelease(string); 1245 1246 JSStringRef print = JSStringCreateWithUTF8CString("print"); 1247 JSObjectRef printFunction = JSObjectMakeFunctionWithCallback(context, print, print_callAsFunction); 1248 JSObjectSetProperty(context, globalObject, print, printFunction, kJSPropertyAttributeNone, NULL); 1249 JSStringRelease(print); 1250 1251 ASSERT(!JSObjectSetPrivate(printFunction, (void*)1)); 1252 ASSERT(!JSObjectGetPrivate(printFunction)); 1253 1254 JSStringRef myConstructorIString = JSStringCreateWithUTF8CString("MyConstructor"); 1255 JSObjectRef myConstructor = JSObjectMakeConstructor(context, NULL, myConstructor_callAsConstructor); 1256 JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone, NULL); 1257 JSStringRelease(myConstructorIString); 1258 1259 ASSERT(!JSObjectSetPrivate(myConstructor, (void*)1)); 1260 ASSERT(!JSObjectGetPrivate(myConstructor)); 1261 1262 string = JSStringCreateWithUTF8CString("Base"); 1263 JSObjectRef baseConstructor = JSObjectMakeConstructor(context, Base_class(context), NULL); 1264 JSObjectSetProperty(context, globalObject, string, baseConstructor, kJSPropertyAttributeNone, NULL); 1265 JSStringRelease(string); 1266 1267 string = JSStringCreateWithUTF8CString("Derived"); 1268 JSObjectRef derivedConstructor = JSObjectMakeConstructor(context, Derived_class(context), NULL); 1269 JSObjectSetProperty(context, globalObject, string, derivedConstructor, kJSPropertyAttributeNone, NULL); 1270 JSStringRelease(string); 1271 1272 string = JSStringCreateWithUTF8CString("Derived2"); 1273 JSObjectRef derived2Constructor = JSObjectMakeConstructor(context, Derived2_class(context), NULL); 1274 JSObjectSetProperty(context, globalObject, string, derived2Constructor, kJSPropertyAttributeNone, NULL); 1275 JSStringRelease(string); 1276 1277 o = JSObjectMake(context, NULL, NULL); 1278 JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeNone, NULL); 1279 JSObjectSetProperty(context, o, jsCFIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeDontEnum, NULL); 1280 JSPropertyNameArrayRef nameArray = JSObjectCopyPropertyNames(context, o); 1281 size_t expectedCount = JSPropertyNameArrayGetCount(nameArray); 1282 size_t count; 1283 for (count = 0; count < expectedCount; ++count) 1284 JSPropertyNameArrayGetNameAtIndex(nameArray, count); 1285 JSPropertyNameArrayRelease(nameArray); 1286 ASSERT(count == 1); // jsCFString should not be enumerated 1287 1288 JSValueRef argumentsArrayValues[] = { JSValueMakeNumber(context, 10), JSValueMakeNumber(context, 20) }; 1289 o = JSObjectMakeArray(context, sizeof(argumentsArrayValues) / sizeof(JSValueRef), argumentsArrayValues, NULL); 1290 string = JSStringCreateWithUTF8CString("length"); 1291 v = JSObjectGetProperty(context, o, string, NULL); 1292 assertEqualsAsNumber(v, 2); 1293 v = JSObjectGetPropertyAtIndex(context, o, 0, NULL); 1294 assertEqualsAsNumber(v, 10); 1295 v = JSObjectGetPropertyAtIndex(context, o, 1, NULL); 1296 assertEqualsAsNumber(v, 20); 1297 1298 o = JSObjectMakeArray(context, 0, NULL, NULL); 1299 v = JSObjectGetProperty(context, o, string, NULL); 1300 assertEqualsAsNumber(v, 0); 1301 JSStringRelease(string); 1302 1303 JSValueRef argumentsDateValues[] = { JSValueMakeNumber(context, 0) }; 1304 o = JSObjectMakeDate(context, 1, argumentsDateValues, NULL); 1305 if (timeZoneIsPST()) 1306 assertEqualsAsUTF8String(o, "Wed Dec 31 1969 16:00:00 GMT-0800 (PST)"); 1307 1308 string = JSStringCreateWithUTF8CString("an error message"); 1309 JSValueRef argumentsErrorValues[] = { JSValueMakeString(context, string) }; 1310 o = JSObjectMakeError(context, 1, argumentsErrorValues, NULL); 1311 assertEqualsAsUTF8String(o, "Error: an error message"); 1312 JSStringRelease(string); 1313 1314 string = JSStringCreateWithUTF8CString("foo"); 1315 JSStringRef string2 = JSStringCreateWithUTF8CString("gi"); 1316 JSValueRef argumentsRegExpValues[] = { JSValueMakeString(context, string), JSValueMakeString(context, string2) }; 1317 o = JSObjectMakeRegExp(context, 2, argumentsRegExpValues, NULL); 1318 assertEqualsAsUTF8String(o, "/foo/gi"); 1319 JSStringRelease(string); 1320 JSStringRelease(string2); 1321 1322 JSClassDefinition nullDefinition = kJSClassDefinitionEmpty; 1323 nullDefinition.attributes = kJSClassAttributeNoAutomaticPrototype; 1324 JSClassRef nullClass = JSClassCreate(&nullDefinition); 1325 JSClassRelease(nullClass); 1326 1327 nullDefinition = kJSClassDefinitionEmpty; 1328 nullClass = JSClassCreate(&nullDefinition); 1329 JSClassRelease(nullClass); 1330 1331 functionBody = JSStringCreateWithUTF8CString("return this;"); 1332 function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, NULL); 1333 JSStringRelease(functionBody); 1334 v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL); 1335 ASSERT(JSValueIsEqual(context, v, globalObject, NULL)); 1336 v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL); 1337 ASSERT(JSValueIsEqual(context, v, o, NULL)); 1338 1339 functionBody = JSStringCreateWithUTF8CString("return eval(\"this\");"); 1340 function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, NULL); 1341 JSStringRelease(functionBody); 1342 v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL); 1343 ASSERT(JSValueIsEqual(context, v, globalObject, NULL)); 1344 v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL); 1345 ASSERT(JSValueIsEqual(context, v, o, NULL)); 1346 1347 JSStringRef script = JSStringCreateWithUTF8CString("this;"); 1348 v = JSEvaluateScript(context, script, NULL, NULL, 1, NULL); 1349 ASSERT(JSValueIsEqual(context, v, globalObject, NULL)); 1350 v = JSEvaluateScript(context, script, o, NULL, 1, NULL); 1351 ASSERT(JSValueIsEqual(context, v, o, NULL)); 1352 JSStringRelease(script); 1353 1354 script = JSStringCreateWithUTF8CString("eval(this);"); 1355 v = JSEvaluateScript(context, script, NULL, NULL, 1, NULL); 1356 ASSERT(JSValueIsEqual(context, v, globalObject, NULL)); 1357 v = JSEvaluateScript(context, script, o, NULL, 1, NULL); 1358 ASSERT(JSValueIsEqual(context, v, o, NULL)); 1359 JSStringRelease(script); 1360 1361 // Verify that creating a constructor for a class with no static functions does not trigger 1362 // an assert inside putDirect or lead to a crash during GC. <https://bugs.webkit.org/show_bug.cgi?id=25785> 1363 nullDefinition = kJSClassDefinitionEmpty; 1364 nullClass = JSClassCreate(&nullDefinition); 1365 myConstructor = JSObjectMakeConstructor(context, nullClass, 0); 1366 JSClassRelease(nullClass); 1367 1368 char* scriptUTF8 = createStringWithContentsOfFile(scriptPath); 1369 if (!scriptUTF8) { 1370 printf("FAIL: Test script could not be loaded.\n"); 1371 failed = 1; 1372 } else { 1373 script = JSStringCreateWithUTF8CString(scriptUTF8); 1374 result = JSEvaluateScript(context, script, NULL, NULL, 1, &exception); 1375 if (result && JSValueIsUndefined(context, result)) 1376 printf("PASS: Test script executed successfully.\n"); 1377 else { 1378 printf("FAIL: Test script returned unexpected value:\n"); 1379 JSStringRef exceptionIString = JSValueToStringCopy(context, exception, NULL); 1380 CFStringRef exceptionCF = JSStringCopyCFString(kCFAllocatorDefault, exceptionIString); 1381 CFShow(exceptionCF); 1382 CFRelease(exceptionCF); 1383 JSStringRelease(exceptionIString); 1384 failed = 1; 1385 } 1386 JSStringRelease(script); 1387 free(scriptUTF8); 1388 } 1389 1390 // Clear out local variables pointing at JSObjectRefs to allow their values to be collected 1391 function = NULL; 1392 v = NULL; 1393 o = NULL; 1394 globalObject = NULL; 1395 myConstructor = NULL; 1396 1397 JSStringRelease(jsEmptyIString); 1398 JSStringRelease(jsOneIString); 1399 JSStringRelease(jsCFIString); 1400 JSStringRelease(jsCFEmptyIString); 1401 JSStringRelease(jsCFIStringWithCharacters); 1402 JSStringRelease(jsCFEmptyIStringWithCharacters); 1403 JSStringRelease(goodSyntax); 1404 JSStringRelease(badSyntax); 1405 1406 JSGlobalContextRelease(context); 1407 JSClassRelease(globalObjectClass); 1408 1409 // Test for an infinite prototype chain that used to be created. This test 1410 // passes if the call to JSObjectHasProperty() does not hang. 1411 1412 JSClassDefinition prototypeLoopClassDefinition = kJSClassDefinitionEmpty; 1413 prototypeLoopClassDefinition.staticFunctions = globalObject_staticFunctions; 1414 JSClassRef prototypeLoopClass = JSClassCreate(&prototypeLoopClassDefinition); 1415 JSGlobalContextRef prototypeLoopContext = JSGlobalContextCreateInGroup(NULL, prototypeLoopClass); 1416 1417 JSStringRef nameProperty = JSStringCreateWithUTF8CString("name"); 1418 JSObjectHasProperty(prototypeLoopContext, JSContextGetGlobalObject(prototypeLoopContext), nameProperty); 1419 1420 JSGlobalContextRelease(prototypeLoopContext); 1421 JSClassRelease(prototypeLoopClass); 1422 1423 printf("PASS: Infinite prototype chain does not occur.\n"); 1424 1425 if (checkForCycleInPrototypeChain()) 1426 printf("PASS: A cycle in a prototype chain can't be created.\n"); 1427 else { 1428 printf("FAIL: A cycle in a prototype chain can be created.\n"); 1429 failed = true; 1430 } 1431 1432 if (failed) { 1433 printf("FAIL: Some tests failed.\n"); 1434 return 1; 1435 } 1436 1437 printf("PASS: Program exited normally.\n"); 1438 return 0; 1439 } 1440 1441 static char* createStringWithContentsOfFile(const char* fileName) 1442 { 1443 char* buffer; 1444 1445 size_t buffer_size = 0; 1446 size_t buffer_capacity = 1024; 1447 buffer = (char*)malloc(buffer_capacity); 1448 1449 FILE* f = fopen(fileName, "r"); 1450 if (!f) { 1451 fprintf(stderr, "Could not open file: %s\n", fileName); 1452 return 0; 1453 } 1454 1455 while (!feof(f) && !ferror(f)) { 1456 buffer_size += fread(buffer + buffer_size, 1, buffer_capacity - buffer_size, f); 1457 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0' 1458 buffer_capacity *= 2; 1459 buffer = (char*)realloc(buffer, buffer_capacity); 1460 ASSERT(buffer); 1461 } 1462 1463 ASSERT(buffer_size < buffer_capacity); 1464 } 1465 fclose(f); 1466 buffer[buffer_size] = '\0'; 1467 1468 return buffer; 1469 } 1470