]>
Commit | Line | Data |
---|---|---|
93a37866 | 1 | /* |
40a37d08 | 2 | * Copyright (C) 2013-2015 Apple Inc. All rights reserved. |
93a37866 A |
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 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 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 | |
81345200 | 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
93a37866 A |
24 | */ |
25 | ||
26 | #import <JavaScriptCore/JavaScriptCore.h> | |
27 | ||
81345200 A |
28 | #import "CurrentThisInsideBlockGetterTest.h" |
29 | #import "DateTests.h" | |
30 | #import "JSExportTests.h" | |
ed1e77d3 | 31 | #import "Regress141275.h" |
40a37d08 A |
32 | #import "Regress141809.h" |
33 | ||
34 | #import <pthread.h> | |
81345200 | 35 | |
93a37866 | 36 | extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef); |
81345200 | 37 | extern "C" void JSSynchronousEdenCollectForDebugging(JSContextRef); |
93a37866 A |
38 | |
39 | extern "C" bool _Block_has_signature(id); | |
40 | extern "C" const char * _Block_signature(id); | |
41 | ||
42 | extern int failed; | |
43 | extern "C" void testObjectiveCAPI(void); | |
81345200 | 44 | extern "C" void checkResult(NSString *, bool); |
93a37866 A |
45 | |
46 | #if JSC_OBJC_API_ENABLED | |
47 | ||
12899fa2 A |
48 | @interface UnexportedObject : NSObject |
49 | @end | |
50 | ||
51 | @implementation UnexportedObject | |
52 | @end | |
53 | ||
93a37866 A |
54 | @protocol ParentObject <JSExport> |
55 | @end | |
56 | ||
57 | @interface ParentObject : NSObject<ParentObject> | |
58 | + (NSString *)parentTest; | |
59 | @end | |
60 | ||
61 | @implementation ParentObject | |
62 | + (NSString *)parentTest | |
63 | { | |
64 | return [self description]; | |
65 | } | |
66 | @end | |
67 | ||
68 | @protocol TestObject <JSExport> | |
12899fa2 | 69 | - (id)init; |
93a37866 A |
70 | @property int variable; |
71 | @property (readonly) int six; | |
72 | @property CGPoint point; | |
73 | + (NSString *)classTest; | |
74 | + (NSString *)parentTest; | |
75 | - (NSString *)getString; | |
76 | JSExportAs(testArgumentTypes, | |
77 | - (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o | |
78 | ); | |
79 | - (void)callback:(JSValue *)function; | |
80 | - (void)bogusCallback:(void(^)(int))function; | |
81 | @end | |
82 | ||
83 | @interface TestObject : ParentObject <TestObject> | |
84 | @property int six; | |
85 | + (id)testObject; | |
86 | @end | |
87 | ||
88 | @implementation TestObject | |
89 | @synthesize variable; | |
90 | @synthesize six; | |
91 | @synthesize point; | |
92 | + (id)testObject | |
93 | { | |
94 | return [[TestObject alloc] init]; | |
95 | } | |
96 | + (NSString *)classTest | |
97 | { | |
98 | return @"classTest - okay"; | |
99 | } | |
100 | - (NSString *)getString | |
101 | { | |
102 | return @"42"; | |
103 | } | |
104 | - (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o | |
105 | { | |
106 | return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]]; | |
107 | } | |
108 | - (void)callback:(JSValue *)function | |
109 | { | |
110 | [function callWithArguments:[NSArray arrayWithObject:[NSNumber numberWithInt:42]]]; | |
111 | } | |
112 | - (void)bogusCallback:(void(^)(int))function | |
113 | { | |
114 | function(42); | |
115 | } | |
116 | @end | |
117 | ||
118 | bool testXYZTested = false; | |
119 | ||
120 | @protocol TextXYZ <JSExport> | |
12899fa2 | 121 | - (id)initWithString:(NSString*)string; |
93a37866 A |
122 | @property int x; |
123 | @property (readonly) int y; | |
124 | @property (assign) JSValue *onclick; | |
125 | @property (assign) JSValue *weakOnclick; | |
126 | - (void)test:(NSString *)message; | |
127 | @end | |
128 | ||
129 | @interface TextXYZ : NSObject <TextXYZ> | |
130 | @property int x; | |
131 | @property int y; | |
132 | @property int z; | |
133 | - (void)click; | |
134 | @end | |
135 | ||
136 | @implementation TextXYZ { | |
137 | JSManagedValue *m_weakOnclickHandler; | |
138 | JSManagedValue *m_onclickHandler; | |
139 | } | |
140 | @synthesize x; | |
141 | @synthesize y; | |
142 | @synthesize z; | |
12899fa2 A |
143 | - (id)initWithString:(NSString*)string |
144 | { | |
145 | self = [super init]; | |
146 | if (!self) | |
147 | return nil; | |
148 | ||
149 | NSLog(@"%@", string); | |
150 | ||
151 | return self; | |
152 | } | |
93a37866 A |
153 | - (void)test:(NSString *)message |
154 | { | |
155 | testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5; | |
156 | } | |
157 | - (void)setWeakOnclick:(JSValue *)value | |
158 | { | |
159 | m_weakOnclickHandler = [JSManagedValue managedValueWithValue:value]; | |
160 | } | |
161 | ||
162 | - (void)setOnclick:(JSValue *)value | |
163 | { | |
164 | m_onclickHandler = [JSManagedValue managedValueWithValue:value]; | |
165 | [value.context.virtualMachine addManagedReference:m_onclickHandler withOwner:self]; | |
166 | } | |
167 | - (JSValue *)weakOnclick | |
168 | { | |
169 | return [m_weakOnclickHandler value]; | |
170 | } | |
171 | - (JSValue *)onclick | |
172 | { | |
173 | return [m_onclickHandler value]; | |
174 | } | |
175 | - (void)click | |
176 | { | |
177 | if (!m_onclickHandler) | |
178 | return; | |
179 | ||
180 | JSValue *function = [m_onclickHandler value]; | |
181 | [function callWithArguments:[NSArray array]]; | |
182 | } | |
93a37866 A |
183 | @end |
184 | ||
185 | @class TinyDOMNode; | |
186 | ||
187 | @protocol TinyDOMNode <JSExport> | |
188 | - (void)appendChild:(TinyDOMNode *)child; | |
189 | - (NSUInteger)numberOfChildren; | |
190 | - (TinyDOMNode *)childAtIndex:(NSUInteger)index; | |
191 | - (void)removeChildAtIndex:(NSUInteger)index; | |
192 | @end | |
193 | ||
194 | @interface TinyDOMNode : NSObject<TinyDOMNode> | |
93a37866 A |
195 | @end |
196 | ||
197 | @implementation TinyDOMNode { | |
198 | NSMutableArray *m_children; | |
81345200 | 199 | JSVirtualMachine *m_sharedVirtualMachine; |
93a37866 A |
200 | } |
201 | ||
81345200 | 202 | - (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine |
93a37866 A |
203 | { |
204 | self = [super init]; | |
205 | if (!self) | |
206 | return nil; | |
207 | ||
208 | m_children = [[NSMutableArray alloc] initWithCapacity:0]; | |
81345200 | 209 | m_sharedVirtualMachine = virtualMachine; |
93a37866 | 210 | #if !__has_feature(objc_arc) |
81345200 | 211 | [m_sharedVirtualMachine retain]; |
93a37866 | 212 | #endif |
81345200 A |
213 | |
214 | return self; | |
93a37866 A |
215 | } |
216 | ||
217 | - (void)appendChild:(TinyDOMNode *)child | |
218 | { | |
81345200 | 219 | [m_sharedVirtualMachine addManagedReference:child withOwner:self]; |
93a37866 A |
220 | [m_children addObject:child]; |
221 | } | |
222 | ||
223 | - (NSUInteger)numberOfChildren | |
224 | { | |
225 | return [m_children count]; | |
226 | } | |
227 | ||
228 | - (TinyDOMNode *)childAtIndex:(NSUInteger)index | |
229 | { | |
230 | if (index >= [m_children count]) | |
231 | return nil; | |
232 | return [m_children objectAtIndex:index]; | |
233 | } | |
234 | ||
235 | - (void)removeChildAtIndex:(NSUInteger)index | |
236 | { | |
237 | if (index >= [m_children count]) | |
238 | return; | |
81345200 | 239 | [m_sharedVirtualMachine removeManagedReference:[m_children objectAtIndex:index] withOwner:self]; |
93a37866 A |
240 | [m_children removeObjectAtIndex:index]; |
241 | } | |
242 | ||
243 | @end | |
244 | ||
12899fa2 A |
245 | @interface JSCollection : NSObject |
246 | - (void)setValue:(JSValue *)value forKey:(NSString *)key; | |
247 | - (JSValue *)valueForKey:(NSString *)key; | |
248 | @end | |
249 | ||
250 | @implementation JSCollection { | |
251 | NSMutableDictionary *_dict; | |
252 | } | |
253 | - (id)init | |
254 | { | |
255 | self = [super init]; | |
256 | if (!self) | |
257 | return nil; | |
258 | ||
259 | _dict = [[NSMutableDictionary alloc] init]; | |
260 | ||
261 | return self; | |
262 | } | |
263 | ||
264 | - (void)setValue:(JSValue *)value forKey:(NSString *)key | |
265 | { | |
266 | JSManagedValue *oldManagedValue = [_dict objectForKey:key]; | |
267 | if (oldManagedValue) { | |
268 | JSValue* oldValue = [oldManagedValue value]; | |
269 | if (oldValue) | |
270 | [oldValue.context.virtualMachine removeManagedReference:oldManagedValue withOwner:self]; | |
271 | } | |
272 | JSManagedValue *managedValue = [JSManagedValue managedValueWithValue:value]; | |
273 | [value.context.virtualMachine addManagedReference:managedValue withOwner:self]; | |
274 | [_dict setObject:managedValue forKey:key]; | |
275 | } | |
276 | ||
277 | - (JSValue *)valueForKey:(NSString *)key | |
278 | { | |
279 | JSManagedValue *managedValue = [_dict objectForKey:key]; | |
280 | if (!managedValue) | |
281 | return nil; | |
282 | return [managedValue value]; | |
283 | } | |
284 | @end | |
285 | ||
286 | @protocol InitA <JSExport> | |
287 | - (id)initWithA:(int)a; | |
81345200 | 288 | - (int)initialize; |
12899fa2 A |
289 | @end |
290 | ||
291 | @protocol InitB <JSExport> | |
292 | - (id)initWithA:(int)a b:(int)b; | |
293 | @end | |
294 | ||
81345200 A |
295 | @protocol InitC <JSExport> |
296 | - (id)_init; | |
297 | @end | |
298 | ||
12899fa2 A |
299 | @interface ClassA : NSObject<InitA> |
300 | @end | |
301 | ||
302 | @interface ClassB : ClassA<InitB> | |
303 | @end | |
304 | ||
305 | @interface ClassC : ClassB<InitA, InitB> | |
306 | @end | |
307 | ||
81345200 A |
308 | @interface ClassCPrime : ClassB<InitA, InitC> |
309 | @end | |
310 | ||
12899fa2 A |
311 | @interface ClassD : NSObject<InitA> |
312 | - (id)initWithA:(int)a; | |
313 | @end | |
314 | ||
315 | @interface ClassE : ClassD | |
316 | - (id)initWithA:(int)a; | |
317 | @end | |
318 | ||
319 | @implementation ClassA { | |
320 | int _a; | |
321 | } | |
322 | - (id)initWithA:(int)a | |
323 | { | |
324 | self = [super init]; | |
325 | if (!self) | |
326 | return nil; | |
327 | ||
328 | _a = a; | |
329 | ||
330 | return self; | |
331 | } | |
81345200 A |
332 | - (int)initialize |
333 | { | |
334 | return 42; | |
335 | } | |
12899fa2 A |
336 | @end |
337 | ||
338 | @implementation ClassB { | |
339 | int _b; | |
340 | } | |
341 | - (id)initWithA:(int)a b:(int)b | |
342 | { | |
343 | self = [super initWithA:a]; | |
344 | if (!self) | |
345 | return nil; | |
346 | ||
347 | _b = b; | |
348 | ||
349 | return self; | |
350 | } | |
351 | @end | |
352 | ||
353 | @implementation ClassC { | |
354 | int _c; | |
355 | } | |
356 | - (id)initWithA:(int)a | |
357 | { | |
358 | return [self initWithA:a b:0]; | |
359 | } | |
360 | - (id)initWithA:(int)a b:(int)b | |
361 | { | |
362 | self = [super initWithA:a b:b]; | |
363 | if (!self) | |
364 | return nil; | |
365 | ||
366 | _c = a + b; | |
367 | ||
368 | return self; | |
369 | } | |
370 | @end | |
371 | ||
81345200 A |
372 | @implementation ClassCPrime |
373 | - (id)initWithA:(int)a | |
374 | { | |
375 | self = [super initWithA:a b:0]; | |
376 | if (!self) | |
377 | return nil; | |
378 | return self; | |
379 | } | |
380 | - (id)_init | |
381 | { | |
382 | return [self initWithA:42]; | |
383 | } | |
384 | @end | |
385 | ||
12899fa2 A |
386 | @implementation ClassD |
387 | ||
388 | - (id)initWithA:(int)a | |
389 | { | |
390 | self = nil; | |
391 | return [[ClassE alloc] initWithA:a]; | |
392 | } | |
81345200 A |
393 | - (int)initialize |
394 | { | |
395 | return 0; | |
396 | } | |
12899fa2 A |
397 | @end |
398 | ||
399 | @implementation ClassE { | |
400 | int _a; | |
401 | } | |
402 | ||
403 | - (id)initWithA:(int)a | |
404 | { | |
405 | self = [super init]; | |
406 | if (!self) | |
407 | return nil; | |
408 | ||
409 | _a = a; | |
410 | ||
411 | return self; | |
412 | } | |
413 | @end | |
81345200 A |
414 | |
415 | static bool evilAllocationObjectWasDealloced = false; | |
416 | ||
417 | @interface EvilAllocationObject : NSObject | |
418 | - (JSValue *)doEvilThingsWithContext:(JSContext *)context; | |
419 | @end | |
420 | ||
421 | @implementation EvilAllocationObject { | |
422 | JSContext *m_context; | |
423 | } | |
424 | - (id)initWithContext:(JSContext *)context | |
425 | { | |
426 | self = [super init]; | |
427 | if (!self) | |
428 | return nil; | |
429 | ||
430 | m_context = context; | |
431 | ||
432 | return self; | |
433 | } | |
434 | - (void)dealloc | |
435 | { | |
436 | [self doEvilThingsWithContext:m_context]; | |
437 | evilAllocationObjectWasDealloced = true; | |
438 | #if !__has_feature(objc_arc) | |
439 | [super dealloc]; | |
440 | #endif | |
441 | } | |
442 | ||
443 | - (JSValue *)doEvilThingsWithContext:(JSContext *)context | |
444 | { | |
445 | JSValue *result = [context evaluateScript:@" \ | |
446 | (function() { \ | |
447 | var a = []; \ | |
448 | var sum = 0; \ | |
449 | for (var i = 0; i < 10000; ++i) { \ | |
450 | sum += i; \ | |
451 | a[i] = sum; \ | |
452 | } \ | |
453 | return sum; \ | |
454 | })()"]; | |
455 | ||
456 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
457 | return result; | |
458 | } | |
459 | @end | |
460 | ||
461 | extern "C" void checkResult(NSString *description, bool passed) | |
93a37866 A |
462 | { |
463 | NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED"); | |
464 | if (!passed) | |
465 | failed = 1; | |
466 | } | |
467 | ||
468 | static bool blockSignatureContainsClass() | |
469 | { | |
470 | static bool containsClass = ^{ | |
471 | id block = ^(NSString *string){ return string; }; | |
472 | return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString"); | |
473 | }(); | |
474 | return containsClass; | |
475 | } | |
476 | ||
40a37d08 A |
477 | static void* threadMain(void* contextPtr) |
478 | { | |
479 | JSContext *context = (__bridge JSContext*)contextPtr; | |
480 | ||
481 | // Do something to enter the VM. | |
482 | TestObject *testObject = [TestObject testObject]; | |
483 | context[@"testObject"] = testObject; | |
484 | pthread_exit(nullptr); | |
485 | } | |
486 | ||
ed1e77d3 A |
487 | // This test is flaky. Since GC marks C stack and registers as roots conservatively, |
488 | // objects not referenced logically can be accidentally marked and alive. | |
489 | // To avoid this situation as possible as we can, | |
490 | // 1. run this test first before stack is polluted, | |
491 | // 2. extract this test as a function to suppress stack height. | |
492 | static void testWeakValue() | |
93a37866 | 493 | { |
ed1e77d3 A |
494 | @autoreleasepool { |
495 | JSVirtualMachine *vm = [[JSVirtualMachine alloc] init]; | |
496 | TestObject *testObject = [TestObject testObject]; | |
497 | JSManagedValue *weakValue; | |
498 | @autoreleasepool { | |
499 | JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm]; | |
500 | context[@"testObject"] = testObject; | |
501 | weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]]; | |
502 | } | |
503 | ||
504 | @autoreleasepool { | |
505 | JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm]; | |
506 | context[@"testObject"] = testObject; | |
507 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
508 | checkResult(@"weak value == nil", ![weakValue value]); | |
509 | checkResult(@"root is still alive", !context[@"testObject"].isUndefined); | |
510 | } | |
511 | } | |
512 | } | |
93a37866 | 513 | |
ed1e77d3 A |
514 | static void testObjectiveCAPIMain() |
515 | { | |
81345200 A |
516 | @autoreleasepool { |
517 | JSVirtualMachine* vm = [[JSVirtualMachine alloc] init]; | |
518 | JSContext* context = [[JSContext alloc] initWithVirtualMachine:vm]; | |
519 | [context evaluateScript:@"bad"]; | |
520 | } | |
521 | ||
93a37866 A |
522 | @autoreleasepool { |
523 | JSContext *context = [[JSContext alloc] init]; | |
524 | JSValue *result = [context evaluateScript:@"2 + 2"]; | |
ed1e77d3 | 525 | checkResult(@"2 + 2", result.isNumber && [result toInt32] == 4); |
93a37866 A |
526 | } |
527 | ||
528 | @autoreleasepool { | |
529 | JSContext *context = [[JSContext alloc] init]; | |
530 | NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]]; | |
531 | checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]); | |
532 | } | |
533 | ||
534 | @autoreleasepool { | |
535 | JSContext *context = [[JSContext alloc] init]; | |
536 | context[@"message"] = @"Hello"; | |
537 | JSValue *result = [context evaluateScript:@"message + ', World!'"]; | |
ed1e77d3 A |
538 | checkResult(@"Hello, World!", result.isString && [result isEqualToObject:@"Hello, World!"]); |
539 | } | |
540 | ||
541 | @autoreleasepool { | |
542 | JSContext *context = [[JSContext alloc] init]; | |
543 | checkResult(@"Promise is not exposed", [context[@"Promise"] isUndefined]); | |
544 | JSValue *result = [context evaluateScript:@"typeof Promise"]; | |
545 | checkResult(@"typeof Promise is 'undefined'", result.isString && [result isEqualToObject:@"undefined"]); | |
93a37866 A |
546 | } |
547 | ||
548 | @autoreleasepool { | |
549 | JSContext *context = [[JSContext alloc] init]; | |
550 | JSValue *result = [context evaluateScript:@"({ x:42 })"]; | |
ed1e77d3 | 551 | checkResult(@"({ x:42 })", result.isObject && [result[@"x"] isEqualToObject:@42]); |
93a37866 A |
552 | id obj = [result toObject]; |
553 | checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]); | |
554 | id num = (NSDictionary *)obj[@"x"]; | |
555 | checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]); | |
556 | } | |
557 | ||
ed1e77d3 A |
558 | @autoreleasepool { |
559 | JSContext *context = [[JSContext alloc] init]; | |
560 | JSValue *result = [context evaluateScript:@"[ ]"]; | |
561 | checkResult(@"[ ]", result.isArray); | |
562 | } | |
563 | ||
564 | @autoreleasepool { | |
565 | JSContext *context = [[JSContext alloc] init]; | |
566 | JSValue *result = [context evaluateScript:@"new Date"]; | |
567 | checkResult(@"new Date", result.isDate); | |
568 | } | |
569 | ||
12899fa2 A |
570 | @autoreleasepool { |
571 | JSCollection* myPrivateProperties = [[JSCollection alloc] init]; | |
572 | ||
573 | @autoreleasepool { | |
574 | JSContext* context = [[JSContext alloc] init]; | |
575 | TestObject* rootObject = [TestObject testObject]; | |
576 | context[@"root"] = rootObject; | |
577 | [context.virtualMachine addManagedReference:myPrivateProperties withOwner:rootObject]; | |
578 | [myPrivateProperties setValue:[JSValue valueWithBool:true inContext:context] forKey:@"is_ham"]; | |
579 | [myPrivateProperties setValue:[JSValue valueWithObject:@"hello!" inContext:context] forKey:@"message"]; | |
580 | [myPrivateProperties setValue:[JSValue valueWithInt32:42 inContext:context] forKey:@"my_number"]; | |
581 | [myPrivateProperties setValue:[JSValue valueWithNullInContext:context] forKey:@"definitely_null"]; | |
582 | [myPrivateProperties setValue:[JSValue valueWithUndefinedInContext:context] forKey:@"not_sure_if_undefined"]; | |
583 | ||
584 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
585 | ||
586 | JSValue *isHam = [myPrivateProperties valueForKey:@"is_ham"]; | |
587 | JSValue *message = [myPrivateProperties valueForKey:@"message"]; | |
588 | JSValue *myNumber = [myPrivateProperties valueForKey:@"my_number"]; | |
589 | JSValue *definitelyNull = [myPrivateProperties valueForKey:@"definitely_null"]; | |
590 | JSValue *notSureIfUndefined = [myPrivateProperties valueForKey:@"not_sure_if_undefined"]; | |
ed1e77d3 A |
591 | checkResult(@"is_ham is true", isHam.isBoolean && [isHam toBool]); |
592 | checkResult(@"message is hello!", message.isString && [@"hello!" isEqualToString:[message toString]]); | |
593 | checkResult(@"my_number is 42", myNumber.isNumber && [myNumber toInt32] == 42); | |
594 | checkResult(@"definitely_null is null", definitelyNull.isNull); | |
595 | checkResult(@"not_sure_if_undefined is undefined", notSureIfUndefined.isUndefined); | |
12899fa2 A |
596 | } |
597 | ||
598 | checkResult(@"is_ham is nil", ![myPrivateProperties valueForKey:@"is_ham"]); | |
599 | checkResult(@"message is nil", ![myPrivateProperties valueForKey:@"message"]); | |
600 | checkResult(@"my_number is 42", ![myPrivateProperties valueForKey:@"my_number"]); | |
601 | checkResult(@"definitely_null is null", ![myPrivateProperties valueForKey:@"definitely_null"]); | |
602 | checkResult(@"not_sure_if_undefined is undefined", ![myPrivateProperties valueForKey:@"not_sure_if_undefined"]); | |
603 | } | |
604 | ||
81345200 A |
605 | @autoreleasepool { |
606 | JSContext *context = [[JSContext alloc] init]; | |
607 | JSValue *message = [JSValue valueWithObject:@"hello" inContext:context]; | |
608 | TestObject *rootObject = [TestObject testObject]; | |
609 | JSCollection *collection = [[JSCollection alloc] init]; | |
610 | context[@"root"] = rootObject; | |
611 | @autoreleasepool { | |
612 | JSValue *jsCollection = [JSValue valueWithObject:collection inContext:context]; | |
613 | JSManagedValue *weakCollection = [JSManagedValue managedValueWithValue:jsCollection andOwner:rootObject]; | |
614 | [context.virtualMachine addManagedReference:weakCollection withOwner:message]; | |
615 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
616 | } | |
617 | } | |
618 | ||
93a37866 A |
619 | @autoreleasepool { |
620 | JSContext *context = [[JSContext alloc] init]; | |
621 | __block int result; | |
622 | context[@"blockCallback"] = ^(int value){ | |
623 | result = value; | |
624 | }; | |
625 | [context evaluateScript:@"blockCallback(42)"]; | |
626 | checkResult(@"blockCallback", result == 42); | |
627 | } | |
628 | ||
629 | if (blockSignatureContainsClass()) { | |
630 | @autoreleasepool { | |
631 | JSContext *context = [[JSContext alloc] init]; | |
632 | __block bool result = false; | |
633 | context[@"blockCallback"] = ^(NSString *value){ | |
634 | result = [@"42" isEqualToString:value] == YES; | |
635 | }; | |
636 | [context evaluateScript:@"blockCallback(42)"]; | |
637 | checkResult(@"blockCallback(NSString *)", result); | |
638 | } | |
639 | } else | |
640 | NSLog(@"Skipping 'blockCallback(NSString *)' test case"); | |
641 | ||
642 | @autoreleasepool { | |
643 | JSContext *context = [[JSContext alloc] init]; | |
644 | checkResult(@"!context.exception", !context.exception); | |
645 | [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | |
646 | checkResult(@"context.exception", context.exception); | |
647 | } | |
648 | ||
649 | @autoreleasepool { | |
650 | JSContext *context = [[JSContext alloc] init]; | |
651 | __block bool caught = false; | |
652 | context.exceptionHandler = ^(JSContext *context, JSValue *exception) { | |
653 | (void)context; | |
654 | (void)exception; | |
655 | caught = true; | |
656 | }; | |
657 | [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | |
658 | checkResult(@"JSContext.exceptionHandler", caught); | |
659 | } | |
660 | ||
81345200 A |
661 | @autoreleasepool { |
662 | JSContext *context = [[JSContext alloc] init]; | |
663 | __block int expectedExceptionLineNumber = 1; | |
664 | __block bool sawExpectedExceptionLineNumber = false; | |
665 | context.exceptionHandler = ^(JSContext *, JSValue *exception) { | |
666 | sawExpectedExceptionLineNumber = [exception[@"line"] toInt32] == expectedExceptionLineNumber; | |
667 | }; | |
668 | [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | |
669 | checkResult(@"evaluteScript exception on line 1", sawExpectedExceptionLineNumber); | |
670 | ||
671 | expectedExceptionLineNumber = 2; | |
672 | sawExpectedExceptionLineNumber = false; | |
673 | [context evaluateScript:@"// Line 1\n!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | |
674 | checkResult(@"evaluteScript exception on line 2", sawExpectedExceptionLineNumber); | |
675 | } | |
676 | ||
677 | @autoreleasepool { | |
678 | JSContext *context = [[JSContext alloc] init]; | |
679 | __block bool emptyExceptionSourceURL = false; | |
680 | context.exceptionHandler = ^(JSContext *, JSValue *exception) { | |
ed1e77d3 | 681 | emptyExceptionSourceURL = exception[@"sourceURL"].isUndefined; |
81345200 A |
682 | }; |
683 | [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | |
684 | checkResult(@"evaluteScript: exception has no sourceURL", emptyExceptionSourceURL); | |
685 | ||
686 | __block NSString *exceptionSourceURL = nil; | |
687 | context.exceptionHandler = ^(JSContext *, JSValue *exception) { | |
688 | exceptionSourceURL = [exception[@"sourceURL"] toString]; | |
689 | }; | |
690 | NSURL *url = [NSURL fileURLWithPath:@"/foo/bar.js"]; | |
691 | [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()" withSourceURL:url]; | |
692 | checkResult(@"evaluateScript:withSourceURL: exception has expected sourceURL", [exceptionSourceURL isEqualToString:[url absoluteString]]); | |
693 | } | |
694 | ||
93a37866 A |
695 | @autoreleasepool { |
696 | JSContext *context = [[JSContext alloc] init]; | |
697 | context[@"callback"] = ^{ | |
698 | JSContext *context = [JSContext currentContext]; | |
699 | context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context]; | |
700 | }; | |
701 | JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"]; | |
702 | checkResult(@"Explicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]); | |
703 | checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception); | |
704 | } | |
705 | ||
706 | @autoreleasepool { | |
707 | JSContext *context = [[JSContext alloc] init]; | |
708 | context[@"callback"] = ^{ | |
709 | JSContext *context = [JSContext currentContext]; | |
710 | [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"]; | |
711 | }; | |
712 | JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"]; | |
713 | checkResult(@"Implicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]); | |
714 | checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception); | |
715 | } | |
716 | ||
717 | @autoreleasepool { | |
718 | JSContext *context = [[JSContext alloc] init]; | |
719 | [context evaluateScript: | |
720 | @"function sum(array) { \ | |
721 | var result = 0; \ | |
722 | for (var i in array) \ | |
723 | result += array[i]; \ | |
724 | return result; \ | |
725 | }"]; | |
726 | JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context]; | |
727 | JSValue *sumFunction = context[@"sum"]; | |
728 | JSValue *result = [sumFunction callWithArguments:@[ array ]]; | |
729 | checkResult(@"sum([13, 2, 7])", [result toInt32] == 22); | |
730 | } | |
731 | ||
732 | @autoreleasepool { | |
733 | JSContext *context = [[JSContext alloc] init]; | |
734 | JSValue *mulAddFunction = [context evaluateScript: | |
735 | @"(function(array, object) { \ | |
736 | var result = []; \ | |
737 | for (var i in array) \ | |
738 | result.push(array[i] * object.x + object.y); \ | |
739 | return result; \ | |
740 | })"]; | |
741 | JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]]; | |
ed1e77d3 | 742 | checkResult(@"mulAddFunction", result.isObject && [[result toString] isEqual:@"43,44,46"]); |
93a37866 A |
743 | } |
744 | ||
745 | @autoreleasepool { | |
746 | JSContext *context = [[JSContext alloc] init]; | |
747 | JSValue *array = [JSValue valueWithNewArrayInContext:context]; | |
748 | checkResult(@"arrayLengthEmpty", [[array[@"length"] toNumber] unsignedIntegerValue] == 0); | |
749 | JSValue *value1 = [JSValue valueWithInt32:42 inContext:context]; | |
750 | JSValue *value2 = [JSValue valueWithInt32:24 inContext:context]; | |
751 | NSUInteger lowIndex = 5; | |
752 | NSUInteger maxLength = UINT_MAX; | |
753 | ||
754 | [array setValue:value1 atIndex:lowIndex]; | |
755 | checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1)); | |
756 | ||
757 | [array setValue:value1 atIndex:(maxLength - 1)]; | |
758 | checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength); | |
759 | ||
760 | [array setValue:value2 atIndex:maxLength]; | |
761 | checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength); | |
762 | ||
763 | [array setValue:value2 atIndex:(maxLength + 1)]; | |
764 | checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength); | |
765 | ||
766 | if (sizeof(NSUInteger) == 8) | |
ed1e77d3 | 767 | checkResult(@"valueAtIndex:0 is undefined", [array valueAtIndex:0].isUndefined); |
93a37866 A |
768 | else |
769 | checkResult(@"valueAtIndex:0", [[array valueAtIndex:0] toInt32] == 24); | |
770 | checkResult(@"valueAtIndex:lowIndex", [[array valueAtIndex:lowIndex] toInt32] == 42); | |
771 | checkResult(@"valueAtIndex:maxLength - 1", [[array valueAtIndex:(maxLength - 1)] toInt32] == 42); | |
772 | checkResult(@"valueAtIndex:maxLength", [[array valueAtIndex:maxLength] toInt32] == 24); | |
773 | checkResult(@"valueAtIndex:maxLength + 1", [[array valueAtIndex:(maxLength + 1)] toInt32] == 24); | |
774 | } | |
775 | ||
776 | @autoreleasepool { | |
777 | JSContext *context = [[JSContext alloc] init]; | |
778 | JSValue *object = [JSValue valueWithNewObjectInContext:context]; | |
779 | ||
780 | object[@"point"] = @{ @"x":@1, @"y":@2 }; | |
781 | object[@"point"][@"x"] = @3; | |
782 | CGPoint point = [object[@"point"] toPoint]; | |
783 | checkResult(@"toPoint", point.x == 3 && point.y == 2); | |
784 | ||
785 | object[@{ @"toString":^{ return @"foo"; } }] = @"bar"; | |
786 | checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]); | |
787 | ||
788 | object[[@"foobar" substringToIndex:3]] = @"bar"; | |
789 | checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]); | |
790 | } | |
791 | ||
93a37866 A |
792 | @autoreleasepool { |
793 | JSContext *context = [[JSContext alloc] init]; | |
794 | TextXYZ *testXYZ = [[TextXYZ alloc] init]; | |
795 | context[@"testXYZ"] = testXYZ; | |
796 | testXYZ.x = 3; | |
797 | testXYZ.y = 4; | |
798 | testXYZ.z = 5; | |
799 | [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"]; | |
800 | [context evaluateScript:@"testXYZ.test('test')"]; | |
801 | checkResult(@"TextXYZ - testXYZTested", testXYZTested); | |
802 | JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"]; | |
803 | checkResult(@"TextXYZ - result", [result isEqualToObject:@"13,4,undefined"]); | |
804 | } | |
805 | ||
806 | @autoreleasepool { | |
807 | JSContext *context = [[JSContext alloc] init]; | |
808 | [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{ | |
809 | JSPropertyDescriptorGetKey:^{ | |
810 | return [JSContext currentThis][@"x"]; | |
811 | } | |
812 | }]; | |
813 | JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context]; | |
814 | int result = [object [@"getterProperty"] toInt32]; | |
815 | checkResult(@"getterProperty", result == 101); | |
816 | } | |
817 | ||
818 | @autoreleasepool { | |
819 | JSContext *context = [[JSContext alloc] init]; | |
820 | context[@"concatenate"] = ^{ | |
821 | NSArray *arguments = [JSContext currentArguments]; | |
822 | if (![arguments count]) | |
823 | return @""; | |
824 | NSString *message = [arguments[0] description]; | |
825 | for (NSUInteger index = 1; index < [arguments count]; ++index) | |
826 | message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]]; | |
827 | return message; | |
828 | }; | |
829 | JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"]; | |
830 | checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]); | |
831 | } | |
832 | ||
833 | @autoreleasepool { | |
834 | JSContext *context = [[JSContext alloc] init]; | |
835 | context[@"foo"] = @YES; | |
836 | checkResult(@"@YES is boolean", [context[@"foo"] isBoolean]); | |
837 | JSValue *result = [context evaluateScript:@"typeof foo"]; | |
838 | checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]); | |
839 | } | |
840 | ||
81345200 A |
841 | @autoreleasepool { |
842 | JSContext *context = [[JSContext alloc] init]; | |
843 | JSValue *result = [context evaluateScript:@"String(console)"]; | |
844 | checkResult(@"String(console)", [result isEqualToObject:@"[object Console]"]); | |
845 | result = [context evaluateScript:@"typeof console.log"]; | |
846 | checkResult(@"typeof console.log", [result isEqualToObject:@"function"]); | |
847 | } | |
848 | ||
93a37866 A |
849 | @autoreleasepool { |
850 | JSContext *context = [[JSContext alloc] init]; | |
851 | TestObject* testObject = [TestObject testObject]; | |
852 | context[@"testObject"] = testObject; | |
853 | JSValue *result = [context evaluateScript:@"String(testObject)"]; | |
854 | checkResult(@"String(testObject)", [result isEqualToObject:@"[object TestObject]"]); | |
855 | } | |
856 | ||
857 | @autoreleasepool { | |
858 | JSContext *context = [[JSContext alloc] init]; | |
859 | TestObject* testObject = [TestObject testObject]; | |
860 | context[@"testObject"] = testObject; | |
861 | JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"]; | |
862 | checkResult(@"String(testObject.__proto__)", [result isEqualToObject:@"[object TestObjectPrototype]"]); | |
863 | } | |
864 | ||
865 | @autoreleasepool { | |
866 | JSContext *context = [[JSContext alloc] init]; | |
867 | context[@"TestObject"] = [TestObject class]; | |
868 | JSValue *result = [context evaluateScript:@"String(TestObject)"]; | |
12899fa2 | 869 | checkResult(@"String(TestObject)", [result isEqualToObject:@"function TestObject() {\n [native code]\n}"]); |
93a37866 A |
870 | } |
871 | ||
872 | @autoreleasepool { | |
873 | JSContext *context = [[JSContext alloc] init]; | |
874 | JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context]; | |
875 | checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]); | |
876 | } | |
877 | ||
878 | @autoreleasepool { | |
879 | JSContext *context = [[JSContext alloc] init]; | |
880 | context[@"TestObject"] = [TestObject class]; | |
881 | JSValue *result = [context evaluateScript:@"TestObject.parentTest()"]; | |
882 | checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]); | |
883 | } | |
884 | ||
885 | @autoreleasepool { | |
886 | JSContext *context = [[JSContext alloc] init]; | |
887 | TestObject* testObject = [TestObject testObject]; | |
888 | context[@"testObjectA"] = testObject; | |
889 | context[@"testObjectB"] = testObject; | |
890 | JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"]; | |
ed1e77d3 | 891 | checkResult(@"testObjectA == testObjectB", result.isBoolean && [result toBool]); |
93a37866 A |
892 | } |
893 | ||
894 | @autoreleasepool { | |
895 | JSContext *context = [[JSContext alloc] init]; | |
896 | TestObject* testObject = [TestObject testObject]; | |
897 | context[@"testObject"] = testObject; | |
898 | testObject.point = (CGPoint){3,4}; | |
899 | JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"]; | |
900 | checkResult(@"testObject.point - result", [result isEqualToObject:@"{\"x\":3,\"y\":4}"]); | |
901 | checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14); | |
902 | } | |
903 | ||
904 | @autoreleasepool { | |
905 | JSContext *context = [[JSContext alloc] init]; | |
906 | TestObject* testObject = [TestObject testObject]; | |
907 | testObject.six = 6; | |
908 | context[@"testObject"] = testObject; | |
909 | context[@"mul"] = ^(int x, int y){ return x * y; }; | |
910 | JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"]; | |
ed1e77d3 | 911 | checkResult(@"mul(testObject.six, 7)", result.isNumber && [result toInt32] == 42); |
93a37866 A |
912 | } |
913 | ||
914 | @autoreleasepool { | |
915 | JSContext *context = [[JSContext alloc] init]; | |
916 | TestObject* testObject = [TestObject testObject]; | |
917 | context[@"testObject"] = testObject; | |
918 | context[@"testObject"][@"variable"] = @4; | |
919 | [context evaluateScript:@"++testObject.variable"]; | |
920 | checkResult(@"++testObject.variable", testObject.variable == 5); | |
921 | } | |
922 | ||
923 | @autoreleasepool { | |
924 | JSContext *context = [[JSContext alloc] init]; | |
925 | context[@"point"] = @{ @"x":@6, @"y":@7 }; | |
926 | JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"]; | |
927 | checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]); | |
928 | } | |
929 | ||
930 | @autoreleasepool { | |
931 | JSContext *context = [[JSContext alloc] init]; | |
932 | context[@"point"] = @{ @"x":@6, @"y":@7 }; | |
933 | JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"]; | |
934 | checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]); | |
935 | } | |
936 | ||
937 | @autoreleasepool { | |
938 | JSContext *context = [[JSContext alloc] init]; | |
939 | TestObject* testObject = [TestObject testObject]; | |
940 | context[@"testObject"] = testObject; | |
941 | JSValue *result = [context evaluateScript:@"testObject.getString()"]; | |
ed1e77d3 | 942 | checkResult(@"testObject.getString()", result.isString && [result toInt32] == 42); |
93a37866 A |
943 | } |
944 | ||
945 | @autoreleasepool { | |
946 | JSContext *context = [[JSContext alloc] init]; | |
947 | TestObject* testObject = [TestObject testObject]; | |
948 | context[@"testObject"] = testObject; | |
949 | JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"]; | |
950 | checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]); | |
951 | } | |
952 | ||
953 | @autoreleasepool { | |
954 | JSContext *context = [[JSContext alloc] init]; | |
955 | TestObject* testObject = [TestObject testObject]; | |
956 | context[@"testObject"] = testObject; | |
957 | JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"]; | |
ed1e77d3 | 958 | checkResult(@"testObject.getString.call(testObject)", result.isString && [result toInt32] == 42); |
93a37866 A |
959 | } |
960 | ||
961 | @autoreleasepool { | |
962 | JSContext *context = [[JSContext alloc] init]; | |
963 | TestObject* testObject = [TestObject testObject]; | |
964 | context[@"testObject"] = testObject; | |
965 | checkResult(@"testObject.getString.call({}) pre", !context.exception); | |
966 | [context evaluateScript:@"testObject.getString.call({})"]; | |
967 | checkResult(@"testObject.getString.call({}) post", context.exception); | |
968 | } | |
969 | ||
970 | @autoreleasepool { | |
971 | JSContext *context = [[JSContext alloc] init]; | |
972 | TestObject* testObject = [TestObject testObject]; | |
973 | context[@"testObject"] = testObject; | |
974 | JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"]; | |
ed1e77d3 | 975 | checkResult(@"testObject.callback", result.isNumber && [result toInt32] == 42); |
93a37866 | 976 | result = [context evaluateScript:@"testObject.bogusCallback"]; |
ed1e77d3 | 977 | checkResult(@"testObject.bogusCallback == undefined", result.isUndefined); |
93a37866 A |
978 | } |
979 | ||
980 | @autoreleasepool { | |
981 | JSContext *context = [[JSContext alloc] init]; | |
982 | TestObject *testObject = [TestObject testObject]; | |
983 | context[@"testObject"] = testObject; | |
984 | JSValue *result = [context evaluateScript:@"Function.prototype.toString.call(testObject.callback)"]; | |
ed1e77d3 | 985 | checkResult(@"Function.prototype.toString", !context.exception && !result.isUndefined); |
93a37866 A |
986 | } |
987 | ||
988 | @autoreleasepool { | |
989 | JSContext *context1 = [[JSContext alloc] init]; | |
990 | JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine]; | |
991 | JSValue *value = [JSValue valueWithDouble:42 inContext:context2]; | |
992 | context1[@"passValueBetweenContexts"] = value; | |
993 | JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"]; | |
994 | checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]); | |
995 | } | |
996 | ||
997 | @autoreleasepool { | |
998 | JSContext *context = [[JSContext alloc] init]; | |
999 | context[@"handleTheDictionary"] = ^(NSDictionary *dict) { | |
1000 | NSDictionary *expectedDict = @{ | |
1001 | @"foo" : [NSNumber numberWithInt:1], | |
1002 | @"bar" : @{ | |
1003 | @"baz": [NSNumber numberWithInt:2] | |
1004 | } | |
1005 | }; | |
1006 | checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]); | |
1007 | }; | |
1008 | [context evaluateScript:@"var myDict = { \ | |
1009 | 'foo': 1, \ | |
1010 | 'bar': {'baz': 2} \ | |
1011 | }; \ | |
1012 | handleTheDictionary(myDict);"]; | |
1013 | ||
1014 | context[@"handleTheArray"] = ^(NSArray *array) { | |
1015 | NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]]; | |
1016 | checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]); | |
1017 | }; | |
1018 | [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"]; | |
1019 | } | |
1020 | ||
1021 | @autoreleasepool { | |
1022 | JSContext *context = [[JSContext alloc] init]; | |
1023 | TestObject *testObject = [TestObject testObject]; | |
1024 | @autoreleasepool { | |
1025 | context[@"testObject"] = testObject; | |
1026 | [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"]; | |
1027 | [context evaluateScript:@"testObject = undefined"]; | |
1028 | } | |
1029 | ||
1030 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
1031 | ||
1032 | @autoreleasepool { | |
1033 | context[@"testObject"] = testObject; | |
1034 | } | |
1035 | } | |
1036 | ||
1037 | @autoreleasepool { | |
1038 | JSContext *context = [[JSContext alloc] init]; | |
1039 | TextXYZ *testXYZ = [[TextXYZ alloc] init]; | |
1040 | ||
1041 | @autoreleasepool { | |
1042 | context[@"testXYZ"] = testXYZ; | |
1043 | ||
1044 | [context evaluateScript:@" \ | |
1045 | didClick = false; \ | |
1046 | testXYZ.onclick = function() { \ | |
1047 | didClick = true; \ | |
1048 | }; \ | |
1049 | \ | |
1050 | testXYZ.weakOnclick = function() { \ | |
1051 | return 'foo'; \ | |
1052 | }; \ | |
1053 | "]; | |
1054 | } | |
1055 | ||
1056 | @autoreleasepool { | |
1057 | [testXYZ click]; | |
1058 | JSValue *result = [context evaluateScript:@"didClick"]; | |
1059 | checkResult(@"Event handler onclick", [result toBool]); | |
1060 | } | |
1061 | ||
1062 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
1063 | ||
1064 | @autoreleasepool { | |
1065 | JSValue *result = [context evaluateScript:@"testXYZ.onclick"]; | |
ed1e77d3 | 1066 | checkResult(@"onclick still around after GC", !(result.isNull || result.isUndefined)); |
93a37866 A |
1067 | } |
1068 | ||
1069 | ||
1070 | @autoreleasepool { | |
1071 | JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"]; | |
ed1e77d3 | 1072 | checkResult(@"weakOnclick not around after GC", result.isNull || result.isUndefined); |
93a37866 A |
1073 | } |
1074 | ||
1075 | @autoreleasepool { | |
1076 | [context evaluateScript:@" \ | |
1077 | didClick = false; \ | |
1078 | testXYZ = null; \ | |
1079 | "]; | |
1080 | } | |
1081 | ||
1082 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
1083 | ||
1084 | @autoreleasepool { | |
1085 | [testXYZ click]; | |
1086 | JSValue *result = [context evaluateScript:@"didClick"]; | |
1087 | checkResult(@"Event handler onclick doesn't fire", ![result toBool]); | |
1088 | } | |
1089 | } | |
1090 | ||
93a37866 | 1091 | @autoreleasepool { |
81345200 A |
1092 | JSContext *context = [[JSContext alloc] init]; |
1093 | TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine]; | |
93a37866 A |
1094 | TinyDOMNode *lastNode = root; |
1095 | for (NSUInteger i = 0; i < 3; i++) { | |
81345200 | 1096 | TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine]; |
93a37866 A |
1097 | [lastNode appendChild:newNode]; |
1098 | lastNode = newNode; | |
1099 | } | |
1100 | ||
1101 | @autoreleasepool { | |
1102 | context[@"root"] = root; | |
1103 | context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){ | |
1104 | TinyDOMNode *lastNode = nil; | |
1105 | while (head) { | |
1106 | lastNode = head; | |
1107 | head = [lastNode childAtIndex:0]; | |
1108 | } | |
1109 | return lastNode; | |
1110 | }; | |
1111 | [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"]; | |
1112 | } | |
1113 | ||
1114 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
1115 | ||
1116 | JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"]; | |
ed1e77d3 | 1117 | checkResult(@"My custom property == 42", myCustomProperty.isNumber && [myCustomProperty toInt32] == 42); |
93a37866 A |
1118 | } |
1119 | ||
1120 | @autoreleasepool { | |
81345200 A |
1121 | JSContext *context = [[JSContext alloc] init]; |
1122 | TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine]; | |
93a37866 A |
1123 | TinyDOMNode *lastNode = root; |
1124 | for (NSUInteger i = 0; i < 3; i++) { | |
81345200 | 1125 | TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine]; |
93a37866 A |
1126 | [lastNode appendChild:newNode]; |
1127 | lastNode = newNode; | |
1128 | } | |
1129 | ||
1130 | @autoreleasepool { | |
1131 | context[@"root"] = root; | |
1132 | context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){ | |
1133 | TinyDOMNode *lastNode = nil; | |
1134 | while (head) { | |
1135 | lastNode = head; | |
1136 | head = [lastNode childAtIndex:0]; | |
1137 | } | |
1138 | return lastNode; | |
1139 | }; | |
1140 | [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"]; | |
1141 | ||
1142 | [root appendChild:[root childAtIndex:0]]; | |
1143 | [root removeChildAtIndex:0]; | |
1144 | } | |
1145 | ||
1146 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
1147 | ||
1148 | JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"]; | |
ed1e77d3 | 1149 | checkResult(@"duplicate calls to addManagedReference don't cause things to die", myCustomProperty.isNumber && [myCustomProperty toInt32] == 42); |
93a37866 A |
1150 | } |
1151 | ||
1152 | @autoreleasepool { | |
1153 | JSContext *context = [[JSContext alloc] init]; | |
1154 | JSValue *o = [JSValue valueWithNewObjectInContext:context]; | |
1155 | o[@"foo"] = @"foo"; | |
1156 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
1157 | ||
1158 | checkResult(@"JSValue correctly protected its internal value", [[o[@"foo"] toString] isEqualToString:@"foo"]); | |
1159 | } | |
1160 | ||
1161 | @autoreleasepool { | |
1162 | JSContext *context = [[JSContext alloc] init]; | |
1163 | TestObject *testObject = [TestObject testObject]; | |
1164 | context[@"testObject"] = testObject; | |
1165 | [context evaluateScript:@"testObject.__lookupGetter__('variable').call({})"]; | |
1166 | checkResult(@"Make sure we throw an exception when calling getter on incorrect |this|", context.exception); | |
1167 | } | |
1168 | ||
1169 | @autoreleasepool { | |
1170 | TestObject *testObject = [TestObject testObject]; | |
1171 | JSManagedValue *managedTestObject; | |
1172 | @autoreleasepool { | |
1173 | JSContext *context = [[JSContext alloc] init]; | |
1174 | context[@"testObject"] = testObject; | |
1175 | managedTestObject = [JSManagedValue managedValueWithValue:context[@"testObject"]]; | |
1176 | [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject]; | |
1177 | } | |
1178 | } | |
12899fa2 A |
1179 | |
1180 | @autoreleasepool { | |
1181 | JSContext *context = [[JSContext alloc] init]; | |
81345200 A |
1182 | TestObject *testObject = [TestObject testObject]; |
1183 | context[@"testObject"] = testObject; | |
1184 | JSManagedValue *managedValue = nil; | |
1185 | @autoreleasepool { | |
1186 | JSValue *object = [JSValue valueWithNewObjectInContext:context]; | |
1187 | managedValue = [JSManagedValue managedValueWithValue:object andOwner:testObject]; | |
1188 | [context.virtualMachine addManagedReference:managedValue withOwner:testObject]; | |
1189 | } | |
1190 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
12899fa2 A |
1191 | } |
1192 | ||
1193 | @autoreleasepool { | |
1194 | JSContext *context = [[JSContext alloc] init]; | |
1195 | context[@"MyClass"] = ^{ | |
1196 | JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]]; | |
1197 | JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef]; | |
1198 | JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL); | |
1199 | JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyClass"][@"prototype"] JSValueRef]); | |
1200 | return newThis; | |
1201 | }; | |
1202 | ||
1203 | context[@"MyOtherClass"] = ^{ | |
1204 | JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]]; | |
1205 | JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef]; | |
1206 | JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL); | |
1207 | JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentContext][@"MyOtherClass"][@"prototype"] JSValueRef]); | |
1208 | return newThis; | |
1209 | }; | |
1210 | ||
1211 | context.exceptionHandler = ^(JSContext *context, JSValue *exception) { | |
1212 | NSLog(@"EXCEPTION: %@", [exception toString]); | |
1213 | context.exception = nil; | |
1214 | }; | |
1215 | ||
1216 | JSValue *constructor1 = context[@"MyClass"]; | |
1217 | JSValue *constructor2 = context[@"MyOtherClass"]; | |
1218 | ||
1219 | JSValue *value1 = [context evaluateScript:@"new MyClass()"]; | |
1220 | checkResult(@"value1 instanceof MyClass", [value1 isInstanceOf:constructor1]); | |
1221 | checkResult(@"!(value1 instanceof MyOtherClass)", ![value1 isInstanceOf:constructor2]); | |
1222 | checkResult(@"MyClass.prototype.constructor === MyClass", [[context evaluateScript:@"MyClass.prototype.constructor === MyClass"] toBool]); | |
1223 | checkResult(@"MyClass instanceof Function", [[context evaluateScript:@"MyClass instanceof Function"] toBool]); | |
1224 | ||
1225 | JSValue *value2 = [context evaluateScript:@"new MyOtherClass()"]; | |
1226 | checkResult(@"value2 instanceof MyOtherClass", [value2 isInstanceOf:constructor2]); | |
1227 | checkResult(@"!(value2 instanceof MyClass)", ![value2 isInstanceOf:constructor1]); | |
1228 | checkResult(@"MyOtherClass.prototype.constructor === MyOtherClass", [[context evaluateScript:@"MyOtherClass.prototype.constructor === MyOtherClass"] toBool]); | |
1229 | checkResult(@"MyOtherClass instanceof Function", [[context evaluateScript:@"MyOtherClass instanceof Function"] toBool]); | |
1230 | } | |
1231 | ||
1232 | @autoreleasepool { | |
1233 | JSContext *context = [[JSContext alloc] init]; | |
1234 | context[@"MyClass"] = ^{ | |
1235 | NSLog(@"I'm intentionally not returning anything."); | |
1236 | }; | |
1237 | JSValue *result = [context evaluateScript:@"new MyClass()"]; | |
ed1e77d3 | 1238 | checkResult(@"result === undefined", result.isUndefined); |
81345200 A |
1239 | checkResult(@"exception.message is correct'", context.exception |
1240 | && [@"Objective-C blocks called as constructors must return an object." isEqualToString:[context.exception[@"message"] toString]]); | |
1241 | } | |
1242 | ||
1243 | @autoreleasepool { | |
1244 | checkResult(@"[JSContext currentThis] == nil outside of callback", ![JSContext currentThis]); | |
1245 | checkResult(@"[JSContext currentArguments] == nil outside of callback", ![JSContext currentArguments]); | |
1246 | if ([JSContext currentCallee]) | |
1247 | checkResult(@"[JSContext currentCallee] == nil outside of callback", ![JSContext currentCallee]); | |
1248 | } | |
1249 | ||
1250 | if ([JSContext currentCallee]) { | |
1251 | @autoreleasepool { | |
1252 | JSContext *context = [[JSContext alloc] init]; | |
1253 | context[@"testFunction"] = ^{ | |
1254 | checkResult(@"testFunction.foo === 42", [[JSContext currentCallee][@"foo"] toInt32] == 42); | |
1255 | }; | |
1256 | context[@"testFunction"][@"foo"] = @42; | |
1257 | [context[@"testFunction"] callWithArguments:nil]; | |
1258 | ||
1259 | context[@"TestConstructor"] = ^{ | |
1260 | JSValue *newThis = [JSValue valueWithNewObjectInContext:[JSContext currentContext]]; | |
1261 | JSGlobalContextRef contextRef = [[JSContext currentContext] JSGlobalContextRef]; | |
1262 | JSObjectRef newThisRef = JSValueToObject(contextRef, [newThis JSValueRef], NULL); | |
1263 | JSObjectSetPrototype(contextRef, newThisRef, [[JSContext currentCallee][@"prototype"] JSValueRef]); | |
1264 | return newThis; | |
1265 | }; | |
1266 | checkResult(@"(new TestConstructor) instanceof TestConstructor", [context evaluateScript:@"(new TestConstructor) instanceof TestConstructor"]); | |
1267 | } | |
1268 | } | |
1269 | ||
12899fa2 A |
1270 | @autoreleasepool { |
1271 | JSContext *context = [[JSContext alloc] init]; | |
1272 | context[@"TestObject"] = [TestObject class]; | |
1273 | JSValue *testObject = [context evaluateScript:@"(new TestObject())"]; | |
1274 | checkResult(@"testObject instanceof TestObject", [testObject isInstanceOf:context[@"TestObject"]]); | |
1275 | ||
1276 | context[@"TextXYZ"] = [TextXYZ class]; | |
1277 | JSValue *textObject = [context evaluateScript:@"(new TextXYZ(\"Called TextXYZ constructor!\"))"]; | |
1278 | checkResult(@"textObject instanceof TextXYZ", [textObject isInstanceOf:context[@"TextXYZ"]]); | |
1279 | } | |
1280 | ||
1281 | @autoreleasepool { | |
1282 | JSContext *context = [[JSContext alloc] init]; | |
1283 | context[@"ClassA"] = [ClassA class]; | |
1284 | context[@"ClassB"] = [ClassB class]; | |
1285 | context[@"ClassC"] = [ClassC class]; // Should print error message about too many inits found. | |
81345200 | 1286 | context[@"ClassCPrime"] = [ClassCPrime class]; // Ditto. |
12899fa2 A |
1287 | |
1288 | JSValue *a = [context evaluateScript:@"(new ClassA(42))"]; | |
1289 | checkResult(@"a instanceof ClassA", [a isInstanceOf:context[@"ClassA"]]); | |
81345200 | 1290 | checkResult(@"a.initialize() is callable", [[a invokeMethod:@"initialize" withArguments:@[]] toInt32] == 42); |
12899fa2 A |
1291 | |
1292 | JSValue *b = [context evaluateScript:@"(new ClassB(42, 53))"]; | |
1293 | checkResult(@"b instanceof ClassB", [b isInstanceOf:context[@"ClassB"]]); | |
1294 | ||
1295 | JSValue *canConstructClassC = [context evaluateScript:@"(function() { \ | |
1296 | try { \ | |
1297 | (new ClassC(1, 2)); \ | |
1298 | return true; \ | |
1299 | } catch(e) { \ | |
1300 | return false; \ | |
1301 | } \ | |
1302 | })()"]; | |
1303 | checkResult(@"shouldn't be able to construct ClassC", ![canConstructClassC toBool]); | |
81345200 A |
1304 | JSValue *canConstructClassCPrime = [context evaluateScript:@"(function() { \ |
1305 | try { \ | |
1306 | (new ClassCPrime(1)); \ | |
1307 | return true; \ | |
1308 | } catch(e) { \ | |
1309 | return false; \ | |
1310 | } \ | |
1311 | })()"]; | |
1312 | checkResult(@"shouldn't be able to construct ClassCPrime", ![canConstructClassCPrime toBool]); | |
12899fa2 A |
1313 | } |
1314 | ||
1315 | @autoreleasepool { | |
1316 | JSContext *context = [[JSContext alloc] init]; | |
1317 | context[@"ClassD"] = [ClassD class]; | |
1318 | context[@"ClassE"] = [ClassE class]; | |
1319 | ||
1320 | JSValue *d = [context evaluateScript:@"(new ClassD())"]; | |
1321 | checkResult(@"Returning instance of ClassE from ClassD's init has correct class", [d isInstanceOf:context[@"ClassE"]]); | |
1322 | } | |
1323 | ||
81345200 A |
1324 | @autoreleasepool { |
1325 | JSContext *context = [[JSContext alloc] init]; | |
1326 | while (!evilAllocationObjectWasDealloced) { | |
1327 | @autoreleasepool { | |
1328 | EvilAllocationObject *evilObject = [[EvilAllocationObject alloc] initWithContext:context]; | |
1329 | context[@"evilObject"] = evilObject; | |
1330 | context[@"evilObject"] = nil; | |
1331 | } | |
1332 | } | |
1333 | checkResult(@"EvilAllocationObject was successfully dealloced without crashing", evilAllocationObjectWasDealloced); | |
1334 | } | |
1335 | ||
1336 | @autoreleasepool { | |
1337 | JSContext *context = [[JSContext alloc] init]; | |
1338 | checkResult(@"default context.name is nil", context.name == nil); | |
1339 | NSString *name1 = @"Name1"; | |
1340 | NSString *name2 = @"Name2"; | |
1341 | context.name = name1; | |
1342 | NSString *fetchedName1 = context.name; | |
1343 | context.name = name2; | |
1344 | NSString *fetchedName2 = context.name; | |
1345 | context.name = nil; | |
1346 | NSString *fetchedName3 = context.name; | |
1347 | checkResult(@"fetched context.name was expected", [fetchedName1 isEqualToString:name1]); | |
1348 | checkResult(@"fetched context.name was expected", [fetchedName2 isEqualToString:name2]); | |
1349 | checkResult(@"fetched context.name was expected", ![fetchedName1 isEqualToString:fetchedName2]); | |
1350 | checkResult(@"fetched context.name was expected", fetchedName3 == nil); | |
12899fa2 | 1351 | } |
81345200 A |
1352 | |
1353 | @autoreleasepool { | |
1354 | JSContext *context = [[JSContext alloc] init]; | |
1355 | context[@"UnexportedObject"] = [UnexportedObject class]; | |
1356 | context[@"makeObject"] = ^{ | |
1357 | return [[UnexportedObject alloc] init]; | |
1358 | }; | |
1359 | JSValue *result = [context evaluateScript:@"(makeObject() instanceof UnexportedObject)"]; | |
ed1e77d3 | 1360 | checkResult(@"makeObject() instanceof UnexportedObject", result.isBoolean && [result toBool]); |
81345200 A |
1361 | } |
1362 | ||
1363 | @autoreleasepool { | |
1364 | JSContext *context = [[JSContext alloc] init]; | |
1365 | [[JSValue valueWithInt32:42 inContext:context] toDictionary]; | |
1366 | [[JSValue valueWithInt32:42 inContext:context] toArray]; | |
1367 | } | |
1368 | ||
1369 | @autoreleasepool { | |
1370 | JSContext *context = [[JSContext alloc] init]; | |
1371 | ||
1372 | // Create the root, make it reachable from JS, and force an EdenCollection | |
1373 | // so that we scan the external object graph. | |
1374 | TestObject *root = [TestObject testObject]; | |
1375 | @autoreleasepool { | |
1376 | context[@"root"] = root; | |
1377 | } | |
1378 | JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]); | |
1379 | ||
1380 | // Create a new Obj-C object only reachable via the external object graph | |
1381 | // through the object we already scanned during the EdenCollection. | |
1382 | TestObject *child = [TestObject testObject]; | |
1383 | [context.virtualMachine addManagedReference:child withOwner:root]; | |
1384 | ||
1385 | // Create a new managed JSValue that will only be kept alive if we properly rescan | |
1386 | // the external object graph. | |
1387 | JSManagedValue *managedJSObject = nil; | |
1388 | @autoreleasepool { | |
1389 | JSValue *jsObject = [JSValue valueWithObject:@"hello" inContext:context]; | |
1390 | managedJSObject = [JSManagedValue managedValueWithValue:jsObject]; | |
1391 | [context.virtualMachine addManagedReference:managedJSObject withOwner:child]; | |
1392 | } | |
1393 | ||
1394 | // Force another EdenCollection. It should rescan the new part of the external object graph. | |
1395 | JSSynchronousEdenCollectForDebugging([context JSGlobalContextRef]); | |
1396 | ||
1397 | // Check that the managed JSValue is still alive. | |
1398 | checkResult(@"EdenCollection doesn't reclaim new managed values", [managedJSObject value] != nil); | |
1399 | } | |
1400 | ||
40a37d08 A |
1401 | @autoreleasepool { |
1402 | JSContext *context = [[JSContext alloc] init]; | |
1403 | ||
1404 | pthread_t threadID; | |
1405 | pthread_create(&threadID, NULL, &threadMain, (__bridge void*)context); | |
1406 | pthread_join(threadID, nullptr); | |
1407 | JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]); | |
1408 | ||
1409 | checkResult(@"Did not crash after entering the VM from another thread", true); | |
1410 | } | |
1411 | ||
81345200 A |
1412 | currentThisInsideBlockGetterTest(); |
1413 | runDateTests(); | |
1414 | runJSExportTests(); | |
ed1e77d3 | 1415 | runRegress141275(); |
40a37d08 | 1416 | runRegress141809(); |
93a37866 A |
1417 | } |
1418 | ||
ed1e77d3 A |
1419 | @protocol NumberProtocol <JSExport> |
1420 | ||
1421 | @property (nonatomic) NSInteger number; | |
1422 | ||
1423 | @end | |
1424 | ||
1425 | @interface NumberObject : NSObject <NumberProtocol> | |
1426 | ||
1427 | @property (nonatomic) NSInteger number; | |
1428 | ||
1429 | @end | |
1430 | ||
1431 | @implementation NumberObject | |
1432 | ||
1433 | @end | |
1434 | ||
1435 | // Check that negative NSIntegers retain the correct value when passed into JS code. | |
1436 | static void checkNegativeNSIntegers() | |
1437 | { | |
1438 | NumberObject *container = [[NumberObject alloc] init]; | |
1439 | container.number = -1; | |
1440 | JSContext *context = [[JSContext alloc] init]; | |
1441 | context[@"container"] = container; | |
1442 | NSString *jsID = @"var getContainerNumber = function() { return container.number }"; | |
1443 | [context evaluateScript:jsID]; | |
1444 | JSValue *jsFunction = context[@"getContainerNumber"]; | |
1445 | JSValue *result = [jsFunction callWithArguments:@[]]; | |
1446 | ||
1447 | checkResult(@"Negative number maintained its original value", [[result toString] isEqualToString:@"-1"]); | |
1448 | } | |
1449 | ||
1450 | void testObjectiveCAPI() | |
1451 | { | |
1452 | NSLog(@"Testing Objective-C API"); | |
1453 | checkNegativeNSIntegers(); | |
1454 | testWeakValue(); | |
1455 | testObjectiveCAPIMain(); | |
1456 | } | |
1457 | ||
93a37866 A |
1458 | #else |
1459 | ||
1460 | void testObjectiveCAPI() | |
1461 | { | |
1462 | } | |
1463 | ||
81345200 | 1464 | #endif // JSC_OBJC_API_ENABLED |