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
43static double nan(const char*)
44{
45    return std::numeric_limits<double>::quiet_NaN();
46}
47
48#endif
49
50static JSGlobalContextRef context;
51static int failed;
52static 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
60static 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
73static 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
98static 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
127static 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
137static JSValueRef jsGlobalValue; // non-stack value for testing JSValueProtect()
138
139/* MyObject pseudo-class */
140
141static 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
158static 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
191static 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
208static 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
224static 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
240static 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
258static 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
274static 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
291static 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
314static JSStaticValue evilStaticValues[] = {
315    { "nullGetSet", 0, 0, kJSPropertyAttributeNone },
316    { 0, 0, 0, 0 }
317};
318
319static JSStaticFunction evilStaticFunctions[] = {
320    { "nullCall", 0, kJSPropertyAttributeNone },
321    { 0, 0, 0 }
322};
323
324JSClassDefinition 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
347static 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
358static 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
373static 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
405JSClassDefinition 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
428static 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
439JSClassDefinition 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
462static 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
474static 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
483static 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
493static 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
504static JSStaticFunction Base_staticFunctions[] = {
505    { "baseProtoDup", NULL, kJSPropertyAttributeNone },
506    { "baseProto", Base_callAsFunction, kJSPropertyAttributeNone },
507    { 0, 0, 0 }
508};
509
510static JSStaticValue Base_staticValues[] = {
511    { "baseDup", Base_get, Base_set, kJSPropertyAttributeNone },
512    { "baseOnly", Base_get, Base_set, kJSPropertyAttributeNone },
513    { 0, 0, 0, 0 }
514};
515
516static bool TestInitializeFinalize;
517static 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
527static unsigned Base_didFinalize;
528static 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
537static 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
553static 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
562static 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
573static 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
584static JSStaticFunction Derived_staticFunctions[] = {
585    { "protoOnly", Derived_callAsFunction, kJSPropertyAttributeNone },
586    { "protoDup", NULL, kJSPropertyAttributeNone },
587    { "baseProtoDup", Derived_callAsFunction, kJSPropertyAttributeNone },
588    { 0, 0, 0 }
589};
590
591static 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
598static 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
608static void Derived_finalize(JSObjectRef object)
609{
610    if (TestInitializeFinalize) {
611        ASSERT((void*)3 == JSObjectGetPrivate(object));
612        JSObjectSetPrivate(object, (void*)4);
613    }
614}
615
616static 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
631static 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
642static 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
663static 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
679static 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
699static 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
708static 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
718static 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
729static 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
740static JSStaticValue globalObject_staticValues[] = {
741    { "globalStaticValue", globalObject_get, globalObject_set, kJSPropertyAttributeNone },
742    { 0, 0, 0, 0 }
743};
744
745static JSStaticFunction globalObject_staticFunctions[] = {
746    { "globalStaticFunction", globalObject_call, kJSPropertyAttributeNone },
747    { "gc", functionGC, kJSPropertyAttributeNone },
748    { 0, 0, 0 }
749};
750
751static char* createStringWithContentsOfFile(const char* fileName);
752
753static 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
760static JSValueRef jsNumberValue =  NULL;
761
762static JSObjectRef aHeapRef = NULL;
763
764static void makeGlobalNumberValue(JSContextRef context) {
765    JSValueRef v = JSValueMakeNumber(context, 420);
766    JSValueProtect(context, v);
767    jsNumberValue = v;
768    v = NULL;
769}
770
771static 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
783static 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
824int 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
1441static 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