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