Allow override of default script file name using command-line argument.
[WebKit-https.git] / JavaScriptCore / API / testapi.c
1 // -*- mode: c++; c-basic-offset: 4 -*-
2 /*
3  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "JavaScriptCore.h"
28 #include <math.h>
29 #include <wtf/Assertions.h>
30 #include <wtf/UnusedParam.h>
31
32 static JSGlobalContextRef context = 0;
33
34 static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
35 {
36     if (JSValueToBoolean(context, value) != expectedValue)
37         fprintf(stderr, "assertEqualsAsBoolean failed: %p, %d\n", value, expectedValue);
38 }
39
40 static void assertEqualsAsNumber(JSValueRef value, double expectedValue)
41 {
42     double number = JSValueToNumber(context, value, NULL);
43
44     // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function,
45     // causing a build break with -Wshorten-64-to-32 enabled.  The issue is known by the appropriate team.
46     // After that's resolved, we can remove these casts
47     if (number != expectedValue && !(isnan((float)number) && isnan((float)expectedValue)))
48         fprintf(stderr, "assertEqualsAsNumber failed: %p, %lf\n", value, expectedValue);
49 }
50
51 static void assertEqualsAsUTF8String(JSValueRef value, const char* expectedValue)
52 {
53     JSStringRef valueAsString = JSValueToStringCopy(context, value, NULL);
54
55     size_t jsSize = JSStringGetMaximumUTF8CStringSize(valueAsString);
56     char jsBuffer[jsSize];
57     JSStringGetUTF8CString(valueAsString, jsBuffer, jsSize);
58     
59     unsigned i;
60     for (i = 0; jsBuffer[i]; i++)
61         if (jsBuffer[i] != expectedValue[i])
62             fprintf(stderr, "assertEqualsAsUTF8String failed at character %d: %c(%d) != %c(%d)\n", i, jsBuffer[i], jsBuffer[i], expectedValue[i], expectedValue[i]);
63         
64     if (jsSize < strlen(jsBuffer) + 1)
65         fprintf(stderr, "assertEqualsAsUTF8String failed: jsSize was too small\n");
66
67     JSStringRelease(valueAsString);
68 }
69
70 static void assertEqualsAsCharactersPtr(JSValueRef value, const char* expectedValue)
71 {
72     JSStringRef valueAsString = JSValueToStringCopy(context, value, NULL);
73
74     size_t jsLength = JSStringGetLength(valueAsString);
75     const JSChar* jsBuffer = JSStringGetCharactersPtr(valueAsString);
76
77     CFStringRef expectedValueAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, 
78                                                                     expectedValue,
79                                                                     kCFStringEncodingUTF8);    
80     CFIndex cfLength = CFStringGetLength(expectedValueAsCFString);
81     UniChar cfBuffer[cfLength];
82     CFStringGetCharacters(expectedValueAsCFString, CFRangeMake(0, cfLength), cfBuffer);
83     CFRelease(expectedValueAsCFString);
84
85     if (memcmp(jsBuffer, cfBuffer, cfLength * sizeof(UniChar)) != 0)
86         fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsBuffer != cfBuffer\n");
87     
88     if (jsLength != (size_t)cfLength)
89         fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsLength(%ld) != cfLength(%ld)\n", jsLength, cfLength);
90     
91     JSStringRelease(valueAsString);
92 }
93
94 static JSValueRef jsGlobalValue; // non-stack value for testing JSValueProtect()
95
96 /* MyObject pseudo-class */
97
98 static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName)
99 {
100     UNUSED_PARAM(context);
101     UNUSED_PARAM(object);
102
103     if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")
104         || JSStringIsEqualToUTF8CString(propertyName, "cantFind")
105         || JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")
106         || JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie")
107         || JSStringIsEqualToUTF8CString(propertyName, "0")) {
108         return true;
109     }
110     
111     return false;
112 }
113
114 static JSValueRef MyObject_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
115 {
116     UNUSED_PARAM(context);
117     UNUSED_PARAM(object);
118     
119     if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")) {
120         return JSValueMakeNumber(context, 1);
121     }
122     
123     if (JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")) {
124         return JSValueMakeNumber(context, 1);
125     }
126
127     if (JSStringIsEqualToUTF8CString(propertyName, "cantFind")) {
128         return JSValueMakeUndefined(context);
129     }
130     
131     if (JSStringIsEqualToUTF8CString(propertyName, "0")) {
132         *exception = JSValueMakeNumber(context, 1);
133         return JSValueMakeNumber(context, 1);
134     }
135     
136     return NULL;
137 }
138
139 static bool MyObject_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
140 {
141     UNUSED_PARAM(context);
142     UNUSED_PARAM(object);
143     UNUSED_PARAM(value);
144     UNUSED_PARAM(exception);
145
146     if (JSStringIsEqualToUTF8CString(propertyName, "cantSet"))
147         return true; // pretend we set the property in order to swallow it
148     
149     return false;
150 }
151
152 static bool MyObject_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
153 {
154     UNUSED_PARAM(context);
155     UNUSED_PARAM(object);
156     
157     if (JSStringIsEqualToUTF8CString(propertyName, "cantDelete"))
158         return true;
159     
160     if (JSStringIsEqualToUTF8CString(propertyName, "throwOnDelete")) {
161         *exception = JSValueMakeNumber(context, 2);
162         return false;
163     }
164
165     return false;
166 }
167
168 static void MyObject_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames)
169 {
170     UNUSED_PARAM(context);
171     UNUSED_PARAM(object);
172     
173     JSStringRef propertyName;
174     
175     propertyName = JSStringCreateWithUTF8CString("alwaysOne");
176     JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
177     JSStringRelease(propertyName);
178     
179     propertyName = JSStringCreateWithUTF8CString("myPropertyName");
180     JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
181     JSStringRelease(propertyName);
182 }
183
184 static JSValueRef MyObject_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
185 {
186     UNUSED_PARAM(context);
187     UNUSED_PARAM(object);
188     UNUSED_PARAM(thisObject);
189     UNUSED_PARAM(exception);
190
191     if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0)))
192         return JSValueMakeNumber(context, 1);
193     
194     return JSValueMakeUndefined(context);
195 }
196
197 static JSObjectRef MyObject_callAsConstructor(JSContextRef context, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
198 {
199     UNUSED_PARAM(context);
200     UNUSED_PARAM(object);
201
202     if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0)))
203         return JSValueToObject(context, JSValueMakeNumber(context, 1), exception);
204     
205     return JSValueToObject(context, JSValueMakeNumber(context, 0), exception);
206 }
207
208 static bool MyObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception)
209 {
210     UNUSED_PARAM(context);
211     UNUSED_PARAM(constructor);
212
213     JSStringRef numberString = JSStringCreateWithUTF8CString("Number");
214     JSObjectRef numberConstructor = JSValueToObject(context, JSObjectGetProperty(context, JSContextGetGlobalObject(context), numberString, exception), exception);
215     JSStringRelease(numberString);
216
217     return JSValueIsInstanceOfConstructor(context, possibleValue, numberConstructor, exception);
218 }
219
220 static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
221 {
222     UNUSED_PARAM(object);
223     UNUSED_PARAM(exception);
224     
225     switch (type) {
226     case kJSTypeNumber:
227         return JSValueMakeNumber(context, 1);
228     default:
229         break;
230     }
231
232     // string conversion -- forward to default object class
233     return NULL;
234 }
235
236 static JSStaticValue evilStaticValues[] = {
237     { "nullGetSet", 0, 0, kJSPropertyAttributeNone },
238     { 0, 0, 0, 0 }
239 };
240
241 static JSStaticFunction evilStaticFunctions[] = {
242     { "nullCall", 0, kJSPropertyAttributeNone },
243     { 0, 0, 0 }
244 };
245
246 JSClassDefinition MyObject_definition = {
247     0,
248     kJSClassAttributeNone,
249     
250     "MyObject",
251     NULL,
252     
253     evilStaticValues,
254     evilStaticFunctions,
255     
256     NULL,
257     NULL,
258     MyObject_hasProperty,
259     MyObject_getProperty,
260     MyObject_setProperty,
261     MyObject_deleteProperty,
262     MyObject_getPropertyNames,
263     MyObject_callAsFunction,
264     MyObject_callAsConstructor,
265     MyObject_hasInstance,
266     MyObject_convertToType,
267 };
268
269 static JSClassRef MyObject_class(JSContextRef context)
270 {
271     UNUSED_PARAM(context);
272
273     static JSClassRef jsClass;
274     if (!jsClass)
275         jsClass = JSClassCreate(&MyObject_definition);
276     
277     return jsClass;
278 }
279
280 static JSValueRef Base_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
281 {
282     UNUSED_PARAM(object);
283     UNUSED_PARAM(propertyName);
284     UNUSED_PARAM(exception);
285
286     return JSValueMakeNumber(ctx, 1); // distinguish base get form derived get
287 }
288
289 static bool Base_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
290 {
291     UNUSED_PARAM(object);
292     UNUSED_PARAM(propertyName);
293     UNUSED_PARAM(value);
294
295     *exception = JSValueMakeNumber(ctx, 1); // distinguish base set from derived set
296     return true;
297 }
298
299 static JSValueRef Base_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
300 {
301     UNUSED_PARAM(function);
302     UNUSED_PARAM(thisObject);
303     UNUSED_PARAM(argumentCount);
304     UNUSED_PARAM(arguments);
305     UNUSED_PARAM(exception);
306     
307     return JSValueMakeNumber(ctx, 1); // distinguish base call from derived call
308 }
309
310 static JSStaticFunction Base_staticFunctions[] = {
311     { "baseProtoDup", NULL, kJSPropertyAttributeNone },
312     { "baseProto", Base_callAsFunction, kJSPropertyAttributeNone },
313     { 0, 0, 0 }
314 };
315
316 static JSStaticValue Base_staticValues[] = {
317     { "baseDup", Base_get, Base_set, kJSPropertyAttributeNone },
318     { "baseOnly", Base_get, Base_set, kJSPropertyAttributeNone },
319     { 0, 0, 0, 0 }
320 };
321
322 static bool TestInitializeFinalize;
323 static void Base_initialize(JSContextRef context, JSObjectRef object)
324 {
325     UNUSED_PARAM(context);
326
327     if (TestInitializeFinalize) {
328         ASSERT((void*)1 == JSObjectGetPrivate(object));
329         JSObjectSetPrivate(object, (void*)2);
330     }
331 }
332
333 static unsigned Base_didFinalize;
334 static void Base_finalize(JSObjectRef object)
335 {
336     UNUSED_PARAM(object);
337     if (TestInitializeFinalize) {
338         ASSERT((void*)4 == JSObjectGetPrivate(object));
339         Base_didFinalize = true;
340     }
341 }
342
343 static JSClassRef Base_class(JSContextRef context)
344 {
345     UNUSED_PARAM(context);
346
347     static JSClassRef jsClass;
348     if (!jsClass) {
349         JSClassDefinition definition = kJSClassDefinitionEmpty;
350         definition.staticValues = Base_staticValues;
351         definition.staticFunctions = Base_staticFunctions;
352         definition.initialize = Base_initialize;
353         definition.finalize = Base_finalize;
354         jsClass = JSClassCreate(&definition);
355     }
356     return jsClass;
357 }
358
359 static JSValueRef Derived_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
360 {
361     UNUSED_PARAM(object);
362     UNUSED_PARAM(propertyName);
363     UNUSED_PARAM(exception);
364
365     return JSValueMakeNumber(ctx, 2); // distinguish base get form derived get
366 }
367
368 static bool Derived_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
369 {
370     UNUSED_PARAM(ctx);
371     UNUSED_PARAM(object);
372     UNUSED_PARAM(propertyName);
373     UNUSED_PARAM(value);
374
375     *exception = JSValueMakeNumber(ctx, 2); // distinguish base set from derived set
376     return true;
377 }
378
379 static JSValueRef Derived_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
380 {
381     UNUSED_PARAM(function);
382     UNUSED_PARAM(thisObject);
383     UNUSED_PARAM(argumentCount);
384     UNUSED_PARAM(arguments);
385     UNUSED_PARAM(exception);
386     
387     return JSValueMakeNumber(ctx, 2); // distinguish base call from derived call
388 }
389
390 static JSStaticFunction Derived_staticFunctions[] = {
391     { "protoOnly", Derived_callAsFunction, kJSPropertyAttributeNone },
392     { "protoDup", NULL, kJSPropertyAttributeNone },
393     { "baseProtoDup", Derived_callAsFunction, kJSPropertyAttributeNone },
394     { 0, 0, 0 }
395 };
396
397 static JSStaticValue Derived_staticValues[] = {
398     { "derivedOnly", Derived_get, Derived_set, kJSPropertyAttributeNone },
399     { "protoDup", Derived_get, Derived_set, kJSPropertyAttributeNone },
400     { "baseDup", Derived_get, Derived_set, kJSPropertyAttributeNone },
401     { 0, 0, 0, 0 }
402 };
403
404 static void Derived_initialize(JSContextRef context, JSObjectRef object)
405 {
406     UNUSED_PARAM(context);
407
408     if (TestInitializeFinalize) {
409         ASSERT((void*)2 == JSObjectGetPrivate(object));
410         JSObjectSetPrivate(object, (void*)3);
411     }
412 }
413
414 static void Derived_finalize(JSObjectRef object)
415 {
416     if (TestInitializeFinalize) {
417         ASSERT((void*)3 == JSObjectGetPrivate(object));
418         JSObjectSetPrivate(object, (void*)4);
419     }
420 }
421
422 static JSClassRef Derived_class(JSContextRef context)
423 {
424     static JSClassRef jsClass;
425     if (!jsClass) {
426         JSClassDefinition definition = kJSClassDefinitionEmpty;
427         definition.parentClass = Base_class(context);
428         definition.staticValues = Derived_staticValues;
429         definition.staticFunctions = Derived_staticFunctions;
430         definition.initialize = Derived_initialize;
431         definition.finalize = Derived_finalize;
432         jsClass = JSClassCreate(&definition);
433     }
434     return jsClass;
435 }
436
437 static JSValueRef print_callAsFunction(JSContextRef context, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
438 {
439     UNUSED_PARAM(functionObject);
440     UNUSED_PARAM(thisObject);
441     UNUSED_PARAM(exception);
442     
443     if (argumentCount > 0) {
444         JSStringRef string = JSValueToStringCopy(context, arguments[0], NULL);
445         size_t sizeUTF8 = JSStringGetMaximumUTF8CStringSize(string);
446         char stringUTF8[sizeUTF8];
447         JSStringGetUTF8CString(string, stringUTF8, sizeUTF8);
448         printf("%s\n", stringUTF8);
449         JSStringRelease(string);
450     }
451     
452     return JSValueMakeUndefined(context);
453 }
454
455 static JSObjectRef myConstructor_callAsConstructor(JSContextRef context, JSObjectRef constructorObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
456 {
457     UNUSED_PARAM(constructorObject);
458     UNUSED_PARAM(exception);
459     
460     JSObjectRef result = JSObjectMake(context, NULL, NULL);
461     if (argumentCount > 0) {
462         JSStringRef value = JSStringCreateWithUTF8CString("value");
463         JSObjectSetProperty(context, result, value, arguments[0], kJSPropertyAttributeNone, NULL);
464         JSStringRelease(value);
465     }
466     
467     return result;
468 }
469
470
471 static void globalObject_initialize(JSContextRef context, JSObjectRef object)
472 {
473     UNUSED_PARAM(object);
474     // Ensure that an execution context is passed in
475     ASSERT(context);
476
477     // Ensure that the global object is set to the object that we were passed
478     JSObjectRef globalObject = JSContextGetGlobalObject(context);
479     ASSERT(globalObject);
480     ASSERT(object == globalObject);
481
482     // Ensure that the standard global properties have been set on the global object
483     JSStringRef array = JSStringCreateWithUTF8CString("Array");
484     JSObjectRef arrayConstructor = JSValueToObject(context, JSObjectGetProperty(context, globalObject, array, NULL), NULL);
485     JSStringRelease(array);
486
487     UNUSED_PARAM(arrayConstructor);
488     ASSERT(arrayConstructor);
489 }
490
491 static JSValueRef globalObject_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
492 {
493     UNUSED_PARAM(object);
494     UNUSED_PARAM(propertyName);
495     UNUSED_PARAM(exception);
496
497     return JSValueMakeNumber(ctx, 3);
498 }
499
500 static bool globalObject_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
501 {
502     UNUSED_PARAM(object);
503     UNUSED_PARAM(propertyName);
504     UNUSED_PARAM(value);
505
506     *exception = JSValueMakeNumber(ctx, 3);
507     return true;
508 }
509
510 static JSValueRef globalObject_call(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
511 {
512     UNUSED_PARAM(function);
513     UNUSED_PARAM(thisObject);
514     UNUSED_PARAM(argumentCount);
515     UNUSED_PARAM(arguments);
516     UNUSED_PARAM(exception);
517
518     return JSValueMakeNumber(ctx, 3);
519 }
520
521 static JSStaticValue globalObject_staticValues[] = {
522     { "globalStaticValue", globalObject_get, globalObject_set, kJSPropertyAttributeNone },
523     { 0, 0, 0, 0 }
524 };
525
526 static JSStaticFunction globalObject_staticFunctions[] = {
527     { "globalStaticFunction", globalObject_call, kJSPropertyAttributeNone },
528     { 0, 0, 0 }
529 };
530
531 static char* createStringWithContentsOfFile(const char* fileName);
532
533 static void testInitializeFinalize()
534 {
535     JSObjectRef o = JSObjectMake(context, Derived_class(context), (void*)1);
536     UNUSED_PARAM(o);
537     ASSERT(JSObjectGetPrivate(o) == (void*)3);
538 }
539
540 int main(int argc, char* argv[])
541 {
542     const char *scriptPath = "testapi.js";
543     if (argc > 1) {
544         scriptPath = argv[1];
545     }
546     
547     // Test garbage collection with a fresh context
548     context = JSGlobalContextCreate(NULL);
549     TestInitializeFinalize = true;
550     testInitializeFinalize();
551     JSGlobalContextRelease(context);
552     JSGarbageCollect(context);
553     TestInitializeFinalize = false;
554
555     ASSERT(Base_didFinalize);
556
557     JSClassDefinition globalObjectClassDefinition = kJSClassDefinitionEmpty;
558     globalObjectClassDefinition.initialize = globalObject_initialize;
559     globalObjectClassDefinition.staticValues = globalObject_staticValues;
560     globalObjectClassDefinition.staticFunctions = globalObject_staticFunctions;
561     globalObjectClassDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
562     JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition);
563     context = JSGlobalContextCreate(globalObjectClass);
564     
565     JSObjectRef globalObject = JSContextGetGlobalObject(context);
566     ASSERT(JSValueIsObject(context, globalObject));
567     
568     JSValueRef jsUndefined = JSValueMakeUndefined(context);
569     JSValueRef jsNull = JSValueMakeNull(context);
570     JSValueRef jsTrue = JSValueMakeBoolean(context, true);
571     JSValueRef jsFalse = JSValueMakeBoolean(context, false);
572     JSValueRef jsZero = JSValueMakeNumber(context, 0);
573     JSValueRef jsOne = JSValueMakeNumber(context, 1);
574     JSValueRef jsOneThird = JSValueMakeNumber(context, 1.0 / 3.0);
575     JSObjectRef jsObjectNoProto = JSObjectMake(context, NULL, NULL);
576     JSObjectSetPrototype(context, jsObjectNoProto, JSValueMakeNull(context));
577
578     // FIXME: test funny utf8 characters
579     JSStringRef jsEmptyIString = JSStringCreateWithUTF8CString("");
580     JSValueRef jsEmptyString = JSValueMakeString(context, jsEmptyIString);
581     
582     JSStringRef jsOneIString = JSStringCreateWithUTF8CString("1");
583     JSValueRef jsOneString = JSValueMakeString(context, jsOneIString);
584
585     UniChar singleUniChar = 65; // Capital A
586     CFMutableStringRef cfString = 
587         CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
588                                                           &singleUniChar,
589                                                           1,
590                                                           1,
591                                                           kCFAllocatorNull);
592
593     JSStringRef jsCFIString = JSStringCreateWithCFString(cfString);
594     JSValueRef jsCFString = JSValueMakeString(context, jsCFIString);
595     
596     CFStringRef cfEmptyString = CFStringCreateWithCString(kCFAllocatorDefault, "", kCFStringEncodingUTF8);
597     
598     JSStringRef jsCFEmptyIString = JSStringCreateWithCFString(cfEmptyString);
599     JSValueRef jsCFEmptyString = JSValueMakeString(context, jsCFEmptyIString);
600
601     CFIndex cfStringLength = CFStringGetLength(cfString);
602     UniChar buffer[cfStringLength];
603     CFStringGetCharacters(cfString, 
604                           CFRangeMake(0, cfStringLength), 
605                           buffer);
606     JSStringRef jsCFIStringWithCharacters = JSStringCreateWithCharacters(buffer, cfStringLength);
607     JSValueRef jsCFStringWithCharacters = JSValueMakeString(context, jsCFIStringWithCharacters);
608     
609     JSStringRef jsCFEmptyIStringWithCharacters = JSStringCreateWithCharacters(buffer, CFStringGetLength(cfEmptyString));
610     JSValueRef jsCFEmptyStringWithCharacters = JSValueMakeString(context, jsCFEmptyIStringWithCharacters);
611
612     ASSERT(JSValueGetType(context, jsUndefined) == kJSTypeUndefined);
613     ASSERT(JSValueGetType(context, jsNull) == kJSTypeNull);
614     ASSERT(JSValueGetType(context, jsTrue) == kJSTypeBoolean);
615     ASSERT(JSValueGetType(context, jsFalse) == kJSTypeBoolean);
616     ASSERT(JSValueGetType(context, jsZero) == kJSTypeNumber);
617     ASSERT(JSValueGetType(context, jsOne) == kJSTypeNumber);
618     ASSERT(JSValueGetType(context, jsOneThird) == kJSTypeNumber);
619     ASSERT(JSValueGetType(context, jsEmptyString) == kJSTypeString);
620     ASSERT(JSValueGetType(context, jsOneString) == kJSTypeString);
621     ASSERT(JSValueGetType(context, jsCFString) == kJSTypeString);
622     ASSERT(JSValueGetType(context, jsCFStringWithCharacters) == kJSTypeString);
623     ASSERT(JSValueGetType(context, jsCFEmptyString) == kJSTypeString);
624     ASSERT(JSValueGetType(context, jsCFEmptyStringWithCharacters) == kJSTypeString);
625
626     JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL);
627     JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject");
628     JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone, NULL);
629     JSStringRelease(myObjectIString);
630     
631     JSValueRef exception;
632
633     // Conversions that throw exceptions
634     exception = NULL;
635     ASSERT(NULL == JSValueToObject(context, jsNull, &exception));
636     ASSERT(exception);
637     
638     exception = NULL;
639     // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function,
640     // causing a build break with -Wshorten-64-to-32 enabled.  The issue is known by the appropriate team.
641     // After that's resolved, we can remove these casts
642     ASSERT(isnan((float)JSValueToNumber(context, jsObjectNoProto, &exception)));
643     ASSERT(exception);
644
645     exception = NULL;
646     ASSERT(!JSValueToStringCopy(context, jsObjectNoProto, &exception));
647     ASSERT(exception);
648     
649     ASSERT(JSValueToBoolean(context, myObject));
650     
651     exception = NULL;
652     ASSERT(!JSValueIsEqual(context, jsObjectNoProto, JSValueMakeNumber(context, 1), &exception));
653     ASSERT(exception);
654     
655     exception = NULL;
656     JSObjectGetPropertyAtIndex(context, myObject, 0, &exception);
657     ASSERT(1 == JSValueToNumber(context, exception, NULL));
658
659     assertEqualsAsBoolean(jsUndefined, false);
660     assertEqualsAsBoolean(jsNull, false);
661     assertEqualsAsBoolean(jsTrue, true);
662     assertEqualsAsBoolean(jsFalse, false);
663     assertEqualsAsBoolean(jsZero, false);
664     assertEqualsAsBoolean(jsOne, true);
665     assertEqualsAsBoolean(jsOneThird, true);
666     assertEqualsAsBoolean(jsEmptyString, false);
667     assertEqualsAsBoolean(jsOneString, true);
668     assertEqualsAsBoolean(jsCFString, true);
669     assertEqualsAsBoolean(jsCFStringWithCharacters, true);
670     assertEqualsAsBoolean(jsCFEmptyString, false);
671     assertEqualsAsBoolean(jsCFEmptyStringWithCharacters, false);
672     
673     assertEqualsAsNumber(jsUndefined, nan(""));
674     assertEqualsAsNumber(jsNull, 0);
675     assertEqualsAsNumber(jsTrue, 1);
676     assertEqualsAsNumber(jsFalse, 0);
677     assertEqualsAsNumber(jsZero, 0);
678     assertEqualsAsNumber(jsOne, 1);
679     assertEqualsAsNumber(jsOneThird, 1.0 / 3.0);
680     assertEqualsAsNumber(jsEmptyString, 0);
681     assertEqualsAsNumber(jsOneString, 1);
682     assertEqualsAsNumber(jsCFString, nan(""));
683     assertEqualsAsNumber(jsCFStringWithCharacters, nan(""));
684     assertEqualsAsNumber(jsCFEmptyString, 0);
685     assertEqualsAsNumber(jsCFEmptyStringWithCharacters, 0);
686     ASSERT(sizeof(JSChar) == sizeof(UniChar));
687     
688     assertEqualsAsCharactersPtr(jsUndefined, "undefined");
689     assertEqualsAsCharactersPtr(jsNull, "null");
690     assertEqualsAsCharactersPtr(jsTrue, "true");
691     assertEqualsAsCharactersPtr(jsFalse, "false");
692     assertEqualsAsCharactersPtr(jsZero, "0");
693     assertEqualsAsCharactersPtr(jsOne, "1");
694     assertEqualsAsCharactersPtr(jsOneThird, "0.3333333333333333");
695     assertEqualsAsCharactersPtr(jsEmptyString, "");
696     assertEqualsAsCharactersPtr(jsOneString, "1");
697     assertEqualsAsCharactersPtr(jsCFString, "A");
698     assertEqualsAsCharactersPtr(jsCFStringWithCharacters, "A");
699     assertEqualsAsCharactersPtr(jsCFEmptyString, "");
700     assertEqualsAsCharactersPtr(jsCFEmptyStringWithCharacters, "");
701     
702     assertEqualsAsUTF8String(jsUndefined, "undefined");
703     assertEqualsAsUTF8String(jsNull, "null");
704     assertEqualsAsUTF8String(jsTrue, "true");
705     assertEqualsAsUTF8String(jsFalse, "false");
706     assertEqualsAsUTF8String(jsZero, "0");
707     assertEqualsAsUTF8String(jsOne, "1");
708     assertEqualsAsUTF8String(jsOneThird, "0.3333333333333333");
709     assertEqualsAsUTF8String(jsEmptyString, "");
710     assertEqualsAsUTF8String(jsOneString, "1");
711     assertEqualsAsUTF8String(jsCFString, "A");
712     assertEqualsAsUTF8String(jsCFStringWithCharacters, "A");
713     assertEqualsAsUTF8String(jsCFEmptyString, "");
714     assertEqualsAsUTF8String(jsCFEmptyStringWithCharacters, "");
715     
716     ASSERT(JSValueIsStrictEqual(context, jsTrue, jsTrue));
717     ASSERT(!JSValueIsStrictEqual(context, jsOne, jsOneString));
718
719     ASSERT(JSValueIsEqual(context, jsOne, jsOneString, NULL));
720     ASSERT(!JSValueIsEqual(context, jsTrue, jsFalse, NULL));
721     
722     CFStringRef cfJSString = JSStringCopyCFString(kCFAllocatorDefault, jsCFIString);
723     CFStringRef cfJSEmptyString = JSStringCopyCFString(kCFAllocatorDefault, jsCFEmptyIString);
724     ASSERT(CFEqual(cfJSString, cfString));
725     ASSERT(CFEqual(cfJSEmptyString, cfEmptyString));
726     CFRelease(cfJSString);
727     CFRelease(cfJSEmptyString);
728
729     CFRelease(cfString);
730     CFRelease(cfEmptyString);
731     
732     jsGlobalValue = JSObjectMake(context, NULL, NULL);
733     JSValueProtect(context, jsGlobalValue);
734     JSGarbageCollect(context);
735     ASSERT(JSValueIsObject(context, jsGlobalValue));
736     JSValueUnprotect(context, jsGlobalValue);
737
738     JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;");
739     JSStringRef badSyntax = JSStringCreateWithUTF8CString("x := 1;");
740     ASSERT(JSCheckScriptSyntax(context, goodSyntax, NULL, 0, NULL));
741     ASSERT(!JSCheckScriptSyntax(context, badSyntax, NULL, 0, NULL));
742
743     JSValueRef result;
744     JSValueRef v;
745     JSObjectRef o;
746     JSStringRef string;
747
748     result = JSEvaluateScript(context, goodSyntax, NULL, NULL, 1, NULL);
749     ASSERT(result);
750     ASSERT(JSValueIsEqual(context, result, jsOne, NULL));
751
752     exception = NULL;
753     result = JSEvaluateScript(context, badSyntax, NULL, NULL, 1, &exception);
754     ASSERT(!result);
755     ASSERT(JSValueIsObject(context, exception));
756     
757     JSStringRef array = JSStringCreateWithUTF8CString("Array");
758     JSObjectRef arrayConstructor = JSValueToObject(context, JSObjectGetProperty(context, globalObject, array, NULL), NULL);
759     JSStringRelease(array);
760     result = JSObjectCallAsConstructor(context, arrayConstructor, 0, NULL, NULL);
761     ASSERT(result);
762     ASSERT(JSValueIsObject(context, result));
763     ASSERT(JSValueIsInstanceOfConstructor(context, result, arrayConstructor, NULL));
764     ASSERT(!JSValueIsInstanceOfConstructor(context, JSValueMakeNull(context), arrayConstructor, NULL));
765
766     o = JSValueToObject(context, result, NULL);
767     exception = NULL;
768     ASSERT(JSValueIsUndefined(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception)));
769     ASSERT(!exception);
770     
771     JSObjectSetPropertyAtIndex(context, o, 0, JSValueMakeNumber(context, 1), &exception);
772     ASSERT(!exception);
773     
774     exception = NULL;
775     ASSERT(1 == JSValueToNumber(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception), &exception));
776     ASSERT(!exception);
777
778     JSStringRef functionBody;
779     JSObjectRef function;
780     
781     exception = NULL;
782     functionBody = JSStringCreateWithUTF8CString("rreturn Array;");
783     JSStringRef line = JSStringCreateWithUTF8CString("line");
784     ASSERT(!JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception));
785     ASSERT(JSValueIsObject(context, exception));
786     v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line, NULL);
787     assertEqualsAsNumber(v, 2); // FIXME: Lexer::setCode bumps startingLineNumber by 1 -- we need to change internal callers so that it doesn't have to (saying '0' to mean '1' in the API would be really confusing -- it's really confusing internally, in fact)
788     JSStringRelease(functionBody);
789     JSStringRelease(line);
790
791     exception = NULL;
792     functionBody = JSStringCreateWithUTF8CString("return Array;");
793     function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception);
794     JSStringRelease(functionBody);
795     ASSERT(!exception);
796     ASSERT(JSObjectIsFunction(context, function));
797     v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
798     ASSERT(v);
799     ASSERT(JSValueIsEqual(context, v, arrayConstructor, NULL));
800     
801     exception = NULL;
802     function = JSObjectMakeFunction(context, NULL, 0, NULL, jsEmptyIString, NULL, 0, &exception);
803     ASSERT(!exception);
804     v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, &exception);
805     ASSERT(v && !exception);
806     ASSERT(JSValueIsUndefined(context, v));
807     
808     exception = NULL;
809     v = NULL;
810     JSStringRef foo = JSStringCreateWithUTF8CString("foo");
811     JSStringRef argumentNames[] = { foo };
812     functionBody = JSStringCreateWithUTF8CString("return foo;");
813     function = JSObjectMakeFunction(context, foo, 1, argumentNames, functionBody, NULL, 1, &exception);
814     ASSERT(function && !exception);
815     JSValueRef arguments[] = { JSValueMakeNumber(context, 2) };
816     v = JSObjectCallAsFunction(context, function, NULL, 1, arguments, &exception);
817     JSStringRelease(foo);
818     JSStringRelease(functionBody);
819     
820     string = JSValueToStringCopy(context, function, NULL);
821     assertEqualsAsUTF8String(JSValueMakeString(context, string), "function foo(foo) \n{\n  return foo;\n}");
822     JSStringRelease(string);
823
824     JSStringRef print = JSStringCreateWithUTF8CString("print");
825     JSObjectRef printFunction = JSObjectMakeFunctionWithCallback(context, print, print_callAsFunction);
826     JSObjectSetProperty(context, globalObject, print, printFunction, kJSPropertyAttributeNone, NULL); 
827     JSStringRelease(print);
828     
829     ASSERT(!JSObjectSetPrivate(printFunction, (void*)1));
830     ASSERT(!JSObjectGetPrivate(printFunction));
831
832     JSStringRef myConstructorIString = JSStringCreateWithUTF8CString("MyConstructor");
833     JSObjectRef myConstructor = JSObjectMakeConstructor(context, NULL, myConstructor_callAsConstructor);
834     JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone, NULL);
835     JSStringRelease(myConstructorIString);
836     
837     ASSERT(!JSObjectSetPrivate(myConstructor, (void*)1));
838     ASSERT(!JSObjectGetPrivate(myConstructor));
839     
840     string = JSStringCreateWithUTF8CString("Derived");
841     JSObjectRef derivedConstructor = JSObjectMakeConstructor(context, Derived_class(context), NULL);
842     JSObjectSetProperty(context, globalObject, string, derivedConstructor, kJSPropertyAttributeNone, NULL);
843     JSStringRelease(string);
844     
845     o = JSObjectMake(context, NULL, NULL);
846     JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeNone, NULL);
847     JSObjectSetProperty(context, o, jsCFIString,  JSValueMakeNumber(context, 1), kJSPropertyAttributeDontEnum, NULL);
848     JSPropertyNameArrayRef nameArray = JSObjectCopyPropertyNames(context, o);
849     size_t expectedCount = JSPropertyNameArrayGetCount(nameArray);
850     size_t count;
851     for (count = 0; count < expectedCount; ++count)
852         JSPropertyNameArrayGetNameAtIndex(nameArray, count);
853     JSPropertyNameArrayRelease(nameArray);
854     ASSERT(count == 1); // jsCFString should not be enumerated
855
856     JSClassDefinition nullDefinition = kJSClassDefinitionEmpty;
857     nullDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
858     JSClassRef nullClass = JSClassCreate(&nullDefinition);
859     JSClassRelease(nullClass);
860     
861     nullDefinition = kJSClassDefinitionEmpty;
862     nullClass = JSClassCreate(&nullDefinition);
863     JSClassRelease(nullClass);
864
865     functionBody = JSStringCreateWithUTF8CString("return this;");
866     function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, NULL);
867     JSStringRelease(functionBody);
868     v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
869     ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
870     v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
871     ASSERT(JSValueIsEqual(context, v, o, NULL));
872     
873     char* scriptUTF8 = createStringWithContentsOfFile(scriptPath);
874     if (!scriptUTF8)
875         printf("FAIL: Test script could not be loaded.\n");
876     else {
877         JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
878         result = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
879         if (JSValueIsUndefined(context, result))
880             printf("PASS: Test script executed successfully.\n");
881         else {
882             printf("FAIL: Test script returned unexpected value:\n");
883             JSStringRef exceptionIString = JSValueToStringCopy(context, exception, NULL);
884             CFStringRef exceptionCF = JSStringCopyCFString(kCFAllocatorDefault, exceptionIString);
885             CFShow(exceptionCF);
886             CFRelease(exceptionCF);
887             JSStringRelease(exceptionIString);
888         }
889         JSStringRelease(script);
890         free(scriptUTF8);
891     }
892
893     // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
894     function = NULL;
895     v = NULL;
896     o = NULL;
897     globalObject = NULL;
898
899     JSStringRelease(jsEmptyIString);
900     JSStringRelease(jsOneIString);
901     JSStringRelease(jsCFIString);
902     JSStringRelease(jsCFEmptyIString);
903     JSStringRelease(jsCFIStringWithCharacters);
904     JSStringRelease(jsCFEmptyIStringWithCharacters);
905     JSStringRelease(goodSyntax);
906     JSStringRelease(badSyntax);
907
908     JSGlobalContextRelease(context);
909     JSGarbageCollect(context);
910     JSClassRelease(globalObjectClass);
911
912     printf("PASS: Program exited normally.\n");
913     return 0;
914 }
915
916 static char* createStringWithContentsOfFile(const char* fileName)
917 {
918     char* buffer;
919     
920     size_t buffer_size = 0;
921     size_t buffer_capacity = 1024;
922     buffer = (char*)malloc(buffer_capacity);
923     
924     FILE* f = fopen(fileName, "r");
925     if (!f) {
926         fprintf(stderr, "Could not open file: %s\n", fileName);
927         return 0;
928     }
929     
930     while (!feof(f) && !ferror(f)) {
931         buffer_size += fread(buffer + buffer_size, 1, buffer_capacity - buffer_size, f);
932         if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
933             buffer_capacity *= 2;
934             buffer = (char*)realloc(buffer, buffer_capacity);
935             ASSERT(buffer);
936         }
937         
938         ASSERT(buffer_size < buffer_capacity);
939     }
940     fclose(f);
941     buffer[buffer_size] = '\0';
942     
943     return buffer;
944 }