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