JavaScriptCore:
[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 <wtf/UnusedParam.h>
29
30 #if defined(__APPLE__)
31 #include <CoreFoundation/CoreFoundation.h>
32 #endif
33
34 #include <assert.h>
35 #include <math.h>
36
37 static JSGlobalContextRef context = 0;
38
39 static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
40 {
41     if (JSValueToBoolean(context, value, NULL) != expectedValue)
42         fprintf(stderr, "assertEqualsAsBoolean failed: %p, %d\n", value, expectedValue);
43 }
44
45 static void assertEqualsAsNumber(JSValueRef value, double expectedValue)
46 {
47     double number = JSValueToNumber(context, value, NULL);
48     if (number != expectedValue && !(isnan(number) && isnan(expectedValue)))
49         fprintf(stderr, "assertEqualsAsNumber failed: %p, %lf\n", value, expectedValue);
50 }
51
52 static void assertEqualsAsUTF8String(JSValueRef value, const char* expectedValue)
53 {
54     JSStringRef valueAsString = JSValueToStringCopy(context, value, NULL);
55
56     size_t jsSize = JSStringGetMaximumUTF8CStringSize(valueAsString);
57     char jsBuffer[jsSize];
58     JSStringGetUTF8CString(valueAsString, jsBuffer, jsSize);
59     
60     if (strcmp(jsBuffer, expectedValue) != 0)
61         fprintf(stderr, "assertEqualsAsUTF8String strcmp failed: %s != %s\n", jsBuffer, expectedValue);
62         
63     if (jsSize < strlen(jsBuffer) + 1)
64         fprintf(stderr, "assertEqualsAsUTF8String failed: jsSize was too small\n");
65
66     JSStringRelease(valueAsString);
67 }
68
69 #if defined(__APPLE__)
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 #endif // __APPLE__
95
96 static JSValueRef jsGlobalValue; // non-stack value for testing JSValueProtect()
97
98 /* MyObject pseudo-class */
99
100 static bool didInitialize = false;
101 static void MyObject_initialize(JSContextRef context, JSObjectRef object, JSValueRef* exception)
102 {
103     UNUSED_PARAM(context);
104     UNUSED_PARAM(object);
105     didInitialize = true;
106 }
107
108 static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
109 {
110     UNUSED_PARAM(context);
111     UNUSED_PARAM(object);
112
113     if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")
114         || JSStringIsEqualToUTF8CString(propertyName, "cantFind")
115         || JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")) {
116         return true;
117     }
118     
119     return false;
120 }
121
122 static JSValueRef MyObject_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
123 {
124     UNUSED_PARAM(context);
125     UNUSED_PARAM(object);
126     
127     if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")) {
128         return JSValueMakeNumber(1);
129     }
130     
131     if (JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")) {
132         return JSValueMakeNumber(1);
133     }
134
135     if (JSStringIsEqualToUTF8CString(propertyName, "cantFind")) {
136         return JSValueMakeUndefined();
137     }
138     
139     return NULL;
140 }
141
142 static bool MyObject_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
143 {
144     UNUSED_PARAM(context);
145     UNUSED_PARAM(object);
146     UNUSED_PARAM(value);
147
148     if (JSStringIsEqualToUTF8CString(propertyName, "cantSet"))
149         return true; // pretend we set the property in order to swallow it
150     
151     return false;
152 }
153
154 static bool MyObject_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
155 {
156     UNUSED_PARAM(context);
157     UNUSED_PARAM(object);
158     
159     if (JSStringIsEqualToUTF8CString(propertyName, "cantDelete"))
160         return true;
161     
162     return false;
163 }
164
165 static void MyObject_addPropertiesToList(JSObjectRef object, JSPropertyListRef propertyList)
166 {
167     UNUSED_PARAM(context);
168     
169     JSStringRef propertyName;
170     
171     propertyName = JSStringCreateWithUTF8CString("alwaysOne");
172     JSPropertyListAdd(propertyList, object, propertyName);
173     JSStringRelease(propertyName);
174     
175     propertyName = JSStringCreateWithUTF8CString("myPropertyName");
176     JSPropertyListAdd(propertyList, object, propertyName);
177     JSStringRelease(propertyName);
178 }
179
180 static JSValueRef MyObject_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argc, JSValueRef argv[], JSValueRef* exception)
181 {
182     UNUSED_PARAM(context);
183     UNUSED_PARAM(object);
184     UNUSED_PARAM(thisObject);
185
186     if (argc > 0 && JSValueIsStrictEqual(context, argv[0], JSValueMakeNumber(0)))
187         return JSValueMakeNumber(1);
188     
189     return JSValueMakeUndefined();
190 }
191
192 static JSObjectRef MyObject_callAsConstructor(JSContextRef context, JSObjectRef object, size_t argc, JSValueRef argv[], JSValueRef* exception)
193 {
194     UNUSED_PARAM(context);
195     UNUSED_PARAM(object);
196
197     if (argc > 0 && JSValueIsStrictEqual(context, argv[0], JSValueMakeNumber(0)))
198         return JSValueToObject(context, JSValueMakeNumber(1), NULL);
199     
200     return JSValueToObject(context, JSValueMakeNumber(0), NULL);
201 }
202
203 static bool MyObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception)
204 {
205     UNUSED_PARAM(context);
206
207     JSStringRef numberString = JSStringCreateWithUTF8CString("Number");
208     JSObjectRef numberConstructor = JSValueToObject(context, JSObjectGetProperty(context, JSContextGetGlobalObject(context), numberString), NULL);
209     JSStringRelease(numberString);
210
211     return JSValueIsInstanceOfConstructor(context, possibleValue, numberConstructor);
212 }
213
214 static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
215 {
216     UNUSED_PARAM(context);
217     UNUSED_PARAM(object);
218     
219     switch (type) {
220     case kJSTypeBoolean:
221         *exception = JSValueMakeNumber(2);
222         return NULL;
223     case kJSTypeNumber:
224         return JSValueMakeNumber(1);
225     default:
226         break;
227     }
228
229     // string conversion -- forward to default object class
230     return NULL;
231 }
232
233 static bool didFinalize = false;
234 static void MyObject_finalize(JSObjectRef object)
235 {
236     UNUSED_PARAM(context);
237     UNUSED_PARAM(object);
238     didFinalize = true;
239 }
240
241 JSObjectCallbacks MyObject_callbacks = {
242     0,
243     MyObject_initialize,
244     MyObject_finalize,
245     MyObject_hasProperty,
246     MyObject_getProperty,
247     MyObject_setProperty,
248     MyObject_deleteProperty,
249     MyObject_addPropertiesToList,
250     MyObject_callAsFunction,
251     MyObject_callAsConstructor,
252     MyObject_hasInstance,
253     MyObject_convertToType,
254 };
255
256 static JSClassRef MyObject_class(JSContextRef context)
257 {
258     static JSClassRef jsClass;
259     if (!jsClass) {
260         jsClass = JSClassCreate(NULL, NULL, &MyObject_callbacks, NULL);
261     }
262     
263     return jsClass;
264 }
265
266 static JSValueRef print_callAsFunction(JSContextRef context, JSObjectRef functionObject, JSObjectRef thisObject, size_t argc, JSValueRef argv[], JSValueRef* exception)
267 {
268     UNUSED_PARAM(functionObject);
269     UNUSED_PARAM(thisObject);
270     
271     if (argc > 0) {
272         JSStringRef string = JSValueToStringCopy(context, argv[0], NULL);
273         size_t sizeUTF8 = JSStringGetMaximumUTF8CStringSize(string);
274         char stringUTF8[sizeUTF8];
275         JSStringGetUTF8CString(string, stringUTF8, sizeUTF8);
276         printf("%s\n", stringUTF8);
277         JSStringRelease(string);
278     }
279     
280     return JSValueMakeUndefined();
281 }
282
283 static JSObjectRef myConstructor_callAsConstructor(JSContextRef context, JSObjectRef constructorObject, size_t argc, JSValueRef argv[], JSValueRef* exception)
284 {
285     UNUSED_PARAM(constructorObject);
286     
287     JSObjectRef result = JSObjectMake(context, NULL, 0);
288     if (argc > 0) {
289         JSStringRef value = JSStringCreateWithUTF8CString("value");
290         JSObjectSetProperty(context, result, value, argv[0], kJSPropertyAttributeNone);
291         JSStringRelease(value);
292     }
293     
294     return result;
295 }
296
297 static char* createStringWithContentsOfFile(const char* fileName);
298
299 int main(int argc, char* argv[])
300 {
301     UNUSED_PARAM(argc);
302     UNUSED_PARAM(argv);
303     
304     context = JSGlobalContextCreate(NULL);
305     
306     JSObjectRef globalObject = JSContextGetGlobalObject(context);
307     assert(JSValueIsObject(globalObject));
308     
309     JSValueRef jsUndefined = JSValueMakeUndefined();
310     JSValueRef jsNull = JSValueMakeNull();
311     JSValueRef jsTrue = JSValueMakeBoolean(true);
312     JSValueRef jsFalse = JSValueMakeBoolean(false);
313     JSValueRef jsZero = JSValueMakeNumber(0);
314     JSValueRef jsOne = JSValueMakeNumber(1);
315     JSValueRef jsOneThird = JSValueMakeNumber(1.0 / 3.0);
316     JSObjectRef jsObjectNoProto = JSObjectMake(context, NULL, JSValueMakeNull());
317
318     // FIXME: test funny utf8 characters
319     JSStringRef jsEmptyIString = JSStringCreateWithUTF8CString("");
320     JSValueRef jsEmptyString = JSValueMakeString(jsEmptyIString);
321     
322     JSStringRef jsOneIString = JSStringCreateWithUTF8CString("1");
323     JSValueRef jsOneString = JSValueMakeString(jsOneIString);
324
325 #if defined(__APPLE__)
326     UniChar singleUniChar = 65; // Capital A
327     CFMutableStringRef cfString = 
328         CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
329                                                           &singleUniChar,
330                                                           1,
331                                                           1,
332                                                           kCFAllocatorNull);
333
334     JSStringRef jsCFIString = JSStringCreateWithCFString(cfString);
335     JSValueRef jsCFString = JSValueMakeString(jsCFIString);
336     
337     CFStringRef cfEmptyString = CFStringCreateWithCString(kCFAllocatorDefault, "", kCFStringEncodingUTF8);
338     
339     JSStringRef jsCFEmptyIString = JSStringCreateWithCFString(cfEmptyString);
340     JSValueRef jsCFEmptyString = JSValueMakeString(jsCFEmptyIString);
341
342     CFIndex cfStringLength = CFStringGetLength(cfString);
343     UniChar buffer[cfStringLength];
344     CFStringGetCharacters(cfString, 
345                           CFRangeMake(0, cfStringLength), 
346                           buffer);
347     JSStringRef jsCFIStringWithCharacters = JSStringCreateWithCharacters(buffer, cfStringLength);
348     JSValueRef jsCFStringWithCharacters = JSValueMakeString(jsCFIStringWithCharacters);
349     
350     JSStringRef jsCFEmptyIStringWithCharacters = JSStringCreateWithCharacters(buffer, CFStringGetLength(cfEmptyString));
351     JSValueRef jsCFEmptyStringWithCharacters = JSValueMakeString(jsCFEmptyIStringWithCharacters);
352 #endif // __APPLE__
353
354     assert(JSValueGetType(jsUndefined) == kJSTypeUndefined);
355     assert(JSValueGetType(jsNull) == kJSTypeNull);
356     assert(JSValueGetType(jsTrue) == kJSTypeBoolean);
357     assert(JSValueGetType(jsFalse) == kJSTypeBoolean);
358     assert(JSValueGetType(jsZero) == kJSTypeNumber);
359     assert(JSValueGetType(jsOne) == kJSTypeNumber);
360     assert(JSValueGetType(jsOneThird) == kJSTypeNumber);
361     assert(JSValueGetType(jsEmptyString) == kJSTypeString);
362     assert(JSValueGetType(jsOneString) == kJSTypeString);
363 #if defined(__APPLE__)
364     assert(JSValueGetType(jsCFString) == kJSTypeString);
365     assert(JSValueGetType(jsCFStringWithCharacters) == kJSTypeString);
366     assert(JSValueGetType(jsCFEmptyString) == kJSTypeString);
367     assert(JSValueGetType(jsCFEmptyStringWithCharacters) == kJSTypeString);
368 #endif // __APPLE__
369
370     JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL);
371     assert(didInitialize);
372     JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject");
373     JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone);
374     JSStringRelease(myObjectIString);
375     
376     JSValueRef exception;
377
378     // Conversions that throw exceptions
379     exception = NULL;
380     assert(NULL == JSValueToObject(context, jsNull, &exception));
381     assert(exception);
382     
383     exception = NULL;
384     assert(isnan(JSValueToNumber(context, jsObjectNoProto, &exception)));
385     assert(exception);
386
387     exception = NULL;
388     assert(!JSValueToStringCopy(context, jsObjectNoProto, &exception));
389     assert(exception);
390     
391     exception = NULL;
392     assert(!JSValueToBoolean(context, myObject, &exception));
393     assert(exception);
394     
395     exception = NULL;
396     assert(!JSValueIsEqual(context, jsObjectNoProto, JSValueMakeNumber(1), &exception));
397     assert(exception);
398
399     assertEqualsAsBoolean(jsUndefined, false);
400     assertEqualsAsBoolean(jsNull, false);
401     assertEqualsAsBoolean(jsTrue, true);
402     assertEqualsAsBoolean(jsFalse, false);
403     assertEqualsAsBoolean(jsZero, false);
404     assertEqualsAsBoolean(jsOne, true);
405     assertEqualsAsBoolean(jsOneThird, true);
406     assertEqualsAsBoolean(jsEmptyString, false);
407     assertEqualsAsBoolean(jsOneString, true);
408 #if defined(__APPLE__)
409     assertEqualsAsBoolean(jsCFString, true);
410     assertEqualsAsBoolean(jsCFStringWithCharacters, true);
411     assertEqualsAsBoolean(jsCFEmptyString, false);
412     assertEqualsAsBoolean(jsCFEmptyStringWithCharacters, false);
413 #endif // __APPLE__
414     
415     assertEqualsAsNumber(jsUndefined, nan(""));
416     assertEqualsAsNumber(jsNull, 0);
417     assertEqualsAsNumber(jsTrue, 1);
418     assertEqualsAsNumber(jsFalse, 0);
419     assertEqualsAsNumber(jsZero, 0);
420     assertEqualsAsNumber(jsOne, 1);
421     assertEqualsAsNumber(jsOneThird, 1.0 / 3.0);
422     assertEqualsAsNumber(jsEmptyString, 0);
423     assertEqualsAsNumber(jsOneString, 1);
424 #if defined(__APPLE__)
425     assertEqualsAsNumber(jsCFString, nan(""));
426     assertEqualsAsNumber(jsCFStringWithCharacters, nan(""));
427     assertEqualsAsNumber(jsCFEmptyString, 0);
428     assertEqualsAsNumber(jsCFEmptyStringWithCharacters, 0);
429     assert(sizeof(JSChar) == sizeof(UniChar));
430 #endif // __APPLE__
431     
432     assertEqualsAsCharactersPtr(jsUndefined, "undefined");
433     assertEqualsAsCharactersPtr(jsNull, "null");
434     assertEqualsAsCharactersPtr(jsTrue, "true");
435     assertEqualsAsCharactersPtr(jsFalse, "false");
436     assertEqualsAsCharactersPtr(jsZero, "0");
437     assertEqualsAsCharactersPtr(jsOne, "1");
438     assertEqualsAsCharactersPtr(jsOneThird, "0.3333333333333333");
439     assertEqualsAsCharactersPtr(jsEmptyString, "");
440     assertEqualsAsCharactersPtr(jsOneString, "1");
441 #if defined(__APPLE__)
442     assertEqualsAsCharactersPtr(jsCFString, "A");
443     assertEqualsAsCharactersPtr(jsCFStringWithCharacters, "A");
444     assertEqualsAsCharactersPtr(jsCFEmptyString, "");
445     assertEqualsAsCharactersPtr(jsCFEmptyStringWithCharacters, "");
446 #endif // __APPLE__
447     
448     assertEqualsAsUTF8String(jsUndefined, "undefined");
449     assertEqualsAsUTF8String(jsNull, "null");
450     assertEqualsAsUTF8String(jsTrue, "true");
451     assertEqualsAsUTF8String(jsFalse, "false");
452     assertEqualsAsUTF8String(jsZero, "0");
453     assertEqualsAsUTF8String(jsOne, "1");
454     assertEqualsAsUTF8String(jsOneThird, "0.3333333333333333");
455     assertEqualsAsUTF8String(jsEmptyString, "");
456     assertEqualsAsUTF8String(jsOneString, "1");
457 #if defined(__APPLE__)
458     assertEqualsAsUTF8String(jsCFString, "A");
459     assertEqualsAsUTF8String(jsCFStringWithCharacters, "A");
460     assertEqualsAsUTF8String(jsCFEmptyString, "");
461     assertEqualsAsUTF8String(jsCFEmptyStringWithCharacters, "");
462 #endif // __APPLE__
463     
464     assert(JSValueIsStrictEqual(context, jsTrue, jsTrue));
465     assert(!JSValueIsStrictEqual(context, jsOne, jsOneString));
466
467     assert(JSValueIsEqual(context, jsOne, jsOneString, NULL));
468     assert(!JSValueIsEqual(context, jsTrue, jsFalse, NULL));
469     
470 #if defined(__APPLE__)
471     CFStringRef cfJSString = JSStringCopyCFString(kCFAllocatorDefault, jsCFIString);
472     CFStringRef cfJSEmptyString = JSStringCopyCFString(kCFAllocatorDefault, jsCFEmptyIString);
473     assert(CFEqual(cfJSString, cfString));
474     assert(CFEqual(cfJSEmptyString, cfEmptyString));
475     CFRelease(cfJSString);
476     CFRelease(cfJSEmptyString);
477
478     CFRelease(cfString);
479     CFRelease(cfEmptyString);
480 #endif // __APPLE__
481     
482     jsGlobalValue = JSObjectMake(context, NULL, NULL);
483     JSValueProtect(jsGlobalValue);
484     JSGarbageCollect();
485     assert(JSValueIsObject(jsGlobalValue));
486     JSValueUnprotect(jsGlobalValue);
487
488     JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;");
489     JSStringRef badSyntax = JSStringCreateWithUTF8CString("x := 1;");
490     assert(JSCheckScriptSyntax(context, goodSyntax, NULL, 0, NULL));
491     assert(!JSCheckScriptSyntax(context, badSyntax, NULL, 0, NULL));
492
493     JSValueRef result;
494     JSValueRef v;
495     JSObjectRef o;
496
497     result = JSEvaluateScript(context, goodSyntax, NULL, NULL, 1, NULL);
498     assert(result);
499     assert(JSValueIsEqual(context, result, jsOne, NULL));
500
501     exception = NULL;
502     result = JSEvaluateScript(context, badSyntax, NULL, NULL, 1, &exception);
503     assert(!result);
504     assert(JSValueIsObject(exception));
505     
506     JSStringRef array = JSStringCreateWithUTF8CString("Array");
507     v = JSObjectGetProperty(context, globalObject, array);
508     assert(v);
509     JSObjectRef arrayConstructor = JSValueToObject(context, v, NULL);
510     JSStringRelease(array);
511     result = JSObjectCallAsConstructor(context, arrayConstructor, 0, NULL, NULL);
512     assert(result);
513     assert(JSValueIsInstanceOfConstructor(context, result, arrayConstructor));
514     assert(!JSValueIsInstanceOfConstructor(context, JSValueMakeNull(), arrayConstructor));
515     
516     JSStringRef functionBody;
517     
518     exception = NULL;
519     functionBody = JSStringCreateWithUTF8CString("rreturn Array;");
520     JSStringRef line = JSStringCreateWithUTF8CString("line");
521     assert(!JSObjectMakeFunctionWithBody(context, functionBody, NULL, 1, &exception));
522     assert(JSValueIsObject(exception));
523     v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line);
524     assert(v);
525     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)
526     JSStringRelease(functionBody);
527     JSStringRelease(line);
528
529     functionBody = JSStringCreateWithUTF8CString("return Array;");
530     JSObjectRef function = JSObjectMakeFunctionWithBody(context, functionBody, NULL, 1, NULL);
531     JSStringRelease(functionBody);
532
533     assert(JSObjectIsFunction(function));
534     v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
535     assert(JSValueIsEqual(context, v, arrayConstructor, NULL));
536                                                   
537     JSStringRef print = JSStringCreateWithUTF8CString("print");
538     JSObjectRef printFunction = JSObjectMakeFunction(context, print_callAsFunction);
539     JSObjectSetProperty(context, globalObject, print, printFunction, kJSPropertyAttributeNone); 
540     JSStringRelease(print);
541     
542     assert(JSObjectSetPrivate(printFunction, (void*)1));
543     assert(JSObjectGetPrivate(printFunction) == (void*)1);
544
545     JSStringRef myConstructorIString = JSStringCreateWithUTF8CString("MyConstructor");
546     JSObjectRef myConstructor = JSObjectMakeConstructor(context, myConstructor_callAsConstructor);
547     JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone);
548     JSStringRelease(myConstructorIString);
549     
550     assert(JSObjectSetPrivate(myConstructor, (void*)1));
551     assert(JSObjectGetPrivate(myConstructor) == (void*)1);
552     
553     o = JSObjectMake(context, NULL, NULL);
554     JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(1), kJSPropertyAttributeNone);
555     JSObjectSetProperty(context, o, jsCFIString,  JSValueMakeNumber(1), kJSPropertyAttributeDontEnum);
556     JSPropertyEnumeratorRef enumerator = JSObjectCreatePropertyEnumerator(o);
557     int count = 0;
558     while (JSPropertyEnumeratorGetNextName(enumerator))
559         ++count;
560     JSPropertyEnumeratorRelease(enumerator);
561     assert(count == 1); // jsCFString should not be enumerated
562
563     JSClassRef nullCallbacksClass = JSClassCreate(NULL, NULL, NULL, NULL);
564     JSClassRelease(nullCallbacksClass);
565     
566     functionBody = JSStringCreateWithUTF8CString("return this;");
567     function = JSObjectMakeFunctionWithBody(context, functionBody, NULL, 1, NULL);
568     JSStringRelease(functionBody);
569     v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
570     assert(JSValueIsEqual(context, v, globalObject, NULL));
571     v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
572     assert(JSValueIsEqual(context, v, o, NULL));
573     
574     char* scriptUTF8 = createStringWithContentsOfFile("testapi.js");
575     JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
576     result = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
577     if (JSValueIsUndefined(result))
578         printf("PASS: Test script executed successfully.\n");
579     else {
580         printf("FAIL: Test script returned unexcpected value:\n");
581         JSStringRef exceptionIString = JSValueToStringCopy(context, exception, NULL);
582         CFStringRef exceptionCF = JSStringCopyCFString(kCFAllocatorDefault, exceptionIString);
583         CFShow(exceptionCF);
584         CFRelease(exceptionCF);
585         JSStringRelease(exceptionIString);
586     }
587     JSStringRelease(script);
588     free(scriptUTF8);
589
590     // Allocate a few dummies so that at least one will be collected
591     JSObjectMake(context, MyObject_class(context), 0);
592     JSObjectMake(context, MyObject_class(context), 0);
593     JSGarbageCollect();
594     assert(didFinalize);
595
596     JSStringRelease(jsEmptyIString);
597     JSStringRelease(jsOneIString);
598 #if defined(__APPLE__)
599     JSStringRelease(jsCFIString);
600     JSStringRelease(jsCFEmptyIString);
601     JSStringRelease(jsCFIStringWithCharacters);
602     JSStringRelease(jsCFEmptyIStringWithCharacters);
603 #endif // __APPLE__
604     JSStringRelease(goodSyntax);
605     JSStringRelease(badSyntax);
606     
607     JSGlobalContextRelease(context);
608     printf("PASS: Program exited normally.\n");
609     return 0;
610 }
611
612 static char* createStringWithContentsOfFile(const char* fileName)
613 {
614     char* buffer;
615     
616     int buffer_size = 0;
617     int buffer_capacity = 1024;
618     buffer = (char*)malloc(buffer_capacity);
619     
620     FILE* f = fopen(fileName, "r");
621     if (!f) {
622         fprintf(stderr, "Could not open file: %s\n", fileName);
623         return 0;
624     }
625     
626     while (!feof(f) && !ferror(f)) {
627         buffer_size += fread(buffer + buffer_size, 1, buffer_capacity - buffer_size, f);
628         if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
629             buffer_capacity *= 2;
630             buffer = (char*)realloc(buffer, buffer_capacity);
631             assert(buffer);
632         }
633         
634         assert(buffer_size < buffer_capacity);
635     }
636     fclose(f);
637     buffer[buffer_size] = '\0';
638     
639     return buffer;
640 }