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