1/*
2 * Copyright (C) 2006 Apple Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <wtf/Platform.h>
27
28#include "JavaScriptCore.h"
29#include "JSBasePrivate.h"
30#include "JSContextRefPrivate.h"
31#include "JSObjectRefPrivate.h"
32#include "JSScriptRefPrivate.h"
33#include "JSStringRefPrivate.h"
34#include <math.h>
35#define ASSERT_DISABLED 0
36#include <wtf/Assertions.h>
37
38#if OS(DARWIN)
39#include <mach/mach.h>
40#include <mach/mach_time.h>
41#include <sys/time.h>
42#endif
43
44#if OS(WINDOWS)
45#include <windows.h>
46#endif
47
48#include "CustomGlobalObjectClassTest.h"
49
50#if JSC_OBJC_API_ENABLED
51void testObjectiveCAPI(void);
52#endif
53
54bool assertTrue(bool value, const char* message);
55extern void JSSynchronousGarbageCollectForDebugging(JSContextRef);
56
57static JSGlobalContextRef context;
58int failed;
59static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
60{
61    if (JSValueToBoolean(context, value) != expectedValue) {
62        fprintf(stderr, "assertEqualsAsBoolean failed: %p, %d\n", value, expectedValue);
63        failed = 1;
64    }
65}
66
67static void assertEqualsAsNumber(JSValueRef value, double expectedValue)
68{
69    double number = JSValueToNumber(context, value, NULL);
70
71    // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function,
72    // causing a build break with -Wshorten-64-to-32 enabled.  The issue is known by the appropriate team.
73    // After that's resolved, we can remove these casts
74    if (number != expectedValue && !(isnan((float)number) && isnan((float)expectedValue))) {
75        fprintf(stderr, "assertEqualsAsNumber failed: %p, %lf\n", value, expectedValue);
76        failed = 1;
77    }
78}
79
80static void assertEqualsAsUTF8String(JSValueRef value, const char* expectedValue)
81{
82    JSStringRef valueAsString = JSValueToStringCopy(context, value, NULL);
83
84    size_t jsSize = JSStringGetMaximumUTF8CStringSize(valueAsString);
85    char* jsBuffer = (char*)malloc(jsSize);
86    JSStringGetUTF8CString(valueAsString, jsBuffer, jsSize);
87
88    unsigned i;
89    for (i = 0; jsBuffer[i]; i++) {
90        if (jsBuffer[i] != expectedValue[i]) {
91            fprintf(stderr, "assertEqualsAsUTF8String failed at character %d: %c(%d) != %c(%d)\n", i, jsBuffer[i], jsBuffer[i], expectedValue[i], expectedValue[i]);
92            failed = 1;
93        }
94    }
95
96    if (jsSize < strlen(jsBuffer) + 1) {
97        fprintf(stderr, "assertEqualsAsUTF8String failed: jsSize was too small\n");
98        failed = 1;
99    }
100
101    free(jsBuffer);
102    JSStringRelease(valueAsString);
103}
104
105static void assertEqualsAsCharactersPtr(JSValueRef value, const char* expectedValue)
106{
107    JSStringRef valueAsString = JSValueToStringCopy(context, value, NULL);
108
109    size_t jsLength = JSStringGetLength(valueAsString);
110    const JSChar* jsBuffer = JSStringGetCharactersPtr(valueAsString);
111
112    CFStringRef expectedValueAsCFString = CFStringCreateWithCString(kCFAllocatorDefault,
113                                                                    expectedValue,
114                                                                    kCFStringEncodingUTF8);
115    CFIndex cfLength = CFStringGetLength(expectedValueAsCFString);
116    UniChar* cfBuffer = (UniChar*)malloc(cfLength * sizeof(UniChar));
117    CFStringGetCharacters(expectedValueAsCFString, CFRangeMake(0, cfLength), cfBuffer);
118    CFRelease(expectedValueAsCFString);
119
120    if (memcmp(jsBuffer, cfBuffer, cfLength * sizeof(UniChar)) != 0) {
121        fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsBuffer != cfBuffer\n");
122        failed = 1;
123    }
124
125    if (jsLength != (size_t)cfLength) {
126        fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsLength(%ld) != cfLength(%ld)\n", jsLength, cfLength);
127        failed = 1;
128    }
129
130    free(cfBuffer);
131    JSStringRelease(valueAsString);
132}
133
134static bool timeZoneIsPST()
135{
136    char timeZoneName[70];
137    struct tm gtm;
138    memset(&gtm, 0, sizeof(gtm));
139    strftime(timeZoneName, sizeof(timeZoneName), "%Z", &gtm);
140
141    return 0 == strcmp("PST", timeZoneName);
142}
143
144static JSValueRef jsGlobalValue; // non-stack value for testing JSValueProtect()
145
146/* MyObject pseudo-class */
147
148static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName)
149{
150    UNUSED_PARAM(context);
151    UNUSED_PARAM(object);
152
153    if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")
154        || JSStringIsEqualToUTF8CString(propertyName, "cantFind")
155        || JSStringIsEqualToUTF8CString(propertyName, "throwOnGet")
156        || JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")
157        || JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie")
158        || JSStringIsEqualToUTF8CString(propertyName, "0")) {
159        return true;
160    }
161
162    return false;
163}
164
165static JSValueRef MyObject_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
166{
167    UNUSED_PARAM(context);
168    UNUSED_PARAM(object);
169
170    if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")) {
171        return JSValueMakeNumber(context, 1);
172    }
173
174    if (JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")) {
175        return JSValueMakeNumber(context, 1);
176    }
177
178    if (JSStringIsEqualToUTF8CString(propertyName, "cantFind")) {
179        return JSValueMakeUndefined(context);
180    }
181
182    if (JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie")) {
183        return 0;
184    }
185
186    if (JSStringIsEqualToUTF8CString(propertyName, "throwOnGet")) {
187        return JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
188    }
189
190    if (JSStringIsEqualToUTF8CString(propertyName, "0")) {
191        *exception = JSValueMakeNumber(context, 1);
192        return JSValueMakeNumber(context, 1);
193    }
194
195    return JSValueMakeNull(context);
196}
197
198static bool MyObject_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
199{
200    UNUSED_PARAM(context);
201    UNUSED_PARAM(object);
202    UNUSED_PARAM(value);
203    UNUSED_PARAM(exception);
204
205    if (JSStringIsEqualToUTF8CString(propertyName, "cantSet"))
206        return true; // pretend we set the property in order to swallow it
207
208    if (JSStringIsEqualToUTF8CString(propertyName, "throwOnSet")) {
209        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
210    }
211
212    return false;
213}
214
215static bool MyObject_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
216{
217    UNUSED_PARAM(context);
218    UNUSED_PARAM(object);
219
220    if (JSStringIsEqualToUTF8CString(propertyName, "cantDelete"))
221        return true;
222
223    if (JSStringIsEqualToUTF8CString(propertyName, "throwOnDelete")) {
224        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
225        return false;
226    }
227
228    return false;
229}
230
231static void MyObject_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames)
232{
233    UNUSED_PARAM(context);
234    UNUSED_PARAM(object);
235
236    JSStringRef propertyName;
237
238    propertyName = JSStringCreateWithUTF8CString("alwaysOne");
239    JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
240    JSStringRelease(propertyName);
241
242    propertyName = JSStringCreateWithUTF8CString("myPropertyName");
243    JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
244    JSStringRelease(propertyName);
245}
246
247static JSValueRef MyObject_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
248{
249    UNUSED_PARAM(context);
250    UNUSED_PARAM(object);
251    UNUSED_PARAM(thisObject);
252    UNUSED_PARAM(exception);
253
254    if (argumentCount > 0 && JSValueIsString(context, arguments[0]) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, arguments[0], 0), "throwOnCall")) {
255        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
256        return JSValueMakeUndefined(context);
257    }
258
259    if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0)))
260        return JSValueMakeNumber(context, 1);
261
262    return JSValueMakeUndefined(context);
263}
264
265static JSObjectRef MyObject_callAsConstructor(JSContextRef context, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
266{
267    UNUSED_PARAM(context);
268    UNUSED_PARAM(object);
269
270    if (argumentCount > 0 && JSValueIsString(context, arguments[0]) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, arguments[0], 0), "throwOnConstruct")) {
271        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception);
272        return object;
273    }
274
275    if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0)))
276        return JSValueToObject(context, JSValueMakeNumber(context, 1), exception);
277
278    return JSValueToObject(context, JSValueMakeNumber(context, 0), exception);
279}
280
281static bool MyObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception)
282{
283    UNUSED_PARAM(context);
284    UNUSED_PARAM(constructor);
285
286    if (JSValueIsString(context, possibleValue) && JSStringIsEqualToUTF8CString(JSValueToStringCopy(context, possibleValue, 0), "throwOnHasInstance")) {
287        JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), constructor, JSStringCreateWithUTF8CString("test script"), 1, exception);
288        return false;
289    }
290
291    JSStringRef numberString = JSStringCreateWithUTF8CString("Number");
292    JSObjectRef numberConstructor = JSValueToObject(context, JSObjectGetProperty(context, JSContextGetGlobalObject(context), numberString, exception), exception);
293    JSStringRelease(numberString);
294
295    return JSValueIsInstanceOfConstructor(context, possibleValue, numberConstructor, exception);
296}
297
298static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
299{
300    UNUSED_PARAM(object);
301    UNUSED_PARAM(exception);
302
303    switch (type) {
304    case kJSTypeNumber:
305        return JSValueMakeNumber(context, 1);
306    case kJSTypeString:
307        {
308            JSStringRef string = JSStringCreateWithUTF8CString("MyObjectAsString");
309            JSValueRef result = JSValueMakeString(context, string);
310            JSStringRelease(string);
311            return result;
312        }
313    default:
314        break;
315    }
316
317    // string conversion -- forward to default object class
318    return JSValueMakeNull(context);
319}
320
321static JSValueRef MyObject_convertToTypeWrapper(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
322{
323    UNUSED_PARAM(context);
324    UNUSED_PARAM(object);
325    UNUSED_PARAM(type);
326    UNUSED_PARAM(exception);
327    // Forward to default object class
328    return 0;
329}
330
331static bool MyObject_set_nullGetForwardSet(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
332{
333    UNUSED_PARAM(ctx);
334    UNUSED_PARAM(object);
335    UNUSED_PARAM(propertyName);
336    UNUSED_PARAM(value);
337    UNUSED_PARAM(exception);
338    return false; // Forward to parent class.
339}
340
341static JSStaticValue evilStaticValues[] = {
342    { "nullGetSet", 0, 0, kJSPropertyAttributeNone },
343    { "nullGetForwardSet", 0, MyObject_set_nullGetForwardSet, kJSPropertyAttributeNone },
344    { 0, 0, 0, 0 }
345};
346
347static JSStaticFunction evilStaticFunctions[] = {
348    { "nullCall", 0, kJSPropertyAttributeNone },
349    { 0, 0, 0 }
350};
351
352JSClassDefinition MyObject_definition = {
353    0,
354    kJSClassAttributeNone,
355
356    "MyObject",
357    NULL,
358
359    evilStaticValues,
360    evilStaticFunctions,
361
362    NULL,
363    NULL,
364    MyObject_hasProperty,
365    MyObject_getProperty,
366    MyObject_setProperty,
367    MyObject_deleteProperty,
368    MyObject_getPropertyNames,
369    MyObject_callAsFunction,
370    MyObject_callAsConstructor,
371    MyObject_hasInstance,
372    MyObject_convertToType,
373};
374
375JSClassDefinition MyObject_convertToTypeWrapperDefinition = {
376    0,
377    kJSClassAttributeNone,
378
379    "MyObject",
380    NULL,
381
382    NULL,
383    NULL,
384
385    NULL,
386    NULL,
387    NULL,
388    NULL,
389    NULL,
390    NULL,
391    NULL,
392    NULL,
393    NULL,
394    NULL,
395    MyObject_convertToTypeWrapper,
396};
397
398JSClassDefinition MyObject_nullWrapperDefinition = {
399    0,
400    kJSClassAttributeNone,
401
402    "MyObject",
403    NULL,
404
405    NULL,
406    NULL,
407
408    NULL,
409    NULL,
410    NULL,
411    NULL,
412    NULL,
413    NULL,
414    NULL,
415    NULL,
416    NULL,
417    NULL,
418    NULL,
419};
420
421static JSClassRef MyObject_class(JSContextRef context)
422{
423    UNUSED_PARAM(context);
424
425    static JSClassRef jsClass;
426    if (!jsClass) {
427        JSClassRef baseClass = JSClassCreate(&MyObject_definition);
428        MyObject_convertToTypeWrapperDefinition.parentClass = baseClass;
429        JSClassRef wrapperClass = JSClassCreate(&MyObject_convertToTypeWrapperDefinition);
430        MyObject_nullWrapperDefinition.parentClass = wrapperClass;
431        jsClass = JSClassCreate(&MyObject_nullWrapperDefinition);
432    }
433
434    return jsClass;
435}
436
437static JSValueRef PropertyCatchalls_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
438{
439    UNUSED_PARAM(context);
440    UNUSED_PARAM(object);
441    UNUSED_PARAM(propertyName);
442    UNUSED_PARAM(exception);
443
444    if (JSStringIsEqualToUTF8CString(propertyName, "x")) {
445        static size_t count;
446        if (count++ < 5)
447            return NULL;
448
449        // Swallow all .x gets after 5, returning null.
450        return JSValueMakeNull(context);
451    }
452
453    if (JSStringIsEqualToUTF8CString(propertyName, "y")) {
454        static size_t count;
455        if (count++ < 5)
456            return NULL;
457
458        // Swallow all .y gets after 5, returning null.
459        return JSValueMakeNull(context);
460    }
461
462    if (JSStringIsEqualToUTF8CString(propertyName, "z")) {
463        static size_t count;
464        if (count++ < 5)
465            return NULL;
466
467        // Swallow all .y gets after 5, returning null.
468        return JSValueMakeNull(context);
469    }
470
471    return NULL;
472}
473
474static bool PropertyCatchalls_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
475{
476    UNUSED_PARAM(context);
477    UNUSED_PARAM(object);
478    UNUSED_PARAM(propertyName);
479    UNUSED_PARAM(value);
480    UNUSED_PARAM(exception);
481
482    if (JSStringIsEqualToUTF8CString(propertyName, "x")) {
483        static size_t count;
484        if (count++ < 5)
485            return false;
486
487        // Swallow all .x sets after 4.
488        return true;
489    }
490
491    if (JSStringIsEqualToUTF8CString(propertyName, "make_throw") || JSStringIsEqualToUTF8CString(propertyName, "0")) {
492        *exception = JSValueMakeNumber(context, 5);
493        return true;
494    }
495
496    return false;
497}
498
499static void PropertyCatchalls_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames)
500{
501    UNUSED_PARAM(context);
502    UNUSED_PARAM(object);
503
504    static size_t count;
505    static const char* numbers[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
506
507    // Provide a property of a different name every time.
508    JSStringRef propertyName = JSStringCreateWithUTF8CString(numbers[count++ % 10]);
509    JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
510    JSStringRelease(propertyName);
511}
512
513JSClassDefinition PropertyCatchalls_definition = {
514    0,
515    kJSClassAttributeNone,
516
517    "PropertyCatchalls",
518    NULL,
519
520    NULL,
521    NULL,
522
523    NULL,
524    NULL,
525    NULL,
526    PropertyCatchalls_getProperty,
527    PropertyCatchalls_setProperty,
528    NULL,
529    PropertyCatchalls_getPropertyNames,
530    NULL,
531    NULL,
532    NULL,
533    NULL,
534};
535
536static JSClassRef PropertyCatchalls_class(JSContextRef context)
537{
538    UNUSED_PARAM(context);
539
540    static JSClassRef jsClass;
541    if (!jsClass)
542        jsClass = JSClassCreate(&PropertyCatchalls_definition);
543
544    return jsClass;
545}
546
547static bool EvilExceptionObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception)
548{
549    UNUSED_PARAM(context);
550    UNUSED_PARAM(constructor);
551
552    JSStringRef hasInstanceName = JSStringCreateWithUTF8CString("hasInstance");
553    JSValueRef hasInstance = JSObjectGetProperty(context, constructor, hasInstanceName, exception);
554    JSStringRelease(hasInstanceName);
555    if (!hasInstance)
556        return false;
557    JSObjectRef function = JSValueToObject(context, hasInstance, exception);
558    JSValueRef result = JSObjectCallAsFunction(context, function, constructor, 1, &possibleValue, exception);
559    return result && JSValueToBoolean(context, result);
560}
561
562static JSValueRef EvilExceptionObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
563{
564    UNUSED_PARAM(object);
565    UNUSED_PARAM(exception);
566    JSStringRef funcName;
567    switch (type) {
568    case kJSTypeNumber:
569        funcName = JSStringCreateWithUTF8CString("toNumber");
570        break;
571    case kJSTypeString:
572        funcName = JSStringCreateWithUTF8CString("toStringExplicit");
573        break;
574    default:
575        return JSValueMakeNull(context);
576    }
577
578    JSValueRef func = JSObjectGetProperty(context, object, funcName, exception);
579    JSStringRelease(funcName);
580    JSObjectRef function = JSValueToObject(context, func, exception);
581    if (!function)
582        return JSValueMakeNull(context);
583    JSValueRef value = JSObjectCallAsFunction(context, function, object, 0, NULL, exception);
584    if (!value) {
585        JSStringRef errorString = JSStringCreateWithUTF8CString("convertToType failed");
586        JSValueRef errorStringRef = JSValueMakeString(context, errorString);
587        JSStringRelease(errorString);
588        return errorStringRef;
589    }
590    return value;
591}
592
593JSClassDefinition EvilExceptionObject_definition = {
594    0,
595    kJSClassAttributeNone,
596
597    "EvilExceptionObject",
598    NULL,
599
600    NULL,
601    NULL,
602
603    NULL,
604    NULL,
605    NULL,
606    NULL,
607    NULL,
608    NULL,
609    NULL,
610    NULL,
611    NULL,
612    EvilExceptionObject_hasInstance,
613    EvilExceptionObject_convertToType,
614};
615
616static JSClassRef EvilExceptionObject_class(JSContextRef context)
617{
618    UNUSED_PARAM(context);
619
620    static JSClassRef jsClass;
621    if (!jsClass)
622        jsClass = JSClassCreate(&EvilExceptionObject_definition);
623
624    return jsClass;
625}
626
627JSClassDefinition EmptyObject_definition = {
628    0,
629    kJSClassAttributeNone,
630
631    NULL,
632    NULL,
633
634    NULL,
635    NULL,
636
637    NULL,
638    NULL,
639    NULL,
640    NULL,
641    NULL,
642    NULL,
643    NULL,
644    NULL,
645    NULL,
646    NULL,
647    NULL,
648};
649
650static JSClassRef EmptyObject_class(JSContextRef context)
651{
652    UNUSED_PARAM(context);
653
654    static JSClassRef jsClass;
655    if (!jsClass)
656        jsClass = JSClassCreate(&EmptyObject_definition);
657
658    return jsClass;
659}
660
661
662static JSValueRef Base_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
663{
664    UNUSED_PARAM(object);
665    UNUSED_PARAM(propertyName);
666    UNUSED_PARAM(exception);
667
668    return JSValueMakeNumber(ctx, 1); // distinguish base get form derived get
669}
670
671static bool Base_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
672{
673    UNUSED_PARAM(object);
674    UNUSED_PARAM(propertyName);
675    UNUSED_PARAM(value);
676
677    *exception = JSValueMakeNumber(ctx, 1); // distinguish base set from derived set
678    return true;
679}
680
681static JSValueRef Base_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
682{
683    UNUSED_PARAM(function);
684    UNUSED_PARAM(thisObject);
685    UNUSED_PARAM(argumentCount);
686    UNUSED_PARAM(arguments);
687    UNUSED_PARAM(exception);
688
689    return JSValueMakeNumber(ctx, 1); // distinguish base call from derived call
690}
691
692static JSValueRef Base_returnHardNull(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
693{
694    UNUSED_PARAM(ctx);
695    UNUSED_PARAM(function);
696    UNUSED_PARAM(thisObject);
697    UNUSED_PARAM(argumentCount);
698    UNUSED_PARAM(arguments);
699    UNUSED_PARAM(exception);
700
701    return 0; // should convert to undefined!
702}
703
704static JSStaticFunction Base_staticFunctions[] = {
705    { "baseProtoDup", NULL, kJSPropertyAttributeNone },
706    { "baseProto", Base_callAsFunction, kJSPropertyAttributeNone },
707    { "baseHardNull", Base_returnHardNull, kJSPropertyAttributeNone },
708    { 0, 0, 0 }
709};
710
711static JSStaticValue Base_staticValues[] = {
712    { "baseDup", Base_get, Base_set, kJSPropertyAttributeNone },
713    { "baseOnly", Base_get, Base_set, kJSPropertyAttributeNone },
714    { 0, 0, 0, 0 }
715};
716
717static bool TestInitializeFinalize;
718static void Base_initialize(JSContextRef context, JSObjectRef object)
719{
720    UNUSED_PARAM(context);
721
722    if (TestInitializeFinalize) {
723        ASSERT((void*)1 == JSObjectGetPrivate(object));
724        JSObjectSetPrivate(object, (void*)2);
725    }
726}
727
728static unsigned Base_didFinalize;
729static void Base_finalize(JSObjectRef object)
730{
731    UNUSED_PARAM(object);
732    if (TestInitializeFinalize) {
733        ASSERT((void*)4 == JSObjectGetPrivate(object));
734        Base_didFinalize = true;
735    }
736}
737
738static JSClassRef Base_class(JSContextRef context)
739{
740    UNUSED_PARAM(context);
741
742    static JSClassRef jsClass;
743    if (!jsClass) {
744        JSClassDefinition definition = kJSClassDefinitionEmpty;
745        definition.staticValues = Base_staticValues;
746        definition.staticFunctions = Base_staticFunctions;
747        definition.initialize = Base_initialize;
748        definition.finalize = Base_finalize;
749        jsClass = JSClassCreate(&definition);
750    }
751    return jsClass;
752}
753
754static JSValueRef Derived_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
755{
756    UNUSED_PARAM(object);
757    UNUSED_PARAM(propertyName);
758    UNUSED_PARAM(exception);
759
760    return JSValueMakeNumber(ctx, 2); // distinguish base get form derived get
761}
762
763static bool Derived_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
764{
765    UNUSED_PARAM(ctx);
766    UNUSED_PARAM(object);
767    UNUSED_PARAM(propertyName);
768    UNUSED_PARAM(value);
769
770    *exception = JSValueMakeNumber(ctx, 2); // distinguish base set from derived set
771    return true;
772}
773
774static JSValueRef Derived_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
775{
776    UNUSED_PARAM(function);
777    UNUSED_PARAM(thisObject);
778    UNUSED_PARAM(argumentCount);
779    UNUSED_PARAM(arguments);
780    UNUSED_PARAM(exception);
781
782    return JSValueMakeNumber(ctx, 2); // distinguish base call from derived call
783}
784
785static JSStaticFunction Derived_staticFunctions[] = {
786    { "protoOnly", Derived_callAsFunction, kJSPropertyAttributeNone },
787    { "protoDup", NULL, kJSPropertyAttributeNone },
788    { "baseProtoDup", Derived_callAsFunction, kJSPropertyAttributeNone },
789    { 0, 0, 0 }
790};
791
792static JSStaticValue Derived_staticValues[] = {
793    { "derivedOnly", Derived_get, Derived_set, kJSPropertyAttributeNone },
794    { "protoDup", Derived_get, Derived_set, kJSPropertyAttributeNone },
795    { "baseDup", Derived_get, Derived_set, kJSPropertyAttributeNone },
796    { 0, 0, 0, 0 }
797};
798
799static void Derived_initialize(JSContextRef context, JSObjectRef object)
800{
801    UNUSED_PARAM(context);
802
803    if (TestInitializeFinalize) {
804        ASSERT((void*)2 == JSObjectGetPrivate(object));
805        JSObjectSetPrivate(object, (void*)3);
806    }
807}
808
809static void Derived_finalize(JSObjectRef object)
810{
811    if (TestInitializeFinalize) {
812        ASSERT((void*)3 == JSObjectGetPrivate(object));
813        JSObjectSetPrivate(object, (void*)4);
814    }
815}
816
817static JSClassRef Derived_class(JSContextRef context)
818{
819    static JSClassRef jsClass;
820    if (!jsClass) {
821        JSClassDefinition definition = kJSClassDefinitionEmpty;
822        definition.parentClass = Base_class(context);
823        definition.staticValues = Derived_staticValues;
824        definition.staticFunctions = Derived_staticFunctions;
825        definition.initialize = Derived_initialize;
826        definition.finalize = Derived_finalize;
827        jsClass = JSClassCreate(&definition);
828    }
829    return jsClass;
830}
831
832static JSClassRef Derived2_class(JSContextRef context)
833{
834    static JSClassRef jsClass;
835    if (!jsClass) {
836        JSClassDefinition definition = kJSClassDefinitionEmpty;
837        definition.parentClass = Derived_class(context);
838        jsClass = JSClassCreate(&definition);
839    }
840    return jsClass;
841}
842
843static JSValueRef print_callAsFunction(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
844{
845    UNUSED_PARAM(functionObject);
846    UNUSED_PARAM(thisObject);
847    UNUSED_PARAM(exception);
848
849    ASSERT(JSContextGetGlobalContext(ctx) == context);
850
851    if (argumentCount > 0) {
852        JSStringRef string = JSValueToStringCopy(ctx, arguments[0], NULL);
853        size_t sizeUTF8 = JSStringGetMaximumUTF8CStringSize(string);
854        char* stringUTF8 = (char*)malloc(sizeUTF8);
855        JSStringGetUTF8CString(string, stringUTF8, sizeUTF8);
856        printf("%s\n", stringUTF8);
857        free(stringUTF8);
858        JSStringRelease(string);
859    }
860
861    return JSValueMakeUndefined(ctx);
862}
863
864static JSObjectRef myConstructor_callAsConstructor(JSContextRef context, JSObjectRef constructorObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
865{
866    UNUSED_PARAM(constructorObject);
867    UNUSED_PARAM(exception);
868
869    JSObjectRef result = JSObjectMake(context, NULL, NULL);
870    if (argumentCount > 0) {
871        JSStringRef value = JSStringCreateWithUTF8CString("value");
872        JSObjectSetProperty(context, result, value, arguments[0], kJSPropertyAttributeNone, NULL);
873        JSStringRelease(value);
874    }
875
876    return result;
877}
878
879static JSObjectRef myBadConstructor_callAsConstructor(JSContextRef context, JSObjectRef constructorObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
880{
881    UNUSED_PARAM(context);
882    UNUSED_PARAM(constructorObject);
883    UNUSED_PARAM(argumentCount);
884    UNUSED_PARAM(arguments);
885    UNUSED_PARAM(exception);
886
887    return 0;
888}
889
890
891static void globalObject_initialize(JSContextRef context, JSObjectRef object)
892{
893    UNUSED_PARAM(object);
894    // Ensure that an execution context is passed in
895    ASSERT(context);
896
897    JSObjectRef globalObject = JSContextGetGlobalObject(context);
898    ASSERT(globalObject);
899
900    // Ensure that the standard global properties have been set on the global object
901    JSStringRef array = JSStringCreateWithUTF8CString("Array");
902    JSObjectRef arrayConstructor = JSValueToObject(context, JSObjectGetProperty(context, globalObject, array, NULL), NULL);
903    JSStringRelease(array);
904
905    UNUSED_PARAM(arrayConstructor);
906    ASSERT(arrayConstructor);
907}
908
909static JSValueRef globalObject_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
910{
911    UNUSED_PARAM(object);
912    UNUSED_PARAM(propertyName);
913    UNUSED_PARAM(exception);
914
915    return JSValueMakeNumber(ctx, 3);
916}
917
918static bool globalObject_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
919{
920    UNUSED_PARAM(object);
921    UNUSED_PARAM(propertyName);
922    UNUSED_PARAM(value);
923
924    *exception = JSValueMakeNumber(ctx, 3);
925    return true;
926}
927
928static JSValueRef globalObject_call(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
929{
930    UNUSED_PARAM(function);
931    UNUSED_PARAM(thisObject);
932    UNUSED_PARAM(argumentCount);
933    UNUSED_PARAM(arguments);
934    UNUSED_PARAM(exception);
935
936    return JSValueMakeNumber(ctx, 3);
937}
938
939static JSValueRef functionGC(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
940{
941    UNUSED_PARAM(function);
942    UNUSED_PARAM(thisObject);
943    UNUSED_PARAM(argumentCount);
944    UNUSED_PARAM(arguments);
945    UNUSED_PARAM(exception);
946    JSGarbageCollect(context);
947    return JSValueMakeUndefined(context);
948}
949
950static JSStaticValue globalObject_staticValues[] = {
951    { "globalStaticValue", globalObject_get, globalObject_set, kJSPropertyAttributeNone },
952    { 0, 0, 0, 0 }
953};
954
955static JSStaticFunction globalObject_staticFunctions[] = {
956    { "globalStaticFunction", globalObject_call, kJSPropertyAttributeNone },
957    { "gc", functionGC, kJSPropertyAttributeNone },
958    { 0, 0, 0 }
959};
960
961static char* createStringWithContentsOfFile(const char* fileName);
962
963static void testInitializeFinalize()
964{
965    JSObjectRef o = JSObjectMake(context, Derived_class(context), (void*)1);
966    UNUSED_PARAM(o);
967    ASSERT(JSObjectGetPrivate(o) == (void*)3);
968}
969
970static JSValueRef jsNumberValue =  NULL;
971
972static JSObjectRef aHeapRef = NULL;
973
974static void makeGlobalNumberValue(JSContextRef context) {
975    JSValueRef v = JSValueMakeNumber(context, 420);
976    JSValueProtect(context, v);
977    jsNumberValue = v;
978    v = NULL;
979}
980
981bool assertTrue(bool value, const char* message)
982{
983    if (!value) {
984        if (message)
985            fprintf(stderr, "assertTrue failed: '%s'\n", message);
986        else
987            fprintf(stderr, "assertTrue failed.\n");
988        failed = 1;
989    }
990    return value;
991}
992
993static bool checkForCycleInPrototypeChain()
994{
995    bool result = true;
996    JSGlobalContextRef context = JSGlobalContextCreate(0);
997    JSObjectRef object1 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0);
998    JSObjectRef object2 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0);
999    JSObjectRef object3 = JSObjectMake(context, /* jsClass */ 0, /* data */ 0);
1000
1001    JSObjectSetPrototype(context, object1, JSValueMakeNull(context));
1002    ASSERT(JSValueIsNull(context, JSObjectGetPrototype(context, object1)));
1003
1004    // object1 -> object1
1005    JSObjectSetPrototype(context, object1, object1);
1006    result &= assertTrue(JSValueIsNull(context, JSObjectGetPrototype(context, object1)), "It is possible to assign self as a prototype");
1007
1008    // object1 -> object2 -> object1
1009    JSObjectSetPrototype(context, object2, object1);
1010    ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object2), object1));
1011    JSObjectSetPrototype(context, object1, object2);
1012    result &= assertTrue(JSValueIsNull(context, JSObjectGetPrototype(context, object1)), "It is possible to close a prototype chain cycle");
1013
1014    // object1 -> object2 -> object3 -> object1
1015    JSObjectSetPrototype(context, object2, object3);
1016    ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object2), object3));
1017    JSObjectSetPrototype(context, object1, object2);
1018    ASSERT(JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object1), object2));
1019    JSObjectSetPrototype(context, object3, object1);
1020    result &= assertTrue(!JSValueIsStrictEqual(context, JSObjectGetPrototype(context, object3), object1), "It is possible to close a prototype chain cycle");
1021
1022    JSValueRef exception;
1023    JSStringRef code = JSStringCreateWithUTF8CString("o = { }; p = { }; o.__proto__ = p; p.__proto__ = o");
1024    JSStringRef file = JSStringCreateWithUTF8CString("");
1025    result &= assertTrue(!JSEvaluateScript(context, code, /* thisObject*/ 0, file, 1, &exception)
1026                         , "An exception should be thrown");
1027
1028    JSStringRelease(code);
1029    JSStringRelease(file);
1030    JSGlobalContextRelease(context);
1031    return result;
1032}
1033
1034static JSValueRef valueToObjectExceptionCallAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
1035{
1036    UNUSED_PARAM(function);
1037    UNUSED_PARAM(thisObject);
1038    UNUSED_PARAM(argumentCount);
1039    UNUSED_PARAM(arguments);
1040    JSValueRef jsUndefined = JSValueMakeUndefined(JSContextGetGlobalContext(ctx));
1041    JSValueToObject(JSContextGetGlobalContext(ctx), jsUndefined, exception);
1042
1043    return JSValueMakeUndefined(ctx);
1044}
1045static bool valueToObjectExceptionTest()
1046{
1047    JSGlobalContextRef testContext;
1048    JSClassDefinition globalObjectClassDefinition = kJSClassDefinitionEmpty;
1049    globalObjectClassDefinition.initialize = globalObject_initialize;
1050    globalObjectClassDefinition.staticValues = globalObject_staticValues;
1051    globalObjectClassDefinition.staticFunctions = globalObject_staticFunctions;
1052    globalObjectClassDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
1053    JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition);
1054    testContext = JSGlobalContextCreateInGroup(NULL, globalObjectClass);
1055    JSObjectRef globalObject = JSContextGetGlobalObject(testContext);
1056
1057    JSStringRef valueToObject = JSStringCreateWithUTF8CString("valueToObject");
1058    JSObjectRef valueToObjectFunction = JSObjectMakeFunctionWithCallback(testContext, valueToObject, valueToObjectExceptionCallAsFunction);
1059    JSObjectSetProperty(testContext, globalObject, valueToObject, valueToObjectFunction, kJSPropertyAttributeNone, NULL);
1060    JSStringRelease(valueToObject);
1061
1062    JSStringRef test = JSStringCreateWithUTF8CString("valueToObject();");
1063    JSEvaluateScript(testContext, test, NULL, NULL, 1, NULL);
1064
1065    JSStringRelease(test);
1066    JSClassRelease(globalObjectClass);
1067    JSGlobalContextRelease(testContext);
1068
1069    return true;
1070}
1071
1072static bool globalContextNameTest()
1073{
1074    bool result = true;
1075    JSGlobalContextRef context = JSGlobalContextCreate(0);
1076
1077    JSStringRef str = JSGlobalContextCopyName(context);
1078    result &= assertTrue(!str, "Default context name is NULL");
1079
1080    JSStringRef name1 = JSStringCreateWithUTF8CString("name1");
1081    JSStringRef name2 = JSStringCreateWithUTF8CString("name2");
1082
1083    JSGlobalContextSetName(context, name1);
1084    JSStringRef fetchName1 = JSGlobalContextCopyName(context);
1085    JSGlobalContextSetName(context, name2);
1086    JSStringRef fetchName2 = JSGlobalContextCopyName(context);
1087    JSGlobalContextSetName(context, NULL);
1088    JSStringRef fetchName3 = JSGlobalContextCopyName(context);
1089
1090    result &= assertTrue(JSStringIsEqual(name1, fetchName1), "Unexpected Context name");
1091    result &= assertTrue(JSStringIsEqual(name2, fetchName2), "Unexpected Context name");
1092    result &= assertTrue(!JSStringIsEqual(fetchName1, fetchName2), "Unexpected Context name");
1093    result &= assertTrue(!fetchName3, "Unexpected Context name");
1094
1095    JSStringRelease(name1);
1096    JSStringRelease(name2);
1097    JSStringRelease(fetchName1);
1098    JSStringRelease(fetchName2);
1099
1100    return result;
1101}
1102
1103static void checkConstnessInJSObjectNames()
1104{
1105    JSStaticFunction fun;
1106    fun.name = "something";
1107    JSStaticValue val;
1108    val.name = "something";
1109}
1110
1111#if OS(DARWIN)
1112static double currentCPUTime()
1113{
1114    mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
1115    thread_basic_info_data_t info;
1116
1117    /* Get thread information */
1118    mach_port_t threadPort = mach_thread_self();
1119    thread_info(threadPort, THREAD_BASIC_INFO, (thread_info_t)(&info), &infoCount);
1120    mach_port_deallocate(mach_task_self(), threadPort);
1121
1122    double time = info.user_time.seconds + info.user_time.microseconds / 1000000.;
1123    time += info.system_time.seconds + info.system_time.microseconds / 1000000.;
1124
1125    return time;
1126}
1127
1128static JSValueRef currentCPUTime_callAsFunction(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
1129{
1130    UNUSED_PARAM(functionObject);
1131    UNUSED_PARAM(thisObject);
1132    UNUSED_PARAM(argumentCount);
1133    UNUSED_PARAM(arguments);
1134    UNUSED_PARAM(exception);
1135
1136    ASSERT(JSContextGetGlobalContext(ctx) == context);
1137    return JSValueMakeNumber(ctx, currentCPUTime());
1138}
1139
1140bool shouldTerminateCallbackWasCalled = false;
1141static bool shouldTerminateCallback(JSContextRef ctx, void* context)
1142{
1143    UNUSED_PARAM(ctx);
1144    UNUSED_PARAM(context);
1145    shouldTerminateCallbackWasCalled = true;
1146    return true;
1147}
1148
1149bool cancelTerminateCallbackWasCalled = false;
1150static bool cancelTerminateCallback(JSContextRef ctx, void* context)
1151{
1152    UNUSED_PARAM(ctx);
1153    UNUSED_PARAM(context);
1154    cancelTerminateCallbackWasCalled = true;
1155    return false;
1156}
1157
1158int extendTerminateCallbackCalled = 0;
1159static bool extendTerminateCallback(JSContextRef ctx, void* context)
1160{
1161    UNUSED_PARAM(context);
1162    extendTerminateCallbackCalled++;
1163    if (extendTerminateCallbackCalled == 1) {
1164        JSContextGroupRef contextGroup = JSContextGetGroup(ctx);
1165        JSContextGroupSetExecutionTimeLimit(contextGroup, .200f, extendTerminateCallback, 0);
1166        return false;
1167    }
1168    return true;
1169}
1170#endif /* OS(DARWIN) */
1171
1172
1173int main(int argc, char* argv[])
1174{
1175#if OS(WINDOWS)
1176    // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
1177    // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
1178    // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
1179    ::SetErrorMode(0);
1180#endif
1181
1182#if JSC_OBJC_API_ENABLED
1183    testObjectiveCAPI();
1184#endif
1185
1186    const char *scriptPath = "testapi.js";
1187    if (argc > 1) {
1188        scriptPath = argv[1];
1189    }
1190
1191    // Test garbage collection with a fresh context
1192    context = JSGlobalContextCreateInGroup(NULL, NULL);
1193    TestInitializeFinalize = true;
1194    testInitializeFinalize();
1195    JSGlobalContextRelease(context);
1196    TestInitializeFinalize = false;
1197
1198    ASSERT(Base_didFinalize);
1199
1200    JSClassDefinition globalObjectClassDefinition = kJSClassDefinitionEmpty;
1201    globalObjectClassDefinition.initialize = globalObject_initialize;
1202    globalObjectClassDefinition.staticValues = globalObject_staticValues;
1203    globalObjectClassDefinition.staticFunctions = globalObject_staticFunctions;
1204    globalObjectClassDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
1205    JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition);
1206    context = JSGlobalContextCreateInGroup(NULL, globalObjectClass);
1207
1208    JSContextGroupRef contextGroup = JSContextGetGroup(context);
1209
1210    JSGlobalContextRetain(context);
1211    JSGlobalContextRelease(context);
1212    ASSERT(JSContextGetGlobalContext(context) == context);
1213
1214    JSReportExtraMemoryCost(context, 0);
1215    JSReportExtraMemoryCost(context, 1);
1216    JSReportExtraMemoryCost(context, 1024);
1217
1218    JSObjectRef globalObject = JSContextGetGlobalObject(context);
1219    ASSERT(JSValueIsObject(context, globalObject));
1220
1221    JSValueRef jsUndefined = JSValueMakeUndefined(context);
1222    JSValueRef jsNull = JSValueMakeNull(context);
1223    JSValueRef jsTrue = JSValueMakeBoolean(context, true);
1224    JSValueRef jsFalse = JSValueMakeBoolean(context, false);
1225    JSValueRef jsZero = JSValueMakeNumber(context, 0);
1226    JSValueRef jsOne = JSValueMakeNumber(context, 1);
1227    JSValueRef jsOneThird = JSValueMakeNumber(context, 1.0 / 3.0);
1228    JSObjectRef jsObjectNoProto = JSObjectMake(context, NULL, NULL);
1229    JSObjectSetPrototype(context, jsObjectNoProto, JSValueMakeNull(context));
1230
1231    JSObjectSetPrivate(globalObject, (void*)123);
1232    if (JSObjectGetPrivate(globalObject) != (void*)123) {
1233        printf("FAIL: Didn't return private data when set by JSObjectSetPrivate().\n");
1234        failed = 1;
1235    } else
1236        printf("PASS: returned private data when set by JSObjectSetPrivate().\n");
1237
1238    // FIXME: test funny utf8 characters
1239    JSStringRef jsEmptyIString = JSStringCreateWithUTF8CString("");
1240    JSValueRef jsEmptyString = JSValueMakeString(context, jsEmptyIString);
1241
1242    JSStringRef jsOneIString = JSStringCreateWithUTF8CString("1");
1243    JSValueRef jsOneString = JSValueMakeString(context, jsOneIString);
1244
1245    UniChar singleUniChar = 65; // Capital A
1246    CFMutableStringRef cfString =
1247        CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
1248                                                          &singleUniChar,
1249                                                          1,
1250                                                          1,
1251                                                          kCFAllocatorNull);
1252
1253    JSStringRef jsCFIString = JSStringCreateWithCFString(cfString);
1254    JSValueRef jsCFString = JSValueMakeString(context, jsCFIString);
1255
1256    CFStringRef cfEmptyString = CFStringCreateWithCString(kCFAllocatorDefault, "", kCFStringEncodingUTF8);
1257
1258    JSStringRef jsCFEmptyIString = JSStringCreateWithCFString(cfEmptyString);
1259    JSValueRef jsCFEmptyString = JSValueMakeString(context, jsCFEmptyIString);
1260
1261    CFIndex cfStringLength = CFStringGetLength(cfString);
1262    UniChar* buffer = (UniChar*)malloc(cfStringLength * sizeof(UniChar));
1263    CFStringGetCharacters(cfString,
1264                          CFRangeMake(0, cfStringLength),
1265                          buffer);
1266    JSStringRef jsCFIStringWithCharacters = JSStringCreateWithCharacters((JSChar*)buffer, cfStringLength);
1267    JSValueRef jsCFStringWithCharacters = JSValueMakeString(context, jsCFIStringWithCharacters);
1268
1269    JSStringRef jsCFEmptyIStringWithCharacters = JSStringCreateWithCharacters((JSChar*)buffer, CFStringGetLength(cfEmptyString));
1270    free(buffer);
1271    JSValueRef jsCFEmptyStringWithCharacters = JSValueMakeString(context, jsCFEmptyIStringWithCharacters);
1272
1273    JSChar constantString[] = { 'H', 'e', 'l', 'l', 'o', };
1274    JSStringRef constantStringRef = JSStringCreateWithCharactersNoCopy(constantString, sizeof(constantString) / sizeof(constantString[0]));
1275    ASSERT(JSStringGetCharactersPtr(constantStringRef) == constantString);
1276    JSStringRelease(constantStringRef);
1277
1278    ASSERT(JSValueGetType(context, NULL) == kJSTypeNull);
1279    ASSERT(JSValueGetType(context, jsUndefined) == kJSTypeUndefined);
1280    ASSERT(JSValueGetType(context, jsNull) == kJSTypeNull);
1281    ASSERT(JSValueGetType(context, jsTrue) == kJSTypeBoolean);
1282    ASSERT(JSValueGetType(context, jsFalse) == kJSTypeBoolean);
1283    ASSERT(JSValueGetType(context, jsZero) == kJSTypeNumber);
1284    ASSERT(JSValueGetType(context, jsOne) == kJSTypeNumber);
1285    ASSERT(JSValueGetType(context, jsOneThird) == kJSTypeNumber);
1286    ASSERT(JSValueGetType(context, jsEmptyString) == kJSTypeString);
1287    ASSERT(JSValueGetType(context, jsOneString) == kJSTypeString);
1288    ASSERT(JSValueGetType(context, jsCFString) == kJSTypeString);
1289    ASSERT(JSValueGetType(context, jsCFStringWithCharacters) == kJSTypeString);
1290    ASSERT(JSValueGetType(context, jsCFEmptyString) == kJSTypeString);
1291    ASSERT(JSValueGetType(context, jsCFEmptyStringWithCharacters) == kJSTypeString);
1292
1293    ASSERT(!JSValueIsBoolean(context, NULL));
1294    ASSERT(!JSValueIsObject(context, NULL));
1295    ASSERT(!JSValueIsString(context, NULL));
1296    ASSERT(!JSValueIsNumber(context, NULL));
1297    ASSERT(!JSValueIsUndefined(context, NULL));
1298    ASSERT(JSValueIsNull(context, NULL));
1299    ASSERT(!JSObjectCallAsFunction(context, NULL, NULL, 0, NULL, NULL));
1300    ASSERT(!JSObjectCallAsConstructor(context, NULL, 0, NULL, NULL));
1301    ASSERT(!JSObjectIsConstructor(context, NULL));
1302    ASSERT(!JSObjectIsFunction(context, NULL));
1303
1304    JSStringRef nullString = JSStringCreateWithUTF8CString(0);
1305    const JSChar* characters = JSStringGetCharactersPtr(nullString);
1306    if (characters) {
1307        printf("FAIL: Didn't return null when accessing character pointer of a null String.\n");
1308        failed = 1;
1309    } else
1310        printf("PASS: returned null when accessing character pointer of a null String.\n");
1311
1312    JSStringRef emptyString = JSStringCreateWithCFString(CFSTR(""));
1313    characters = JSStringGetCharactersPtr(emptyString);
1314    if (!characters) {
1315        printf("FAIL: Returned null when accessing character pointer of an empty String.\n");
1316        failed = 1;
1317    } else
1318        printf("PASS: returned empty when accessing character pointer of an empty String.\n");
1319
1320    size_t length = JSStringGetLength(nullString);
1321    if (length) {
1322        printf("FAIL: Didn't return 0 length for null String.\n");
1323        failed = 1;
1324    } else
1325        printf("PASS: returned 0 length for null String.\n");
1326    JSStringRelease(nullString);
1327
1328    length = JSStringGetLength(emptyString);
1329    if (length) {
1330        printf("FAIL: Didn't return 0 length for empty String.\n");
1331        failed = 1;
1332    } else
1333        printf("PASS: returned 0 length for empty String.\n");
1334    JSStringRelease(emptyString);
1335
1336    JSObjectRef propertyCatchalls = JSObjectMake(context, PropertyCatchalls_class(context), NULL);
1337    JSStringRef propertyCatchallsString = JSStringCreateWithUTF8CString("PropertyCatchalls");
1338    JSObjectSetProperty(context, globalObject, propertyCatchallsString, propertyCatchalls, kJSPropertyAttributeNone, NULL);
1339    JSStringRelease(propertyCatchallsString);
1340
1341    JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL);
1342    JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject");
1343    JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone, NULL);
1344    JSStringRelease(myObjectIString);
1345
1346    JSObjectRef EvilExceptionObject = JSObjectMake(context, EvilExceptionObject_class(context), NULL);
1347    JSStringRef EvilExceptionObjectIString = JSStringCreateWithUTF8CString("EvilExceptionObject");
1348    JSObjectSetProperty(context, globalObject, EvilExceptionObjectIString, EvilExceptionObject, kJSPropertyAttributeNone, NULL);
1349    JSStringRelease(EvilExceptionObjectIString);
1350
1351    JSObjectRef EmptyObject = JSObjectMake(context, EmptyObject_class(context), NULL);
1352    JSStringRef EmptyObjectIString = JSStringCreateWithUTF8CString("EmptyObject");
1353    JSObjectSetProperty(context, globalObject, EmptyObjectIString, EmptyObject, kJSPropertyAttributeNone, NULL);
1354    JSStringRelease(EmptyObjectIString);
1355
1356    JSStringRef lengthStr = JSStringCreateWithUTF8CString("length");
1357    JSObjectRef aStackRef = JSObjectMakeArray(context, 0, 0, 0);
1358    aHeapRef = aStackRef;
1359    JSObjectSetProperty(context, aHeapRef, lengthStr, JSValueMakeNumber(context, 10), 0, 0);
1360    JSStringRef privatePropertyName = JSStringCreateWithUTF8CString("privateProperty");
1361    if (!JSObjectSetPrivateProperty(context, myObject, privatePropertyName, aHeapRef)) {
1362        printf("FAIL: Could not set private property.\n");
1363        failed = 1;
1364    } else
1365        printf("PASS: Set private property.\n");
1366    aStackRef = 0;
1367    if (JSObjectSetPrivateProperty(context, aHeapRef, privatePropertyName, aHeapRef)) {
1368        printf("FAIL: JSObjectSetPrivateProperty should fail on non-API objects.\n");
1369        failed = 1;
1370    } else
1371        printf("PASS: Did not allow JSObjectSetPrivateProperty on a non-API object.\n");
1372    if (JSObjectGetPrivateProperty(context, myObject, privatePropertyName) != aHeapRef) {
1373        printf("FAIL: Could not retrieve private property.\n");
1374        failed = 1;
1375    } else
1376        printf("PASS: Retrieved private property.\n");
1377    if (JSObjectGetPrivateProperty(context, aHeapRef, privatePropertyName)) {
1378        printf("FAIL: JSObjectGetPrivateProperty should return NULL when called on a non-API object.\n");
1379        failed = 1;
1380    } else
1381        printf("PASS: JSObjectGetPrivateProperty return NULL.\n");
1382
1383    if (JSObjectGetProperty(context, myObject, privatePropertyName, 0) == aHeapRef) {
1384        printf("FAIL: Accessed private property through ordinary property lookup.\n");
1385        failed = 1;
1386    } else
1387        printf("PASS: Cannot access private property through ordinary property lookup.\n");
1388
1389    JSGarbageCollect(context);
1390
1391    for (int i = 0; i < 10000; i++)
1392        JSObjectMake(context, 0, 0);
1393
1394    aHeapRef = JSValueToObject(context, JSObjectGetPrivateProperty(context, myObject, privatePropertyName), 0);
1395    if (JSValueToNumber(context, JSObjectGetProperty(context, aHeapRef, lengthStr, 0), 0) != 10) {
1396        printf("FAIL: Private property has been collected.\n");
1397        failed = 1;
1398    } else
1399        printf("PASS: Private property does not appear to have been collected.\n");
1400    JSStringRelease(lengthStr);
1401
1402    if (!JSObjectSetPrivateProperty(context, myObject, privatePropertyName, 0)) {
1403        printf("FAIL: Could not set private property to NULL.\n");
1404        failed = 1;
1405    } else
1406        printf("PASS: Set private property to NULL.\n");
1407    if (JSObjectGetPrivateProperty(context, myObject, privatePropertyName)) {
1408        printf("FAIL: Could not retrieve private property.\n");
1409        failed = 1;
1410    } else
1411        printf("PASS: Retrieved private property.\n");
1412
1413    JSStringRef nullJSON = JSStringCreateWithUTF8CString(0);
1414    JSValueRef nullJSONObject = JSValueMakeFromJSONString(context, nullJSON);
1415    if (nullJSONObject) {
1416        printf("FAIL: Did not parse null String as JSON correctly\n");
1417        failed = 1;
1418    } else
1419        printf("PASS: Parsed null String as JSON correctly.\n");
1420    JSStringRelease(nullJSON);
1421
1422    JSStringRef validJSON = JSStringCreateWithUTF8CString("{\"aProperty\":true}");
1423    JSValueRef jsonObject = JSValueMakeFromJSONString(context, validJSON);
1424    JSStringRelease(validJSON);
1425    if (!JSValueIsObject(context, jsonObject)) {
1426        printf("FAIL: Did not parse valid JSON correctly\n");
1427        failed = 1;
1428    } else
1429        printf("PASS: Parsed valid JSON string.\n");
1430    JSStringRef propertyName = JSStringCreateWithUTF8CString("aProperty");
1431    assertEqualsAsBoolean(JSObjectGetProperty(context, JSValueToObject(context, jsonObject, 0), propertyName, 0), true);
1432    JSStringRelease(propertyName);
1433    JSStringRef invalidJSON = JSStringCreateWithUTF8CString("fail!");
1434    if (JSValueMakeFromJSONString(context, invalidJSON)) {
1435        printf("FAIL: Should return null for invalid JSON data\n");
1436        failed = 1;
1437    } else
1438        printf("PASS: Correctly returned null for invalid JSON data.\n");
1439    JSValueRef exception;
1440    JSStringRef str = JSValueCreateJSONString(context, jsonObject, 0, 0);
1441    if (!JSStringIsEqualToUTF8CString(str, "{\"aProperty\":true}")) {
1442        printf("FAIL: Did not correctly serialise with indent of 0.\n");
1443        failed = 1;
1444    } else
1445        printf("PASS: Correctly serialised with indent of 0.\n");
1446    JSStringRelease(str);
1447
1448    str = JSValueCreateJSONString(context, jsonObject, 4, 0);
1449    if (!JSStringIsEqualToUTF8CString(str, "{\n    \"aProperty\": true\n}")) {
1450        printf("FAIL: Did not correctly serialise with indent of 4.\n");
1451        failed = 1;
1452    } else
1453        printf("PASS: Correctly serialised with indent of 4.\n");
1454    JSStringRelease(str);
1455    JSStringRef src = JSStringCreateWithUTF8CString("({get a(){ throw '';}})");
1456    JSValueRef unstringifiableObj = JSEvaluateScript(context, src, NULL, NULL, 1, NULL);
1457
1458    str = JSValueCreateJSONString(context, unstringifiableObj, 4, 0);
1459    if (str) {
1460        printf("FAIL: Didn't return null when attempting to serialize unserializable value.\n");
1461        JSStringRelease(str);
1462        failed = 1;
1463    } else
1464        printf("PASS: returned null when attempting to serialize unserializable value.\n");
1465
1466    str = JSValueCreateJSONString(context, unstringifiableObj, 4, &exception);
1467    if (str) {
1468        printf("FAIL: Didn't return null when attempting to serialize unserializable value.\n");
1469        JSStringRelease(str);
1470        failed = 1;
1471    } else
1472        printf("PASS: returned null when attempting to serialize unserializable value.\n");
1473    if (!exception) {
1474        printf("FAIL: Did not set exception on serialisation error\n");
1475        failed = 1;
1476    } else
1477        printf("PASS: set exception on serialisation error\n");
1478    // Conversions that throw exceptions
1479    exception = NULL;
1480    ASSERT(NULL == JSValueToObject(context, jsNull, &exception));
1481    ASSERT(exception);
1482
1483    exception = NULL;
1484    // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function,
1485    // causing a build break with -Wshorten-64-to-32 enabled.  The issue is known by the appropriate team.
1486    // After that's resolved, we can remove these casts
1487    ASSERT(isnan((float)JSValueToNumber(context, jsObjectNoProto, &exception)));
1488    ASSERT(exception);
1489
1490    exception = NULL;
1491    ASSERT(!JSValueToStringCopy(context, jsObjectNoProto, &exception));
1492    ASSERT(exception);
1493
1494    ASSERT(JSValueToBoolean(context, myObject));
1495
1496    exception = NULL;
1497    ASSERT(!JSValueIsEqual(context, jsObjectNoProto, JSValueMakeNumber(context, 1), &exception));
1498    ASSERT(exception);
1499
1500    exception = NULL;
1501    JSObjectGetPropertyAtIndex(context, myObject, 0, &exception);
1502    ASSERT(1 == JSValueToNumber(context, exception, NULL));
1503
1504    assertEqualsAsBoolean(jsUndefined, false);
1505    assertEqualsAsBoolean(jsNull, false);
1506    assertEqualsAsBoolean(jsTrue, true);
1507    assertEqualsAsBoolean(jsFalse, false);
1508    assertEqualsAsBoolean(jsZero, false);
1509    assertEqualsAsBoolean(jsOne, true);
1510    assertEqualsAsBoolean(jsOneThird, true);
1511    assertEqualsAsBoolean(jsEmptyString, false);
1512    assertEqualsAsBoolean(jsOneString, true);
1513    assertEqualsAsBoolean(jsCFString, true);
1514    assertEqualsAsBoolean(jsCFStringWithCharacters, true);
1515    assertEqualsAsBoolean(jsCFEmptyString, false);
1516    assertEqualsAsBoolean(jsCFEmptyStringWithCharacters, false);
1517
1518    assertEqualsAsNumber(jsUndefined, nan(""));
1519    assertEqualsAsNumber(jsNull, 0);
1520    assertEqualsAsNumber(jsTrue, 1);
1521    assertEqualsAsNumber(jsFalse, 0);
1522    assertEqualsAsNumber(jsZero, 0);
1523    assertEqualsAsNumber(jsOne, 1);
1524    assertEqualsAsNumber(jsOneThird, 1.0 / 3.0);
1525    assertEqualsAsNumber(jsEmptyString, 0);
1526    assertEqualsAsNumber(jsOneString, 1);
1527    assertEqualsAsNumber(jsCFString, nan(""));
1528    assertEqualsAsNumber(jsCFStringWithCharacters, nan(""));
1529    assertEqualsAsNumber(jsCFEmptyString, 0);
1530    assertEqualsAsNumber(jsCFEmptyStringWithCharacters, 0);
1531    ASSERT(sizeof(JSChar) == sizeof(UniChar));
1532
1533    assertEqualsAsCharactersPtr(jsUndefined, "undefined");
1534    assertEqualsAsCharactersPtr(jsNull, "null");
1535    assertEqualsAsCharactersPtr(jsTrue, "true");
1536    assertEqualsAsCharactersPtr(jsFalse, "false");
1537    assertEqualsAsCharactersPtr(jsZero, "0");
1538    assertEqualsAsCharactersPtr(jsOne, "1");
1539    assertEqualsAsCharactersPtr(jsOneThird, "0.3333333333333333");
1540    assertEqualsAsCharactersPtr(jsEmptyString, "");
1541    assertEqualsAsCharactersPtr(jsOneString, "1");
1542    assertEqualsAsCharactersPtr(jsCFString, "A");
1543    assertEqualsAsCharactersPtr(jsCFStringWithCharacters, "A");
1544    assertEqualsAsCharactersPtr(jsCFEmptyString, "");
1545    assertEqualsAsCharactersPtr(jsCFEmptyStringWithCharacters, "");
1546
1547    assertEqualsAsUTF8String(jsUndefined, "undefined");
1548    assertEqualsAsUTF8String(jsNull, "null");
1549    assertEqualsAsUTF8String(jsTrue, "true");
1550    assertEqualsAsUTF8String(jsFalse, "false");
1551    assertEqualsAsUTF8String(jsZero, "0");
1552    assertEqualsAsUTF8String(jsOne, "1");
1553    assertEqualsAsUTF8String(jsOneThird, "0.3333333333333333");
1554    assertEqualsAsUTF8String(jsEmptyString, "");
1555    assertEqualsAsUTF8String(jsOneString, "1");
1556    assertEqualsAsUTF8String(jsCFString, "A");
1557    assertEqualsAsUTF8String(jsCFStringWithCharacters, "A");
1558    assertEqualsAsUTF8String(jsCFEmptyString, "");
1559    assertEqualsAsUTF8String(jsCFEmptyStringWithCharacters, "");
1560
1561    checkConstnessInJSObjectNames();
1562
1563    ASSERT(JSValueIsStrictEqual(context, jsTrue, jsTrue));
1564    ASSERT(!JSValueIsStrictEqual(context, jsOne, jsOneString));
1565
1566    ASSERT(JSValueIsEqual(context, jsOne, jsOneString, NULL));
1567    ASSERT(!JSValueIsEqual(context, jsTrue, jsFalse, NULL));
1568
1569    CFStringRef cfJSString = JSStringCopyCFString(kCFAllocatorDefault, jsCFIString);
1570    CFStringRef cfJSEmptyString = JSStringCopyCFString(kCFAllocatorDefault, jsCFEmptyIString);
1571    ASSERT(CFEqual(cfJSString, cfString));
1572    ASSERT(CFEqual(cfJSEmptyString, cfEmptyString));
1573    CFRelease(cfJSString);
1574    CFRelease(cfJSEmptyString);
1575
1576    CFRelease(cfString);
1577    CFRelease(cfEmptyString);
1578
1579    jsGlobalValue = JSObjectMake(context, NULL, NULL);
1580    makeGlobalNumberValue(context);
1581    JSValueProtect(context, jsGlobalValue);
1582    JSGarbageCollect(context);
1583    ASSERT(JSValueIsObject(context, jsGlobalValue));
1584    JSValueUnprotect(context, jsGlobalValue);
1585    JSValueUnprotect(context, jsNumberValue);
1586
1587    JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;");
1588    const char* badSyntaxConstant = "x := 1;";
1589    JSStringRef badSyntax = JSStringCreateWithUTF8CString(badSyntaxConstant);
1590    ASSERT(JSCheckScriptSyntax(context, goodSyntax, NULL, 0, NULL));
1591    ASSERT(!JSCheckScriptSyntax(context, badSyntax, NULL, 0, NULL));
1592    ASSERT(!JSScriptCreateFromString(contextGroup, 0, 0, badSyntax, 0, 0));
1593    ASSERT(!JSScriptCreateReferencingImmortalASCIIText(contextGroup, 0, 0, badSyntaxConstant, strlen(badSyntaxConstant), 0, 0));
1594
1595    JSValueRef result;
1596    JSValueRef v;
1597    JSObjectRef o;
1598    JSStringRef string;
1599
1600    result = JSEvaluateScript(context, goodSyntax, NULL, NULL, 1, NULL);
1601    ASSERT(result);
1602    ASSERT(JSValueIsEqual(context, result, jsOne, NULL));
1603
1604    exception = NULL;
1605    result = JSEvaluateScript(context, badSyntax, NULL, NULL, 1, &exception);
1606    ASSERT(!result);
1607    ASSERT(JSValueIsObject(context, exception));
1608
1609    JSStringRef array = JSStringCreateWithUTF8CString("Array");
1610    JSObjectRef arrayConstructor = JSValueToObject(context, JSObjectGetProperty(context, globalObject, array, NULL), NULL);
1611    JSStringRelease(array);
1612    result = JSObjectCallAsConstructor(context, arrayConstructor, 0, NULL, NULL);
1613    ASSERT(result);
1614    ASSERT(JSValueIsObject(context, result));
1615    ASSERT(JSValueIsInstanceOfConstructor(context, result, arrayConstructor, NULL));
1616    ASSERT(!JSValueIsInstanceOfConstructor(context, JSValueMakeNull(context), arrayConstructor, NULL));
1617
1618    o = JSValueToObject(context, result, NULL);
1619    exception = NULL;
1620    ASSERT(JSValueIsUndefined(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception)));
1621    ASSERT(!exception);
1622
1623    JSObjectSetPropertyAtIndex(context, o, 0, JSValueMakeNumber(context, 1), &exception);
1624    ASSERT(!exception);
1625
1626    exception = NULL;
1627    ASSERT(1 == JSValueToNumber(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception), &exception));
1628    ASSERT(!exception);
1629
1630    JSStringRef functionBody;
1631    JSObjectRef function;
1632
1633    exception = NULL;
1634    functionBody = JSStringCreateWithUTF8CString("rreturn Array;");
1635    JSStringRef line = JSStringCreateWithUTF8CString("line");
1636    ASSERT(!JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception));
1637    ASSERT(JSValueIsObject(context, exception));
1638    v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line, NULL);
1639    assertEqualsAsNumber(v, 1);
1640    JSStringRelease(functionBody);
1641    JSStringRelease(line);
1642
1643    exception = NULL;
1644    functionBody = JSStringCreateWithUTF8CString("rreturn Array;");
1645    line = JSStringCreateWithUTF8CString("line");
1646    ASSERT(!JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, -42, &exception));
1647    ASSERT(JSValueIsObject(context, exception));
1648    v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line, NULL);
1649    assertEqualsAsNumber(v, 1);
1650    JSStringRelease(functionBody);
1651    JSStringRelease(line);
1652
1653    exception = NULL;
1654    functionBody = JSStringCreateWithUTF8CString("// Line one.\nrreturn Array;");
1655    line = JSStringCreateWithUTF8CString("line");
1656    ASSERT(!JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception));
1657    ASSERT(JSValueIsObject(context, exception));
1658    v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line, NULL);
1659    assertEqualsAsNumber(v, 2);
1660    JSStringRelease(functionBody);
1661    JSStringRelease(line);
1662
1663    exception = NULL;
1664    functionBody = JSStringCreateWithUTF8CString("return Array;");
1665    function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception);
1666    JSStringRelease(functionBody);
1667    ASSERT(!exception);
1668    ASSERT(JSObjectIsFunction(context, function));
1669    v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
1670    ASSERT(v);
1671    ASSERT(JSValueIsEqual(context, v, arrayConstructor, NULL));
1672
1673    exception = NULL;
1674    function = JSObjectMakeFunction(context, NULL, 0, NULL, jsEmptyIString, NULL, 0, &exception);
1675    ASSERT(!exception);
1676    v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, &exception);
1677    ASSERT(v && !exception);
1678    ASSERT(JSValueIsUndefined(context, v));
1679
1680    exception = NULL;
1681    v = NULL;
1682    JSStringRef foo = JSStringCreateWithUTF8CString("foo");
1683    JSStringRef argumentNames[] = { foo };
1684    functionBody = JSStringCreateWithUTF8CString("return foo;");
1685    function = JSObjectMakeFunction(context, foo, 1, argumentNames, functionBody, NULL, 1, &exception);
1686    ASSERT(function && !exception);
1687    JSValueRef arguments[] = { JSValueMakeNumber(context, 2) };
1688    JSObjectCallAsFunction(context, function, NULL, 1, arguments, &exception);
1689    JSStringRelease(foo);
1690    JSStringRelease(functionBody);
1691
1692    string = JSValueToStringCopy(context, function, NULL);
1693    assertEqualsAsUTF8String(JSValueMakeString(context, string), "function foo(foo) { return foo;\n}");
1694    JSStringRelease(string);
1695
1696    JSStringRef print = JSStringCreateWithUTF8CString("print");
1697    JSObjectRef printFunction = JSObjectMakeFunctionWithCallback(context, print, print_callAsFunction);
1698    JSObjectSetProperty(context, globalObject, print, printFunction, kJSPropertyAttributeNone, NULL);
1699    JSStringRelease(print);
1700
1701    ASSERT(!JSObjectSetPrivate(printFunction, (void*)1));
1702    ASSERT(!JSObjectGetPrivate(printFunction));
1703
1704    JSStringRef myConstructorIString = JSStringCreateWithUTF8CString("MyConstructor");
1705    JSObjectRef myConstructor = JSObjectMakeConstructor(context, NULL, myConstructor_callAsConstructor);
1706    JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone, NULL);
1707    JSStringRelease(myConstructorIString);
1708
1709    JSStringRef myBadConstructorIString = JSStringCreateWithUTF8CString("MyBadConstructor");
1710    JSObjectRef myBadConstructor = JSObjectMakeConstructor(context, NULL, myBadConstructor_callAsConstructor);
1711    JSObjectSetProperty(context, globalObject, myBadConstructorIString, myBadConstructor, kJSPropertyAttributeNone, NULL);
1712    JSStringRelease(myBadConstructorIString);
1713
1714    ASSERT(!JSObjectSetPrivate(myConstructor, (void*)1));
1715    ASSERT(!JSObjectGetPrivate(myConstructor));
1716
1717    string = JSStringCreateWithUTF8CString("Base");
1718    JSObjectRef baseConstructor = JSObjectMakeConstructor(context, Base_class(context), NULL);
1719    JSObjectSetProperty(context, globalObject, string, baseConstructor, kJSPropertyAttributeNone, NULL);
1720    JSStringRelease(string);
1721
1722    string = JSStringCreateWithUTF8CString("Derived");
1723    JSObjectRef derivedConstructor = JSObjectMakeConstructor(context, Derived_class(context), NULL);
1724    JSObjectSetProperty(context, globalObject, string, derivedConstructor, kJSPropertyAttributeNone, NULL);
1725    JSStringRelease(string);
1726
1727    string = JSStringCreateWithUTF8CString("Derived2");
1728    JSObjectRef derived2Constructor = JSObjectMakeConstructor(context, Derived2_class(context), NULL);
1729    JSObjectSetProperty(context, globalObject, string, derived2Constructor, kJSPropertyAttributeNone, NULL);
1730    JSStringRelease(string);
1731
1732    o = JSObjectMake(context, NULL, NULL);
1733    JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeNone, NULL);
1734    JSObjectSetProperty(context, o, jsCFIString,  JSValueMakeNumber(context, 1), kJSPropertyAttributeDontEnum, NULL);
1735    JSPropertyNameArrayRef nameArray = JSObjectCopyPropertyNames(context, o);
1736    size_t expectedCount = JSPropertyNameArrayGetCount(nameArray);
1737    size_t count;
1738    for (count = 0; count < expectedCount; ++count)
1739        JSPropertyNameArrayGetNameAtIndex(nameArray, count);
1740    JSPropertyNameArrayRelease(nameArray);
1741    ASSERT(count == 1); // jsCFString should not be enumerated
1742
1743    JSValueRef argumentsArrayValues[] = { JSValueMakeNumber(context, 10), JSValueMakeNumber(context, 20) };
1744    o = JSObjectMakeArray(context, sizeof(argumentsArrayValues) / sizeof(JSValueRef), argumentsArrayValues, NULL);
1745    string = JSStringCreateWithUTF8CString("length");
1746    v = JSObjectGetProperty(context, o, string, NULL);
1747    assertEqualsAsNumber(v, 2);
1748    v = JSObjectGetPropertyAtIndex(context, o, 0, NULL);
1749    assertEqualsAsNumber(v, 10);
1750    v = JSObjectGetPropertyAtIndex(context, o, 1, NULL);
1751    assertEqualsAsNumber(v, 20);
1752
1753    o = JSObjectMakeArray(context, 0, NULL, NULL);
1754    v = JSObjectGetProperty(context, o, string, NULL);
1755    assertEqualsAsNumber(v, 0);
1756    JSStringRelease(string);
1757
1758    JSValueRef argumentsDateValues[] = { JSValueMakeNumber(context, 0) };
1759    o = JSObjectMakeDate(context, 1, argumentsDateValues, NULL);
1760    if (timeZoneIsPST())
1761        assertEqualsAsUTF8String(o, "Wed Dec 31 1969 16:00:00 GMT-0800 (PST)");
1762
1763    string = JSStringCreateWithUTF8CString("an error message");
1764    JSValueRef argumentsErrorValues[] = { JSValueMakeString(context, string) };
1765    o = JSObjectMakeError(context, 1, argumentsErrorValues, NULL);
1766    assertEqualsAsUTF8String(o, "Error: an error message");
1767    JSStringRelease(string);
1768
1769    string = JSStringCreateWithUTF8CString("foo");
1770    JSStringRef string2 = JSStringCreateWithUTF8CString("gi");
1771    JSValueRef argumentsRegExpValues[] = { JSValueMakeString(context, string), JSValueMakeString(context, string2) };
1772    o = JSObjectMakeRegExp(context, 2, argumentsRegExpValues, NULL);
1773    assertEqualsAsUTF8String(o, "/foo/gi");
1774    JSStringRelease(string);
1775    JSStringRelease(string2);
1776
1777    JSClassDefinition nullDefinition = kJSClassDefinitionEmpty;
1778    nullDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
1779    JSClassRef nullClass = JSClassCreate(&nullDefinition);
1780    JSClassRelease(nullClass);
1781
1782    nullDefinition = kJSClassDefinitionEmpty;
1783    nullClass = JSClassCreate(&nullDefinition);
1784    JSClassRelease(nullClass);
1785
1786    functionBody = JSStringCreateWithUTF8CString("return this;");
1787    function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, NULL);
1788    JSStringRelease(functionBody);
1789    v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
1790    ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
1791    v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
1792    ASSERT(JSValueIsEqual(context, v, o, NULL));
1793
1794    functionBody = JSStringCreateWithUTF8CString("return eval(\"this\");");
1795    function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, NULL);
1796    JSStringRelease(functionBody);
1797    v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
1798    ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
1799    v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
1800    ASSERT(JSValueIsEqual(context, v, o, NULL));
1801
1802    const char* thisScript = "this;";
1803    JSStringRef script = JSStringCreateWithUTF8CString(thisScript);
1804    v = JSEvaluateScript(context, script, NULL, NULL, 1, NULL);
1805    ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
1806    v = JSEvaluateScript(context, script, o, NULL, 1, NULL);
1807    ASSERT(JSValueIsEqual(context, v, o, NULL));
1808    JSStringRelease(script);
1809
1810    JSScriptRef scriptObject = JSScriptCreateReferencingImmortalASCIIText(contextGroup, 0, 0, thisScript, strlen(thisScript), 0, 0);
1811    v = JSScriptEvaluate(context, scriptObject, NULL, NULL);
1812    ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
1813    v = JSScriptEvaluate(context, scriptObject, o, NULL);
1814    ASSERT(JSValueIsEqual(context, v, o, NULL));
1815    JSScriptRelease(scriptObject);
1816
1817    script = JSStringCreateWithUTF8CString("eval(this);");
1818    v = JSEvaluateScript(context, script, NULL, NULL, 1, NULL);
1819    ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
1820    v = JSEvaluateScript(context, script, o, NULL, 1, NULL);
1821    ASSERT(JSValueIsEqual(context, v, o, NULL));
1822    JSStringRelease(script);
1823
1824    exception = NULL;
1825    script = JSStringCreateWithUTF8CString("rreturn Array;");
1826    JSStringRef sourceURL = JSStringCreateWithUTF8CString("file:///foo/bar.js");
1827    JSStringRef sourceURLKey = JSStringCreateWithUTF8CString("sourceURL");
1828    JSEvaluateScript(context, script, NULL, sourceURL, 1, &exception);
1829    ASSERT(exception);
1830    v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), sourceURLKey, NULL);
1831    assertEqualsAsUTF8String(v, "file:///foo/bar.js");
1832    JSStringRelease(script);
1833    JSStringRelease(sourceURL);
1834    JSStringRelease(sourceURLKey);
1835
1836    // Verify that creating a constructor for a class with no static functions does not trigger
1837    // an assert inside putDirect or lead to a crash during GC. <https://bugs.webkit.org/show_bug.cgi?id=25785>
1838    nullDefinition = kJSClassDefinitionEmpty;
1839    nullClass = JSClassCreate(&nullDefinition);
1840    JSObjectMakeConstructor(context, nullClass, 0);
1841    JSClassRelease(nullClass);
1842
1843    char* scriptUTF8 = createStringWithContentsOfFile(scriptPath);
1844    if (!scriptUTF8) {
1845        printf("FAIL: Test script could not be loaded.\n");
1846        failed = 1;
1847    } else {
1848        JSStringRef url = JSStringCreateWithUTF8CString(scriptPath);
1849        JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
1850        JSStringRef errorMessage = 0;
1851        int errorLine = 0;
1852        JSScriptRef scriptObject = JSScriptCreateFromString(contextGroup, url, 1, script, &errorMessage, &errorLine);
1853        ASSERT((!scriptObject) != (!errorMessage));
1854        if (!scriptObject) {
1855            printf("FAIL: Test script did not parse\n\t%s:%d\n\t", scriptPath, errorLine);
1856            CFStringRef errorCF = JSStringCopyCFString(kCFAllocatorDefault, errorMessage);
1857            CFShow(errorCF);
1858            CFRelease(errorCF);
1859            JSStringRelease(errorMessage);
1860            failed = 1;
1861        }
1862
1863        JSStringRelease(script);
1864        exception = NULL;
1865        result = scriptObject ? JSScriptEvaluate(context, scriptObject, 0, &exception) : 0;
1866        if (result && JSValueIsUndefined(context, result))
1867            printf("PASS: Test script executed successfully.\n");
1868        else {
1869            printf("FAIL: Test script returned unexpected value:\n");
1870            JSStringRef exceptionIString = JSValueToStringCopy(context, exception, NULL);
1871            CFStringRef exceptionCF = JSStringCopyCFString(kCFAllocatorDefault, exceptionIString);
1872            CFShow(exceptionCF);
1873            CFRelease(exceptionCF);
1874            JSStringRelease(exceptionIString);
1875            failed = 1;
1876        }
1877        JSScriptRelease(scriptObject);
1878        free(scriptUTF8);
1879    }
1880
1881#if OS(DARWIN)
1882    JSStringRef currentCPUTimeStr = JSStringCreateWithUTF8CString("currentCPUTime");
1883    JSObjectRef currentCPUTimeFunction = JSObjectMakeFunctionWithCallback(context, currentCPUTimeStr, currentCPUTime_callAsFunction);
1884    JSObjectSetProperty(context, globalObject, currentCPUTimeStr, currentCPUTimeFunction, kJSPropertyAttributeNone, NULL);
1885    JSStringRelease(currentCPUTimeStr);
1886
1887    /* Test script timeout: */
1888    JSContextGroupSetExecutionTimeLimit(contextGroup, .10f, shouldTerminateCallback, 0);
1889    {
1890        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
1891        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
1892        double startTime;
1893        double endTime;
1894        exception = NULL;
1895        shouldTerminateCallbackWasCalled = false;
1896        startTime = currentCPUTime();
1897        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
1898        endTime = currentCPUTime();
1899
1900        if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
1901            printf("PASS: script timed out as expected.\n");
1902        else {
1903            if (!((endTime - startTime) < .150f))
1904                printf("FAIL: script did not timed out as expected.\n");
1905            if (!shouldTerminateCallbackWasCalled)
1906                printf("FAIL: script timeout callback was not called.\n");
1907            failed = true;
1908        }
1909
1910        if (!exception) {
1911            printf("FAIL: TerminatedExecutionException was not thrown.\n");
1912            failed = true;
1913        }
1914    }
1915
1916    /* Test the script timeout's TerminatedExecutionException should NOT be catchable: */
1917    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, shouldTerminateCallback, 0);
1918    {
1919        const char* loopForeverScript = "var startTime = currentCPUTime(); try { while (true) { if (currentCPUTime() - startTime > .150) break; } } catch(e) { }";
1920        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
1921        double startTime;
1922        double endTime;
1923        exception = NULL;
1924        shouldTerminateCallbackWasCalled = false;
1925        startTime = currentCPUTime();
1926        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
1927        endTime = currentCPUTime();
1928
1929        if (((endTime - startTime) >= .150f) || !shouldTerminateCallbackWasCalled) {
1930            if (!((endTime - startTime) < .150f))
1931                printf("FAIL: script did not timed out as expected.\n");
1932            if (!shouldTerminateCallbackWasCalled)
1933                printf("FAIL: script timeout callback was not called.\n");
1934            failed = true;
1935        }
1936
1937        if (exception)
1938            printf("PASS: TerminatedExecutionException was not catchable as expected.\n");
1939        else {
1940            printf("FAIL: TerminatedExecutionException was caught.\n");
1941            failed = true;
1942        }
1943    }
1944
1945    /* Test script timeout with no callback: */
1946    JSContextGroupSetExecutionTimeLimit(contextGroup, .10f, 0, 0);
1947    {
1948        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
1949        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
1950        double startTime;
1951        double endTime;
1952        exception = NULL;
1953        startTime = currentCPUTime();
1954        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
1955        endTime = currentCPUTime();
1956
1957        if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
1958            printf("PASS: script timed out as expected when no callback is specified.\n");
1959        else {
1960            if (!((endTime - startTime) < .150f))
1961                printf("FAIL: script did not timed out as expected when no callback is specified.\n");
1962            failed = true;
1963        }
1964
1965        if (!exception) {
1966            printf("FAIL: TerminatedExecutionException was not thrown.\n");
1967            failed = true;
1968        }
1969    }
1970
1971    /* Test script timeout cancellation: */
1972    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, cancelTerminateCallback, 0);
1973    {
1974        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
1975        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
1976        double startTime;
1977        double endTime;
1978        exception = NULL;
1979        startTime = currentCPUTime();
1980        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
1981        endTime = currentCPUTime();
1982
1983        if (((endTime - startTime) >= .150f) && cancelTerminateCallbackWasCalled && !exception)
1984            printf("PASS: script timeout was cancelled as expected.\n");
1985        else {
1986            if (((endTime - startTime) < .150) || exception)
1987                printf("FAIL: script timeout was not cancelled.\n");
1988            if (!cancelTerminateCallbackWasCalled)
1989                printf("FAIL: script timeout callback was not called.\n");
1990            failed = true;
1991        }
1992
1993        if (exception) {
1994            printf("FAIL: Unexpected TerminatedExecutionException thrown.\n");
1995            failed = true;
1996        }
1997    }
1998
1999    /* Test script timeout extension: */
2000    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.100f, extendTerminateCallback, 0);
2001    {
2002        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .500) break; } ";
2003        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
2004        double startTime;
2005        double endTime;
2006        double deltaTime;
2007        exception = NULL;
2008        startTime = currentCPUTime();
2009        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
2010        endTime = currentCPUTime();
2011        deltaTime = endTime - startTime;
2012
2013        if ((deltaTime >= .300f) && (deltaTime < .500f) && (extendTerminateCallbackCalled == 2) && exception)
2014            printf("PASS: script timeout was extended as expected.\n");
2015        else {
2016            if (deltaTime < .200f)
2017                printf("FAIL: script timeout was not extended as expected.\n");
2018            else if (deltaTime >= .500f)
2019                printf("FAIL: script did not timeout.\n");
2020
2021            if (extendTerminateCallbackCalled < 1)
2022                printf("FAIL: script timeout callback was not called.\n");
2023            if (extendTerminateCallbackCalled < 2)
2024                printf("FAIL: script timeout callback was not called after timeout extension.\n");
2025
2026            if (!exception)
2027                printf("FAIL: TerminatedExecutionException was not thrown during timeout extension test.\n");
2028
2029            failed = true;
2030        }
2031    }
2032#endif /* OS(DARWIN) */
2033
2034    // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
2035    function = NULL;
2036    v = NULL;
2037    o = NULL;
2038    globalObject = NULL;
2039    myConstructor = NULL;
2040
2041    JSStringRelease(jsEmptyIString);
2042    JSStringRelease(jsOneIString);
2043    JSStringRelease(jsCFIString);
2044    JSStringRelease(jsCFEmptyIString);
2045    JSStringRelease(jsCFIStringWithCharacters);
2046    JSStringRelease(jsCFEmptyIStringWithCharacters);
2047    JSStringRelease(goodSyntax);
2048    JSStringRelease(badSyntax);
2049
2050    JSGlobalContextRelease(context);
2051    JSClassRelease(globalObjectClass);
2052
2053    // Test for an infinite prototype chain that used to be created. This test
2054    // passes if the call to JSObjectHasProperty() does not hang.
2055
2056    JSClassDefinition prototypeLoopClassDefinition = kJSClassDefinitionEmpty;
2057    prototypeLoopClassDefinition.staticFunctions = globalObject_staticFunctions;
2058    JSClassRef prototypeLoopClass = JSClassCreate(&prototypeLoopClassDefinition);
2059    JSGlobalContextRef prototypeLoopContext = JSGlobalContextCreateInGroup(NULL, prototypeLoopClass);
2060
2061    JSStringRef nameProperty = JSStringCreateWithUTF8CString("name");
2062    JSObjectHasProperty(prototypeLoopContext, JSContextGetGlobalObject(prototypeLoopContext), nameProperty);
2063
2064    JSGlobalContextRelease(prototypeLoopContext);
2065    JSClassRelease(prototypeLoopClass);
2066
2067    printf("PASS: Infinite prototype chain does not occur.\n");
2068
2069    if (checkForCycleInPrototypeChain())
2070        printf("PASS: A cycle in a prototype chain can't be created.\n");
2071    else {
2072        printf("FAIL: A cycle in a prototype chain can be created.\n");
2073        failed = true;
2074    }
2075    if (valueToObjectExceptionTest())
2076        printf("PASS: throwException did not crash when handling an error with appendMessageToError set and no codeBlock available.\n");
2077
2078    if (globalContextNameTest())
2079        printf("PASS: global context name behaves as expected.\n");
2080
2081    customGlobalObjectClassTest();
2082
2083    if (failed) {
2084        printf("FAIL: Some tests failed.\n");
2085        return 1;
2086    }
2087
2088    printf("PASS: Program exited normally.\n");
2089    return 0;
2090}
2091
2092static char* createStringWithContentsOfFile(const char* fileName)
2093{
2094    char* buffer;
2095
2096    size_t buffer_size = 0;
2097    size_t buffer_capacity = 1024;
2098    buffer = (char*)malloc(buffer_capacity);
2099
2100    FILE* f = fopen(fileName, "r");
2101    if (!f) {
2102        fprintf(stderr, "Could not open file: %s\n", fileName);
2103        free(buffer);
2104        return 0;
2105    }
2106
2107    while (!feof(f) && !ferror(f)) {
2108        buffer_size += fread(buffer + buffer_size, 1, buffer_capacity - buffer_size, f);
2109        if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
2110            buffer_capacity *= 2;
2111            buffer = (char*)realloc(buffer, buffer_capacity);
2112            ASSERT(buffer);
2113        }
2114
2115        ASSERT(buffer_size < buffer_capacity);
2116    }
2117    fclose(f);
2118    buffer[buffer_size] = '\0';
2119
2120    return buffer;
2121}
2122