source: webkit/trunk/JavaScriptCore/API/testapi.c@ 26625

Last change on this file since 26625 was 25257, checked in by bdash, 18 years ago

2007-08-26 Mark Rowe <[email protected]>

Reviewed by Darin Adler.

<rdar://problem/4949002> JSGlobalContextCreate can cause crashes because it passes a NULL JSContextRef to the globalObjectClass's initialize callback

JSCallbackObject now tracks whether it was constructed with a null ExecState. This will happen when the object is being used as the global object,
as the Interpreter needs to be created after the global object. In this situation the initialization is deferred until after the Interpreter's
ExecState is available to be passed down to the initialize callbacks.

  • API/JSCallbackObject.cpp: (KJS::JSCallbackObject::init): Track whether we successfully initialized. (KJS::JSCallbackObject::initializeIfNeeded): Attempt to initialize with the new ExecState.
  • API/JSCallbackObject.h:
  • API/JSContextRef.cpp: (JSGlobalContextCreate): Initialize the JSCallbackObject with the Interpreter's ExecState.
  • API/testapi.c: (testInitializeOfGlobalObjectClassHasNonNullContext): (main): Verify that the context passed to the initialize callback is non-null.
File size: 33.1 KB
Line 
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 <assert.h>
29#include <math.h>
30#include <setjmp.h>
31#include <wtf/UnusedParam.h>
32
33static JSGlobalContextRef context = 0;
34
35static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
36{
37 if (JSValueToBoolean(context, value) != expectedValue)
38 fprintf(stderr, "assertEqualsAsBoolean failed: %p, %d\n", value, expectedValue);
39}
40
41static void assertEqualsAsNumber(JSValueRef value, double expectedValue)
42{
43 double number = JSValueToNumber(context, value, NULL);
44
45 // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function,
46 // causing a build break with -Wshorten-64-to-32 enabled. The issue is known by the appropriate team.
47 // After that's resolved, we can remove these casts
48 if (number != expectedValue && !(isnan((float)number) && isnan((float)expectedValue)))
49 fprintf(stderr, "assertEqualsAsNumber failed: %p, %lf\n", value, expectedValue);
50}
51
52static 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 unsigned i;
61 for (i = 0; jsBuffer[i]; i++)
62 if (jsBuffer[i] != expectedValue[i])
63 fprintf(stderr, "assertEqualsAsUTF8String failed at character %d: %c(%d) != %c(%d)\n", i, jsBuffer[i], jsBuffer[i], expectedValue[i], expectedValue[i]);
64
65 if (jsSize < strlen(jsBuffer) + 1)
66 fprintf(stderr, "assertEqualsAsUTF8String failed: jsSize was too small\n");
67
68 JSStringRelease(valueAsString);
69}
70
71static void assertEqualsAsCharactersPtr(JSValueRef value, const char* expectedValue)
72{
73 JSStringRef valueAsString = JSValueToStringCopy(context, value, NULL);
74
75 size_t jsLength = JSStringGetLength(valueAsString);
76 const JSChar* jsBuffer = JSStringGetCharactersPtr(valueAsString);
77
78 CFStringRef expectedValueAsCFString = CFStringCreateWithCString(kCFAllocatorDefault,
79 expectedValue,
80 kCFStringEncodingUTF8);
81 CFIndex cfLength = CFStringGetLength(expectedValueAsCFString);
82 UniChar cfBuffer[cfLength];
83 CFStringGetCharacters(expectedValueAsCFString, CFRangeMake(0, cfLength), cfBuffer);
84 CFRelease(expectedValueAsCFString);
85
86 if (memcmp(jsBuffer, cfBuffer, cfLength * sizeof(UniChar)) != 0)
87 fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsBuffer != cfBuffer\n");
88
89 if (jsLength != (size_t)cfLength)
90 fprintf(stderr, "assertEqualsAsCharactersPtr failed: jsLength(%ld) != cfLength(%ld)\n", jsLength, cfLength);
91
92 JSStringRelease(valueAsString);
93}
94
95static JSValueRef jsGlobalValue; // non-stack value for testing JSValueProtect()
96
97/* MyObject pseudo-class */
98
99static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName)
100{
101 UNUSED_PARAM(context);
102 UNUSED_PARAM(object);
103
104 if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")
105 || JSStringIsEqualToUTF8CString(propertyName, "cantFind")
106 || JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")
107 || JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie")
108 || JSStringIsEqualToUTF8CString(propertyName, "0")) {
109 return true;
110 }
111
112 return false;
113}
114
115static JSValueRef MyObject_getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
116{
117 UNUSED_PARAM(context);
118 UNUSED_PARAM(object);
119
120 if (JSStringIsEqualToUTF8CString(propertyName, "alwaysOne")) {
121 return JSValueMakeNumber(context, 1);
122 }
123
124 if (JSStringIsEqualToUTF8CString(propertyName, "myPropertyName")) {
125 return JSValueMakeNumber(context, 1);
126 }
127
128 if (JSStringIsEqualToUTF8CString(propertyName, "cantFind")) {
129 return JSValueMakeUndefined(context);
130 }
131
132 if (JSStringIsEqualToUTF8CString(propertyName, "0")) {
133 *exception = JSValueMakeNumber(context, 1);
134 return JSValueMakeNumber(context, 1);
135 }
136
137 return NULL;
138}
139
140static bool MyObject_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
141{
142 UNUSED_PARAM(context);
143 UNUSED_PARAM(object);
144 UNUSED_PARAM(value);
145
146 if (JSStringIsEqualToUTF8CString(propertyName, "cantSet"))
147 return true; // pretend we set the property in order to swallow it
148
149 return false;
150}
151
152static bool MyObject_deleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
153{
154 UNUSED_PARAM(context);
155 UNUSED_PARAM(object);
156
157 if (JSStringIsEqualToUTF8CString(propertyName, "cantDelete"))
158 return true;
159
160 if (JSStringIsEqualToUTF8CString(propertyName, "throwOnDelete")) {
161 *exception = JSValueMakeNumber(context, 2);
162 return false;
163 }
164
165 return false;
166}
167
168static void MyObject_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames)
169{
170 UNUSED_PARAM(context);
171
172 JSStringRef propertyName;
173
174 propertyName = JSStringCreateWithUTF8CString("alwaysOne");
175 JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
176 JSStringRelease(propertyName);
177
178 propertyName = JSStringCreateWithUTF8CString("myPropertyName");
179 JSPropertyNameAccumulatorAddName(propertyNames, propertyName);
180 JSStringRelease(propertyName);
181}
182
183static JSValueRef MyObject_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
184{
185 UNUSED_PARAM(context);
186 UNUSED_PARAM(object);
187 UNUSED_PARAM(thisObject);
188
189 if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0)))
190 return JSValueMakeNumber(context, 1);
191
192 return JSValueMakeUndefined(context);
193}
194
195static JSObjectRef MyObject_callAsConstructor(JSContextRef context, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
196{
197 UNUSED_PARAM(context);
198 UNUSED_PARAM(object);
199
200 if (argumentCount > 0 && JSValueIsStrictEqual(context, arguments[0], JSValueMakeNumber(context, 0)))
201 return JSValueToObject(context, JSValueMakeNumber(context, 1), NULL);
202
203 return JSValueToObject(context, JSValueMakeNumber(context, 0), NULL);
204}
205
206static bool MyObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception)
207{
208 UNUSED_PARAM(context);
209
210 JSStringRef numberString = JSStringCreateWithUTF8CString("Number");
211 JSObjectRef numberConstructor = JSValueToObject(context, JSObjectGetProperty(context, JSContextGetGlobalObject(context), numberString, NULL), NULL);
212 JSStringRelease(numberString);
213
214 return JSValueIsInstanceOfConstructor(context, possibleValue, numberConstructor, NULL);
215}
216
217static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
218{
219 UNUSED_PARAM(context);
220 UNUSED_PARAM(object);
221
222 switch (type) {
223 case kJSTypeNumber:
224 return JSValueMakeNumber(context, 1);
225 default:
226 break;
227 }
228
229 // string conversion -- forward to default object class
230 return NULL;
231}
232
233static JSStaticValue evilStaticValues[] = {
234 { "nullGetSet", 0, 0, kJSPropertyAttributeNone },
235 { 0, 0, 0, 0 }
236};
237
238static JSStaticFunction evilStaticFunctions[] = {
239 { "nullCall", 0, kJSPropertyAttributeNone },
240 { 0, 0, 0 }
241};
242
243JSClassDefinition MyObject_definition = {
244 0,
245 kJSClassAttributeNone,
246
247 "MyObject",
248 NULL,
249
250 evilStaticValues,
251 evilStaticFunctions,
252
253 NULL,
254 NULL,
255 MyObject_hasProperty,
256 MyObject_getProperty,
257 MyObject_setProperty,
258 MyObject_deleteProperty,
259 MyObject_getPropertyNames,
260 MyObject_callAsFunction,
261 MyObject_callAsConstructor,
262 MyObject_hasInstance,
263 MyObject_convertToType,
264};
265
266static JSClassRef MyObject_class(JSContextRef context)
267{
268 static JSClassRef jsClass;
269 if (!jsClass)
270 jsClass = JSClassCreate(&MyObject_definition);
271
272 return jsClass;
273}
274
275static JSValueRef Base_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
276{
277 UNUSED_PARAM(ctx);
278 UNUSED_PARAM(object);
279 UNUSED_PARAM(propertyName);
280
281 return JSValueMakeNumber(ctx, 1); // distinguish base get form derived get
282}
283
284static bool Base_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
285{
286 UNUSED_PARAM(ctx);
287 UNUSED_PARAM(object);
288 UNUSED_PARAM(propertyName);
289 UNUSED_PARAM(value);
290
291 *exception = JSValueMakeNumber(ctx, 1); // distinguish base set from derived set
292 return true;
293}
294
295static JSValueRef Base_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
296{
297 UNUSED_PARAM(ctx);
298 UNUSED_PARAM(function);
299 UNUSED_PARAM(thisObject);
300 UNUSED_PARAM(argumentCount);
301 UNUSED_PARAM(arguments);
302
303 return JSValueMakeNumber(ctx, 1); // distinguish base call from derived call
304}
305
306static JSStaticFunction Base_staticFunctions[] = {
307 { "baseProtoDup", NULL, kJSPropertyAttributeNone },
308 { "baseProto", Base_callAsFunction, kJSPropertyAttributeNone },
309 { 0, 0, 0 }
310};
311
312static JSStaticValue Base_staticValues[] = {
313 { "baseDup", Base_get, Base_set, kJSPropertyAttributeNone },
314 { "baseOnly", Base_get, Base_set, kJSPropertyAttributeNone },
315 { 0, 0, 0, 0 }
316};
317
318static bool TestInitializeFinalize;
319static void Base_initialize(JSContextRef context, JSObjectRef object)
320{
321 if (TestInitializeFinalize) {
322 assert((void*)1 == JSObjectGetPrivate(object));
323 JSObjectSetPrivate(object, (void*)2);
324 }
325}
326
327static unsigned Base_didFinalize;
328static void Base_finalize(JSObjectRef object)
329{
330 if (TestInitializeFinalize) {
331 assert((void*)4 == JSObjectGetPrivate(object));
332 Base_didFinalize = true;
333 }
334}
335
336static JSClassRef Base_class(JSContextRef context)
337{
338 static JSClassRef jsClass;
339 if (!jsClass) {
340 JSClassDefinition definition = kJSClassDefinitionEmpty;
341 definition.staticValues = Base_staticValues;
342 definition.staticFunctions = Base_staticFunctions;
343 definition.initialize = Base_initialize;
344 definition.finalize = Base_finalize;
345 jsClass = JSClassCreate(&definition);
346 }
347 return jsClass;
348}
349
350static JSValueRef Derived_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
351{
352 UNUSED_PARAM(ctx);
353 UNUSED_PARAM(object);
354 UNUSED_PARAM(propertyName);
355
356 return JSValueMakeNumber(ctx, 2); // distinguish base get form derived get
357}
358
359static bool Derived_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
360{
361 UNUSED_PARAM(ctx);
362 UNUSED_PARAM(object);
363 UNUSED_PARAM(propertyName);
364 UNUSED_PARAM(value);
365
366 *exception = JSValueMakeNumber(ctx, 2); // distinguish base set from derived set
367 return true;
368}
369
370static JSValueRef Derived_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
371{
372 UNUSED_PARAM(ctx);
373 UNUSED_PARAM(function);
374 UNUSED_PARAM(thisObject);
375 UNUSED_PARAM(argumentCount);
376 UNUSED_PARAM(arguments);
377
378 return JSValueMakeNumber(ctx, 2); // distinguish base call from derived call
379}
380
381static JSStaticFunction Derived_staticFunctions[] = {
382 { "protoOnly", Derived_callAsFunction, kJSPropertyAttributeNone },
383 { "protoDup", NULL, kJSPropertyAttributeNone },
384 { "baseProtoDup", Derived_callAsFunction, kJSPropertyAttributeNone },
385 { 0, 0, 0 }
386};
387
388static JSStaticValue Derived_staticValues[] = {
389 { "derivedOnly", Derived_get, Derived_set, kJSPropertyAttributeNone },
390 { "protoDup", Derived_get, Derived_set, kJSPropertyAttributeNone },
391 { "baseDup", Derived_get, Derived_set, kJSPropertyAttributeNone },
392 { 0, 0, 0, 0 }
393};
394
395static void Derived_initialize(JSContextRef context, JSObjectRef object)
396{
397 if (TestInitializeFinalize) {
398 assert((void*)2 == JSObjectGetPrivate(object));
399 JSObjectSetPrivate(object, (void*)3);
400 }
401}
402
403static void Derived_finalize(JSObjectRef object)
404{
405 if (TestInitializeFinalize) {
406 assert((void*)3 == JSObjectGetPrivate(object));
407 JSObjectSetPrivate(object, (void*)4);
408 }
409}
410
411static JSClassRef Derived_class(JSContextRef context)
412{
413 static JSClassRef jsClass;
414 if (!jsClass) {
415 JSClassDefinition definition = kJSClassDefinitionEmpty;
416 definition.parentClass = Base_class(context);
417 definition.staticValues = Derived_staticValues;
418 definition.staticFunctions = Derived_staticFunctions;
419 definition.initialize = Derived_initialize;
420 definition.finalize = Derived_finalize;
421 jsClass = JSClassCreate(&definition);
422 }
423 return jsClass;
424}
425
426static JSValueRef print_callAsFunction(JSContextRef context, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
427{
428 UNUSED_PARAM(functionObject);
429 UNUSED_PARAM(thisObject);
430
431 if (argumentCount > 0) {
432 JSStringRef string = JSValueToStringCopy(context, arguments[0], NULL);
433 size_t sizeUTF8 = JSStringGetMaximumUTF8CStringSize(string);
434 char stringUTF8[sizeUTF8];
435 JSStringGetUTF8CString(string, stringUTF8, sizeUTF8);
436 printf("%s\n", stringUTF8);
437 JSStringRelease(string);
438 }
439
440 return JSValueMakeUndefined(context);
441}
442
443static JSObjectRef myConstructor_callAsConstructor(JSContextRef context, JSObjectRef constructorObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
444{
445 UNUSED_PARAM(constructorObject);
446
447 JSObjectRef result = JSObjectMake(context, NULL, NULL);
448 if (argumentCount > 0) {
449 JSStringRef value = JSStringCreateWithUTF8CString("value");
450 JSObjectSetProperty(context, result, value, arguments[0], kJSPropertyAttributeNone, NULL);
451 JSStringRelease(value);
452 }
453
454 return result;
455}
456
457static void testInitializeOfGlobalObjectClassHasNonNullContext(JSContextRef context, JSObjectRef object)
458{
459 UNUSED_PARAM(object);
460 assert(context);
461}
462
463static char* createStringWithContentsOfFile(const char* fileName);
464
465static void testInitializeFinalize()
466{
467 JSObjectRef o = JSObjectMake(context, Derived_class(context), (void*)1);
468 assert(JSObjectGetPrivate(o) == (void*)3);
469}
470
471int main(int argc, char* argv[])
472{
473 UNUSED_PARAM(argc);
474 UNUSED_PARAM(argv);
475
476 // Test garbage collection with a fresh context
477 context = JSGlobalContextCreate(NULL);
478 TestInitializeFinalize = true;
479 testInitializeFinalize();
480 JSGlobalContextRelease(context);
481 JSGarbageCollect(context);
482 TestInitializeFinalize = false;
483
484 assert(Base_didFinalize);
485
486 JSClassDefinition globalObjectClassDefinition = kJSClassDefinitionEmpty;
487 globalObjectClassDefinition.initialize = testInitializeOfGlobalObjectClassHasNonNullContext;
488 JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition);
489 context = JSGlobalContextCreate(globalObjectClass);
490
491 JSObjectRef globalObject = JSContextGetGlobalObject(context);
492 assert(JSValueIsObject(context, globalObject));
493
494 JSValueRef jsUndefined = JSValueMakeUndefined(context);
495 JSValueRef jsNull = JSValueMakeNull(context);
496 JSValueRef jsTrue = JSValueMakeBoolean(context, true);
497 JSValueRef jsFalse = JSValueMakeBoolean(context, false);
498 JSValueRef jsZero = JSValueMakeNumber(context, 0);
499 JSValueRef jsOne = JSValueMakeNumber(context, 1);
500 JSValueRef jsOneThird = JSValueMakeNumber(context, 1.0 / 3.0);
501 JSObjectRef jsObjectNoProto = JSObjectMake(context, NULL, NULL);
502 JSObjectSetPrototype(context, jsObjectNoProto, JSValueMakeNull(context));
503
504 // FIXME: test funny utf8 characters
505 JSStringRef jsEmptyIString = JSStringCreateWithUTF8CString("");
506 JSValueRef jsEmptyString = JSValueMakeString(context, jsEmptyIString);
507
508 JSStringRef jsOneIString = JSStringCreateWithUTF8CString("1");
509 JSValueRef jsOneString = JSValueMakeString(context, jsOneIString);
510
511 UniChar singleUniChar = 65; // Capital A
512 CFMutableStringRef cfString =
513 CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
514 &singleUniChar,
515 1,
516 1,
517 kCFAllocatorNull);
518
519 JSStringRef jsCFIString = JSStringCreateWithCFString(cfString);
520 JSValueRef jsCFString = JSValueMakeString(context, jsCFIString);
521
522 CFStringRef cfEmptyString = CFStringCreateWithCString(kCFAllocatorDefault, "", kCFStringEncodingUTF8);
523
524 JSStringRef jsCFEmptyIString = JSStringCreateWithCFString(cfEmptyString);
525 JSValueRef jsCFEmptyString = JSValueMakeString(context, jsCFEmptyIString);
526
527 CFIndex cfStringLength = CFStringGetLength(cfString);
528 UniChar buffer[cfStringLength];
529 CFStringGetCharacters(cfString,
530 CFRangeMake(0, cfStringLength),
531 buffer);
532 JSStringRef jsCFIStringWithCharacters = JSStringCreateWithCharacters(buffer, cfStringLength);
533 JSValueRef jsCFStringWithCharacters = JSValueMakeString(context, jsCFIStringWithCharacters);
534
535 JSStringRef jsCFEmptyIStringWithCharacters = JSStringCreateWithCharacters(buffer, CFStringGetLength(cfEmptyString));
536 JSValueRef jsCFEmptyStringWithCharacters = JSValueMakeString(context, jsCFEmptyIStringWithCharacters);
537
538 assert(JSValueGetType(context, jsUndefined) == kJSTypeUndefined);
539 assert(JSValueGetType(context, jsNull) == kJSTypeNull);
540 assert(JSValueGetType(context, jsTrue) == kJSTypeBoolean);
541 assert(JSValueGetType(context, jsFalse) == kJSTypeBoolean);
542 assert(JSValueGetType(context, jsZero) == kJSTypeNumber);
543 assert(JSValueGetType(context, jsOne) == kJSTypeNumber);
544 assert(JSValueGetType(context, jsOneThird) == kJSTypeNumber);
545 assert(JSValueGetType(context, jsEmptyString) == kJSTypeString);
546 assert(JSValueGetType(context, jsOneString) == kJSTypeString);
547 assert(JSValueGetType(context, jsCFString) == kJSTypeString);
548 assert(JSValueGetType(context, jsCFStringWithCharacters) == kJSTypeString);
549 assert(JSValueGetType(context, jsCFEmptyString) == kJSTypeString);
550 assert(JSValueGetType(context, jsCFEmptyStringWithCharacters) == kJSTypeString);
551
552 JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL);
553 JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject");
554 JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone, NULL);
555 JSStringRelease(myObjectIString);
556
557 JSValueRef exception;
558
559 // Conversions that throw exceptions
560 exception = NULL;
561 assert(NULL == JSValueToObject(context, jsNull, &exception));
562 assert(exception);
563
564 exception = NULL;
565 // FIXME <rdar://4668451> - On i386 the isnan(double) macro tries to map to the isnan(float) function,
566 // causing a build break with -Wshorten-64-to-32 enabled. The issue is known by the appropriate team.
567 // After that's resolved, we can remove these casts
568 assert(isnan((float)JSValueToNumber(context, jsObjectNoProto, &exception)));
569 assert(exception);
570
571 exception = NULL;
572 assert(!JSValueToStringCopy(context, jsObjectNoProto, &exception));
573 assert(exception);
574
575 assert(JSValueToBoolean(context, myObject));
576
577 exception = NULL;
578 assert(!JSValueIsEqual(context, jsObjectNoProto, JSValueMakeNumber(context, 1), &exception));
579 assert(exception);
580
581 exception = NULL;
582 JSObjectGetPropertyAtIndex(context, myObject, 0, &exception);
583 assert(1 == JSValueToNumber(context, exception, NULL));
584
585 assertEqualsAsBoolean(jsUndefined, false);
586 assertEqualsAsBoolean(jsNull, false);
587 assertEqualsAsBoolean(jsTrue, true);
588 assertEqualsAsBoolean(jsFalse, false);
589 assertEqualsAsBoolean(jsZero, false);
590 assertEqualsAsBoolean(jsOne, true);
591 assertEqualsAsBoolean(jsOneThird, true);
592 assertEqualsAsBoolean(jsEmptyString, false);
593 assertEqualsAsBoolean(jsOneString, true);
594 assertEqualsAsBoolean(jsCFString, true);
595 assertEqualsAsBoolean(jsCFStringWithCharacters, true);
596 assertEqualsAsBoolean(jsCFEmptyString, false);
597 assertEqualsAsBoolean(jsCFEmptyStringWithCharacters, false);
598
599 assertEqualsAsNumber(jsUndefined, nan(""));
600 assertEqualsAsNumber(jsNull, 0);
601 assertEqualsAsNumber(jsTrue, 1);
602 assertEqualsAsNumber(jsFalse, 0);
603 assertEqualsAsNumber(jsZero, 0);
604 assertEqualsAsNumber(jsOne, 1);
605 assertEqualsAsNumber(jsOneThird, 1.0 / 3.0);
606 assertEqualsAsNumber(jsEmptyString, 0);
607 assertEqualsAsNumber(jsOneString, 1);
608 assertEqualsAsNumber(jsCFString, nan(""));
609 assertEqualsAsNumber(jsCFStringWithCharacters, nan(""));
610 assertEqualsAsNumber(jsCFEmptyString, 0);
611 assertEqualsAsNumber(jsCFEmptyStringWithCharacters, 0);
612 assert(sizeof(JSChar) == sizeof(UniChar));
613
614 assertEqualsAsCharactersPtr(jsUndefined, "undefined");
615 assertEqualsAsCharactersPtr(jsNull, "null");
616 assertEqualsAsCharactersPtr(jsTrue, "true");
617 assertEqualsAsCharactersPtr(jsFalse, "false");
618 assertEqualsAsCharactersPtr(jsZero, "0");
619 assertEqualsAsCharactersPtr(jsOne, "1");
620 assertEqualsAsCharactersPtr(jsOneThird, "0.3333333333333333");
621 assertEqualsAsCharactersPtr(jsEmptyString, "");
622 assertEqualsAsCharactersPtr(jsOneString, "1");
623 assertEqualsAsCharactersPtr(jsCFString, "A");
624 assertEqualsAsCharactersPtr(jsCFStringWithCharacters, "A");
625 assertEqualsAsCharactersPtr(jsCFEmptyString, "");
626 assertEqualsAsCharactersPtr(jsCFEmptyStringWithCharacters, "");
627
628 assertEqualsAsUTF8String(jsUndefined, "undefined");
629 assertEqualsAsUTF8String(jsNull, "null");
630 assertEqualsAsUTF8String(jsTrue, "true");
631 assertEqualsAsUTF8String(jsFalse, "false");
632 assertEqualsAsUTF8String(jsZero, "0");
633 assertEqualsAsUTF8String(jsOne, "1");
634 assertEqualsAsUTF8String(jsOneThird, "0.3333333333333333");
635 assertEqualsAsUTF8String(jsEmptyString, "");
636 assertEqualsAsUTF8String(jsOneString, "1");
637 assertEqualsAsUTF8String(jsCFString, "A");
638 assertEqualsAsUTF8String(jsCFStringWithCharacters, "A");
639 assertEqualsAsUTF8String(jsCFEmptyString, "");
640 assertEqualsAsUTF8String(jsCFEmptyStringWithCharacters, "");
641
642 assert(JSValueIsStrictEqual(context, jsTrue, jsTrue));
643 assert(!JSValueIsStrictEqual(context, jsOne, jsOneString));
644
645 assert(JSValueIsEqual(context, jsOne, jsOneString, NULL));
646 assert(!JSValueIsEqual(context, jsTrue, jsFalse, NULL));
647
648 CFStringRef cfJSString = JSStringCopyCFString(kCFAllocatorDefault, jsCFIString);
649 CFStringRef cfJSEmptyString = JSStringCopyCFString(kCFAllocatorDefault, jsCFEmptyIString);
650 assert(CFEqual(cfJSString, cfString));
651 assert(CFEqual(cfJSEmptyString, cfEmptyString));
652 CFRelease(cfJSString);
653 CFRelease(cfJSEmptyString);
654
655 CFRelease(cfString);
656 CFRelease(cfEmptyString);
657
658 jsGlobalValue = JSObjectMake(context, NULL, NULL);
659 JSValueProtect(context, jsGlobalValue);
660 JSGarbageCollect(context);
661 assert(JSValueIsObject(context, jsGlobalValue));
662 JSValueUnprotect(context, jsGlobalValue);
663
664 JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;");
665 JSStringRef badSyntax = JSStringCreateWithUTF8CString("x := 1;");
666 assert(JSCheckScriptSyntax(context, goodSyntax, NULL, 0, NULL));
667 assert(!JSCheckScriptSyntax(context, badSyntax, NULL, 0, NULL));
668
669 JSValueRef result;
670 JSValueRef v;
671 JSObjectRef o;
672 JSStringRef string;
673
674 result = JSEvaluateScript(context, goodSyntax, NULL, NULL, 1, NULL);
675 assert(result);
676 assert(JSValueIsEqual(context, result, jsOne, NULL));
677
678 exception = NULL;
679 result = JSEvaluateScript(context, badSyntax, NULL, NULL, 1, &exception);
680 assert(!result);
681 assert(JSValueIsObject(context, exception));
682
683 JSStringRef array = JSStringCreateWithUTF8CString("Array");
684 JSObjectRef arrayConstructor = JSValueToObject(context, JSObjectGetProperty(context, globalObject, array, NULL), NULL);
685 JSStringRelease(array);
686 result = JSObjectCallAsConstructor(context, arrayConstructor, 0, NULL, NULL);
687 assert(result);
688 assert(JSValueIsObject(context, result));
689 assert(JSValueIsInstanceOfConstructor(context, result, arrayConstructor, NULL));
690 assert(!JSValueIsInstanceOfConstructor(context, JSValueMakeNull(context), arrayConstructor, NULL));
691
692 o = JSValueToObject(context, result, NULL);
693 exception = NULL;
694 assert(JSValueIsUndefined(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception)));
695 assert(!exception);
696
697 JSObjectSetPropertyAtIndex(context, o, 0, JSValueMakeNumber(context, 1), &exception);
698 assert(!exception);
699
700 exception = NULL;
701 assert(1 == JSValueToNumber(context, JSObjectGetPropertyAtIndex(context, o, 0, &exception), &exception));
702 assert(!exception);
703
704 JSStringRef functionBody;
705 JSObjectRef function;
706
707 exception = NULL;
708 functionBody = JSStringCreateWithUTF8CString("rreturn Array;");
709 JSStringRef line = JSStringCreateWithUTF8CString("line");
710 assert(!JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception));
711 assert(JSValueIsObject(context, exception));
712 v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line, NULL);
713 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)
714 JSStringRelease(functionBody);
715 JSStringRelease(line);
716
717 exception = NULL;
718 functionBody = JSStringCreateWithUTF8CString("return Array;");
719 function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, &exception);
720 JSStringRelease(functionBody);
721 assert(!exception);
722 assert(JSObjectIsFunction(context, function));
723 v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
724 assert(v);
725 assert(JSValueIsEqual(context, v, arrayConstructor, NULL));
726
727 exception = NULL;
728 function = JSObjectMakeFunction(context, NULL, 0, NULL, jsEmptyIString, NULL, 0, &exception);
729 assert(!exception);
730 v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, &exception);
731 assert(v && !exception);
732 assert(JSValueIsUndefined(context, v));
733
734 exception = NULL;
735 v = NULL;
736 JSStringRef foo = JSStringCreateWithUTF8CString("foo");
737 JSStringRef argumentNames[] = { foo };
738 functionBody = JSStringCreateWithUTF8CString("return foo;");
739 function = JSObjectMakeFunction(context, foo, 1, argumentNames, functionBody, NULL, 1, &exception);
740 assert(function && !exception);
741 JSValueRef arguments[] = { JSValueMakeNumber(context, 2) };
742 v = JSObjectCallAsFunction(context, function, NULL, 1, arguments, &exception);
743 JSStringRelease(foo);
744 JSStringRelease(functionBody);
745
746 string = JSValueToStringCopy(context, function, NULL);
747 assertEqualsAsUTF8String(JSValueMakeString(context, string), "function foo(foo) \n{\n return foo;\n}");
748 JSStringRelease(string);
749
750 JSStringRef print = JSStringCreateWithUTF8CString("print");
751 JSObjectRef printFunction = JSObjectMakeFunctionWithCallback(context, print, print_callAsFunction);
752 JSObjectSetProperty(context, globalObject, print, printFunction, kJSPropertyAttributeNone, NULL);
753 JSStringRelease(print);
754
755 assert(!JSObjectSetPrivate(printFunction, (void*)1));
756 assert(!JSObjectGetPrivate(printFunction));
757
758 JSStringRef myConstructorIString = JSStringCreateWithUTF8CString("MyConstructor");
759 JSObjectRef myConstructor = JSObjectMakeConstructor(context, NULL, myConstructor_callAsConstructor);
760 JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone, NULL);
761 JSStringRelease(myConstructorIString);
762
763 assert(!JSObjectSetPrivate(myConstructor, (void*)1));
764 assert(!JSObjectGetPrivate(myConstructor));
765
766 string = JSStringCreateWithUTF8CString("Derived");
767 JSObjectRef derivedConstructor = JSObjectMakeConstructor(context, Derived_class(context), NULL);
768 JSObjectSetProperty(context, globalObject, string, derivedConstructor, kJSPropertyAttributeNone, NULL);
769 JSStringRelease(string);
770
771 o = JSObjectMake(context, NULL, NULL);
772 JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeNone, NULL);
773 JSObjectSetProperty(context, o, jsCFIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeDontEnum, NULL);
774 JSPropertyNameArrayRef nameArray = JSObjectCopyPropertyNames(context, o);
775 size_t expectedCount = JSPropertyNameArrayGetCount(nameArray);
776 size_t count;
777 for (count = 0; count < expectedCount; ++count)
778 JSPropertyNameArrayGetNameAtIndex(nameArray, count);
779 JSPropertyNameArrayRelease(nameArray);
780 assert(count == 1); // jsCFString should not be enumerated
781
782 JSClassDefinition nullDefinition = kJSClassDefinitionEmpty;
783 nullDefinition.attributes = kJSClassAttributeNoAutomaticPrototype;
784 JSClassRef nullClass = JSClassCreate(&nullDefinition);
785 JSClassRelease(nullClass);
786
787 nullDefinition = kJSClassDefinitionEmpty;
788 nullClass = JSClassCreate(&nullDefinition);
789 JSClassRelease(nullClass);
790
791 functionBody = JSStringCreateWithUTF8CString("return this;");
792 function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, NULL);
793 JSStringRelease(functionBody);
794 v = JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
795 assert(JSValueIsEqual(context, v, globalObject, NULL));
796 v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
797 assert(JSValueIsEqual(context, v, o, NULL));
798
799 char* scriptUTF8 = createStringWithContentsOfFile("testapi.js");
800 JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
801 result = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
802 if (JSValueIsUndefined(context, result))
803 printf("PASS: Test script executed successfully.\n");
804 else {
805 printf("FAIL: Test script returned unexcpected value:\n");
806 JSStringRef exceptionIString = JSValueToStringCopy(context, exception, NULL);
807 CFStringRef exceptionCF = JSStringCopyCFString(kCFAllocatorDefault, exceptionIString);
808 CFShow(exceptionCF);
809 CFRelease(exceptionCF);
810 JSStringRelease(exceptionIString);
811 }
812 JSStringRelease(script);
813 free(scriptUTF8);
814
815 JSStringRelease(jsEmptyIString);
816 JSStringRelease(jsOneIString);
817 JSStringRelease(jsCFIString);
818 JSStringRelease(jsCFEmptyIString);
819 JSStringRelease(jsCFIStringWithCharacters);
820 JSStringRelease(jsCFEmptyIStringWithCharacters);
821 JSStringRelease(goodSyntax);
822 JSStringRelease(badSyntax);
823
824 JSGlobalContextRelease(context);
825 JSGarbageCollect(context);
826 JSClassRelease(globalObjectClass);
827
828 printf("PASS: Program exited normally.\n");
829 return 0;
830}
831
832static char* createStringWithContentsOfFile(const char* fileName)
833{
834 char* buffer;
835
836 size_t buffer_size = 0;
837 size_t buffer_capacity = 1024;
838 buffer = (char*)malloc(buffer_capacity);
839
840 FILE* f = fopen(fileName, "r");
841 if (!f) {
842 fprintf(stderr, "Could not open file: %s\n", fileName);
843 return 0;
844 }
845
846 while (!feof(f) && !ferror(f)) {
847 buffer_size += fread(buffer + buffer_size, 1, buffer_capacity - buffer_size, f);
848 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
849 buffer_capacity *= 2;
850 buffer = (char*)realloc(buffer, buffer_capacity);
851 assert(buffer);
852 }
853
854 assert(buffer_size < buffer_capacity);
855 }
856 fclose(f);
857 buffer[buffer_size] = '\0';
858
859 return buffer;
860}
Note: See TracBrowser for help on using the repository browser.