Home | History | Annotate | Download | only in tests
      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(&gtm, 0, sizeof(gtm));
    132     strftime(timeZoneName, sizeof(timeZoneName), "%Z", &gtm);
    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