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