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(>m, 0, sizeof(gtm));
132 strftime(timeZoneName, sizeof(timeZoneName), "%Z", >m);
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