850aed857177aa79f116af89b6c819903f097fae
[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     UNUSED_PARAM(argc);
543     UNUSED_PARAM(argv);
544     
545     // Test garbage collection with a fresh context
546     context = JSGlobalContextCreate(NULL);
547     TestInitializeFinalize = true;
548     testInitializeFinalize();
549     JSGlobalContextRelease(context);
550     JSGarbageCollect(context);
551     TestInitializeFinalize = false;
552
553     ASSERT(Base_didFinalize);
554
555     JSClassDefinition globalObjectClassDefinition = kJSClassDefinitionEmpty;
556     globalObjectClassDefinition.initialize = globalObject_initialize;
557     globalObjectClassDefinition.staticValues = globalObject_staticValues;
558     globalObjectClassDefinition.staticFunctions = globalObject_staticFunctions;
559     globalObjectClassDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
560     JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition);
561     context = JSGlobalContextCreate(globalObjectClass);
562     
563     JSObjectRef globalObject = JSContextGetGlobalObject(context);
564     ASSERT(JSValueIsObject(context, globalObject));
565     
566     JSValueRef jsUndefined = JSValueMakeUndefined(context);
567     JSValueRef jsNull = JSValueMakeNull(context);
568     JSValueRef jsTrue = JSValueMakeBoolean(context, true);
569     JSValueRef jsFalse = JSValueMakeBoolean(context, false);
570     JSValueRef jsZero = JSValueMakeNumber(context, 0);
571     JSValueRef jsOne = JSValueMakeNumber(context, 1);
572     JSValueRef jsOneThird = JSValueMakeNumber(context, 1.0 / 3.0);
573     JSObjectRef jsObjectNoProto = JSObjectMake(context, NULL, NULL);
574     JSObjectSetPrototype(context, jsObjectNoProto, JSValueMakeNull(context));
575
576     // FIXME: test funny utf8 characters
577     JSStringRef jsEmptyIString = JSStringCreateWithUTF8CString("");
578     JSValueRef jsEmptyString = JSValueMakeString(context, jsEmptyIString);
579     
580     JSStringRef jsOneIString = JSStringCreateWithUTF8CString("1");
581     JSValueRef jsOneString = JSValueMakeString(context, jsOneIString);
582
583     UniChar singleUniChar = 65; // Capital A
584     CFMutableStringRef cfString = 
585         CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
586                                                           &singleUniChar,
587                                                           1,
588                                                           1,
589                                                           kCFAllocatorNull);
590
591     JSStringRef jsCFIString = JSStringCreateWithCFString(cfString);
592     JSValueRef jsCFString = JSValueMakeString(context, jsCFIString);
593     
594     CFStringRef cfEmptyString = CFStringCreateWithCString(kCFAllocatorDefault, "", kCFStringEncodingUTF8);
595     
596     JSStringRef jsCFEmptyIString = JSStringCreateWithCFString(cfEmptyString);
597     JSValueRef jsCFEmptyString = JSValueMakeString(context, jsCFEmptyIString);
598
599     CFIndex cfStringLength = CFStringGetLength(cfString);
600     UniChar buffer[cfStringLength];
601     CFStringGetCharacters(cfString, 
602                           CFRangeMake(0, cfStringLength), 
603                           buffer);
604     JSStringRef jsCFIStringWithCharacters = JSStringCreateWithCharacters(buffer, cfStringLength);
605     JSValueRef jsCFStringWithCharacters = JSValueMakeString(context, jsCFIStringWithCharacters);
606     
607     JSStringRef jsCFEmptyIStringWithCharacters = JSStringCreateWithCharacters(buffer, CFStringGetLength(cfEmptyString));
608     JSValueRef jsCFEmptyStringWithCharacters = JSValueMakeString(context, jsCFEmptyIStringWithCharacters);
609
610     ASSERT(JSValueGetType(context, jsUndefined) == kJSTypeUndefined);
611     ASSERT(JSValueGetType(context, jsNull) == kJSTypeNull);
612     ASSERT(JSValueGetType(context, jsTrue) == kJSTypeBoolean);
613     ASSERT(JSValueGetType(context, jsFalse) == kJSTypeBoolean);
614     ASSERT(JSValueGetType(context, jsZero) == kJSTypeNumber);
615     ASSERT(JSValueGetType(context, jsOne) == kJSTypeNumber);
616     ASSERT(JSValueGetType(context, jsOneThird) == kJSTypeNumber);
617     ASSERT(JSValueGetType(context, jsEmptyString) == kJSTypeString);
618     ASSERT(JSValueGetType(context, jsOneString) == kJSTypeString);
619     ASSERT(JSValueGetType(context, jsCFString) == kJSTypeString);
620     ASSERT(JSValueGetType(context, jsCFStringWithCharacters) == kJSTypeString);
621     ASSERT(JSValueGetType(context, jsCFEmptyString) == kJSTypeString);
622     ASSERT(JSValueGetType(context, jsCFEmptyStringWithCharacters) == kJSTypeString);
623
624     JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL);
625     JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject");
626     JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone, NULL);
627     JSStringRelease(myObjectIString);
628     
629     JSValueRef exception;
630
631     // Conversions that throw exceptions
632     exception = NULL;
633     ASSERT(NULL == JSValueToObject(context, jsNull, &exception));
634     ASSERT(exception);
635     
636     exception = NULL;
637     // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function,
638     // causing a build break with -Wshorten-64-to-32 enabled.  The issue is known by the appropriate team.
639     // After that's resolved, we can remove these casts
640     ASSERT(isnan((float)JSValueToNumber(context, jsObjectNoProto, &exception)));
641     ASSERT(exception);
642
643     exception = NULL;
644     ASSERT(!JSValueToStringCopy(context, jsObjectNoProto, &exception));
645     ASSERT(exception);
646     
647     ASSERT(JSValueToBoolean(context, myObject));
648     
649     exception = NULL;
650     ASSERT(!JSValueIsEqual(context, jsObjectNoProto, JSValueMakeNumber(context, 1), &exception));
651     ASSERT(exception);
652     
653     exception = NULL;
654     JSObjectGetPropertyAtIndex(context, myObject, 0, &exception);
655     ASSERT(1 == JSValueToNumber(context, exception, NULL));
656
657     assertEqualsAsBoolean(jsUndefined, false);
658     assertEqualsAsBoolean(jsNull, false);
659     assertEqualsAsBoolean(jsTrue, true);
660     assertEqualsAsBoolean(jsFalse, false);
661     assertEqualsAsBoolean(jsZero, false);
662     assertEqualsAsBoolean(jsOne, true);
663     assertEqualsAsBoolean(jsOneThird, true);
664     assertEqualsAsBoolean(jsEmptyString, false);
665     assertEqualsAsBoolean(jsOneString, true);
666     assertEqualsAsBoolean(jsCFString, true);
667     assertEqualsAsBoolean(jsCFStringWithCharacters, true);
668     assertEqualsAsBoolean(jsCFEmptyString, false);
669     assertEqualsAsBoolean(jsCFEmptyStringWithCharacters, false);
670     
671     assertEqualsAsNumber(jsUndefined, nan(""));
672     assertEqualsAsNumber(jsNull, 0);
673     assertEqualsAsNumber(jsTrue, 1);
674     assertEqualsAsNumber(jsFalse, 0);
675     assertEqualsAsNumber(jsZero, 0);
676     assertEqualsAsNumber(jsOne, 1);
677     assertEqualsAsNumber(jsOneThird, 1.0 / 3.0);
678     assertEqualsAsNumber(jsEmptyString, 0);
679     assertEqualsAsNumber(jsOneString, 1);
680     assertEqualsAsNumber(jsCFString, nan(""));
681     assertEqualsAsNumber(jsCFStringWithCharacters, nan(""));
682     assertEqualsAsNumber(jsCFEmptyString, 0);
683     assertEqualsAsNumber(jsCFEmptyStringWithCharacters, 0);
684     ASSERT(sizeof(JSChar) == sizeof(UniChar));
685     
686     assertEqualsAsCharactersPtr(jsUndefined, "undefined");
687     assertEqualsAsCharactersPtr(jsNull, "null");
688     assertEqualsAsCharactersPtr(jsTrue, "true");
689     assertEqualsAsCharactersPtr(jsFalse, "false");
690     assertEqualsAsCharactersPtr(jsZero, "0");
691     assertEqualsAsCharactersPtr(jsOne, "1");
692     assertEqualsAsCharactersPtr(jsOneThird, "0.3333333333333333");
693     assertEqualsAsCharactersPtr(jsEmptyString, "");
694     assertEqualsAsCharactersPtr(jsOneString, "1");
695     assertEqualsAsCharactersPtr(jsCFString, "A");
696     assertEqualsAsCharactersPtr(jsCFStringWithCharacters, "A");
697     assertEqualsAsCharactersPtr(jsCFEmptyString, "");
698     assertEqualsAsCharactersPtr(jsCFEmptyStringWithCharacters, "");
699     
700     assertEqualsAsUTF8String(jsUndefined, "undefined");
701     assertEqualsAsUTF8String(jsNull, "null");
702     assertEqualsAsUTF8String(jsTrue, "true");
703     assertEqualsAsUTF8String(jsFalse, "false");
704     assertEqualsAsUTF8String(jsZero, "0");
705     assertEqualsAsUTF8String(jsOne, "1");
706     assertEqualsAsUTF8String(jsOneThird, "0.3333333333333333");
707     assertEqualsAsUTF8String(jsEmptyString, "");
708     assertEqualsAsUTF8String(jsOneString, "1");
709     assertEqualsAsUTF8String(jsCFString, "A");
710     assertEqualsAsUTF8String(jsCFStringWithCharacters, "A");
711     assertEqualsAsUTF8String(jsCFEmptyString, "");
712     assertEqualsAsUTF8String(jsCFEmptyStringWithCharacters, "");
713     
714     ASSERT(JSValueIsStrictEqual(context, jsTrue, jsTrue));
715     ASSERT(!JSValueIsStrictEqual(context, jsOne, jsOneString));
716
717     ASSERT(JSValueIsEqual(context, jsOne, jsOneString, NULL));
718     ASSERT(!JSValueIsEqual(context, jsTrue, jsFalse, NULL));
719     
720     CFStringRef cfJSString = JSStringCopyCFString(kCFAllocatorDefault, jsCFIString);
721     CFStringRef cfJSEmptyString = JSStringCopyCFString(kCFAllocatorDefault, jsCFEmptyIString);
722     ASSERT(CFEqual(cfJSString, cfString));
723     ASSERT(CFEqual(cfJSEmptyString, cfEmptyString));
724     CFRelease(cfJSString);
725     CFRelease(cfJSEmptyString);
726
727     CFRelease(cfString);
728     CFRelease(cfEmptyString);
729     
730     jsGlobalValue = JSObjectMake(context, NULL, NULL);
731     JSValueProtect(context, jsGlobalValue);
732     JSGarbageCollect(context);
733     ASSERT(JSValueIsObject(context, jsGlobalValue));
734     JSValueUnprotect(context, jsGlobalValue);
735
736     JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;");
737     JSStringRef badSyntax = JSStringCreateWithUTF8CString("x := 1;");
738     ASSERT(JSCheckScriptSyntax(context, goodSyntax, NULL, 0, NULL));
739     ASSERT(!JSCheckScriptSyntax(context, badSyntax, NULL, 0, NULL));
740
741     JSValueRef result;
742     JSValueRef v;
743     JSObjectRef o;
744     JSStringRef string;
745
746     result = JSEvaluateScript(context, goodSyntax, NULL, NULL, 1, NULL);
747     ASSERT(result);
748     ASSERT(JSValueIsEqual(context, result, jsOne, NULL));
749
750     exception = NULL;
751     result = JSEvaluateScript(context, badSyntax, NULL, NULL, 1, &exception);
752     ASSERT(!result);
753     ASSERT(JSValueIsObject(context, exception));
754     
755     JSStringRef array = JSStringCreateWithUTF8CString("Array");
756     JSObjectRef arrayConstructor = JSValueToObject(context, JSObjectGetProperty(context, globalObject, array, NULL), NULL);
757     JSStringRelease(array);
758     result = JSObjectCallAsConstructor(context, arrayConstructor, 0, NULL, NULL);
759     ASSERT(result);
760     ASSERT(JSValueIsObject(context, result));
761     ASSERT(JSValueIsInstanceOfConstructor(context, result, arrayConstructor, NULL));
762     ASSERT(!JSValueIsInstanceOfConstructor(context, JSValueMakeNull(context), arrayConstructor, NULL));
763
764     o = JSValueToObject(context, result, NULL);
765     exception = NULL;
766     ASSERT(JSValueIsUndefined(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception)));
767     ASSERT(!exception);
768     
769     JSObjectSetPropertyAtIndex(context, o, 0, JSValueMakeNumber(context, 1), &exception);
770     ASSERT(!exception);
771     
772     exception = NULL;
773     ASSERT(1 == JSValueToNumber(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception), &exception));
774     ASSERT(!exception);
775
776     JSStringRef functionBody;
777     JSObjectRef function;
778     
779     exception = NULL;
780     functionBody = JSStringCreateWithUTF8CString("rreturn Array;");
781     JSStringRef line = JSStringCreateWithUTF8CString("line");
782     ASSERT(!JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception));
783     ASSERT(JSValueIsObject(context, exception));
784     v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line, NULL);
785     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)
786     JSStringRelease(functionBody);
787     JSStringRelease(line);
788
789     exception = NULL;
790     functionBody = JSStringCreateWithUTF8CString("return Array;");
791     function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception);
792     JSStringRelease(functionBody);
793     ASSERT(!exception);
794     ASSERT(JSObjectIsFunction(context, function));
795     v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
796     ASSERT(v);
797     ASSERT(JSValueIsEqual(context, v, arrayConstructor, NULL));
798     
799     exception = NULL;
800     function = JSObjectMakeFunction(context, NULL, 0, NULL, jsEmptyIString, NULL, 0, &exception);
801     ASSERT(!exception);
802     v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, &exception);
803     ASSERT(v && !exception);
804     ASSERT(JSValueIsUndefined(context, v));
805     
806     exception = NULL;
807     v = NULL;
808     JSStringRef foo = JSStringCreateWithUTF8CString("foo");
809     JSStringRef argumentNames[] = { foo };
810     functionBody = JSStringCreateWithUTF8CString("return foo;");
811     function = JSObjectMakeFunction(context, foo, 1, argumentNames, functionBody, NULL, 1, &exception);
812     ASSERT(function && !exception);
813     JSValueRef arguments[] = { JSValueMakeNumber(context, 2) };
814     v = JSObjectCallAsFunction(context, function, NULL, 1, arguments, &exception);
815     JSStringRelease(foo);
816     JSStringRelease(functionBody);
817     
818     string = JSValueToStringCopy(context, function, NULL);
819     assertEqualsAsUTF8String(JSValueMakeString(context, string), "function foo(foo) \n{\n  return foo;\n}");
820     JSStringRelease(string);
821
822     JSStringRef print = JSStringCreateWithUTF8CString("print");
823     JSObjectRef printFunction = JSObjectMakeFunctionWithCallback(context, print, print_callAsFunction);
824     JSObjectSetProperty(context, globalObject, print, printFunction, kJSPropertyAttributeNone, NULL); 
825     JSStringRelease(print);
826     
827     ASSERT(!JSObjectSetPrivate(printFunction, (void*)1));
828     ASSERT(!JSObjectGetPrivate(printFunction));
829
830     JSStringRef myConstructorIString = JSStringCreateWithUTF8CString("MyConstructor");
831     JSObjectRef myConstructor = JSObjectMakeConstructor(context, NULL, myConstructor_callAsConstructor);
832     JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone, NULL);
833     JSStringRelease(myConstructorIString);
834     
835     ASSERT(!JSObjectSetPrivate(myConstructor, (void*)1));
836     ASSERT(!JSObjectGetPrivate(myConstructor));
837     
838     string = JSStringCreateWithUTF8CString("Derived");
839     JSObjectRef derivedConstructor = JSObjectMakeConstructor(context, Derived_class(context), NULL);
840     JSObjectSetProperty(context, globalObject, string, derivedConstructor, kJSPropertyAttributeNone, NULL);
841     JSStringRelease(string);
842     
843     o = JSObjectMake(context, NULL, NULL);
844     JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeNone, NULL);
845     JSObjectSetProperty(context, o, jsCFIString,  JSValueMakeNumber(context, 1), kJSPropertyAttributeDontEnum, NULL);
846     JSPropertyNameArrayRef nameArray = JSObjectCopyPropertyNames(context, o);
847     size_t expectedCount = JSPropertyNameArrayGetCount(nameArray);
848     size_t count;
849     for (count = 0; count < expectedCount; ++count)
850         JSPropertyNameArrayGetNameAtIndex(nameArray, count);
851     JSPropertyNameArrayRelease(nameArray);
852     ASSERT(count == 1); // jsCFString should not be enumerated
853
854     JSClassDefinition nullDefinition = kJSClassDefinitionEmpty;
855     nullDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
856     JSClassRef nullClass = JSClassCreate(&nullDefinition);
857     JSClassRelease(nullClass);
858     
859     nullDefinition = kJSClassDefinitionEmpty;
860     nullClass = JSClassCreate(&nullDefinition);
861     JSClassRelease(nullClass);
862
863     functionBody = JSStringCreateWithUTF8CString("return this;");
864     function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, NULL);
865     JSStringRelease(functionBody);
866     v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
867     ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
868     v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
869     ASSERT(JSValueIsEqual(context, v, o, NULL));
870     
871     char* scriptUTF8 = createStringWithContentsOfFile("testapi.js");
872     if (!scriptUTF8)
873         printf("FAIL: Test script could not be loaded.\n");
874     else {
875         JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
876         result = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
877         if (JSValueIsUndefined(context, result))
878             printf("PASS: Test script executed successfully.\n");
879         else {
880             printf("FAIL: Test script returned unexpected value:\n");
881             JSStringRef exceptionIString = JSValueToStringCopy(context, exception, NULL);
882             CFStringRef exceptionCF = JSStringCopyCFString(kCFAllocatorDefault, exceptionIString);
883             CFShow(exceptionCF);
884             CFRelease(exceptionCF);
885             JSStringRelease(exceptionIString);
886         }
887         JSStringRelease(script);
888         free(scriptUTF8);
889     }
890
891     // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
892     function = NULL;
893     v = NULL;
894     o = NULL;
895     globalObject = NULL;
896
897     JSStringRelease(jsEmptyIString);
898     JSStringRelease(jsOneIString);
899     JSStringRelease(jsCFIString);
900     JSStringRelease(jsCFEmptyIString);
901     JSStringRelease(jsCFIStringWithCharacters);
902     JSStringRelease(jsCFEmptyIStringWithCharacters);
903     JSStringRelease(goodSyntax);
904     JSStringRelease(badSyntax);
905
906     JSGlobalContextRelease(context);
907     JSGarbageCollect(context);
908     JSClassRelease(globalObjectClass);
909
910     printf("PASS: Program exited normally.\n");
911     return 0;
912 }
913
914 static char* createStringWithContentsOfFile(const char* fileName)
915 {
916     char* buffer;
917     
918     size_t buffer_size = 0;
919     size_t buffer_capacity = 1024;
920     buffer = (char*)malloc(buffer_capacity);
921     
922     FILE* f = fopen(fileName, "r");
923     if (!f) {
924         fprintf(stderr, "Could not open file: %s\n", fileName);
925         return 0;
926     }
927     
928     while (!feof(f) && !ferror(f)) {
929         buffer_size += fread(buffer + buffer_size, 1, buffer_capacity - buffer_size, f);
930         if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
931             buffer_capacity *= 2;
932             buffer = (char*)realloc(buffer, buffer_capacity);
933             ASSERT(buffer);
934         }
935         
936         ASSERT(buffer_size < buffer_capacity);
937     }
938     fclose(f);
939     buffer[buffer_size] = '\0';
940     
941     return buffer;
942 }