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