]> git.saurik.com Git - apple/javascriptcore.git/blame - API/JSValue.mm
JavaScriptCore-7600.1.4.15.12.tar.gz
[apple/javascriptcore.git] / API / JSValue.mm
CommitLineData
93a37866
A
1/*
2 * Copyright (C) 2013 Apple 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 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#include "config.h"
27
28#import "APICast.h"
93a37866
A
29#import "DateInstance.h"
30#import "Error.h"
31#import "JavaScriptCore.h"
32#import "JSContextInternal.h"
33#import "JSVirtualMachineInternal.h"
34#import "JSValueInternal.h"
35#import "JSWrapperMap.h"
36#import "ObjcRuntimeExtras.h"
81345200 37#import "JSCInlines.h"
93a37866 38#import "JSCJSValue.h"
81345200 39#import "Strong.h"
12899fa2 40#import "StrongInlines.h"
93a37866
A
41#import <wtf/HashMap.h>
42#import <wtf/HashSet.h>
12899fa2 43#import <wtf/ObjcRuntimeExtras.h>
93a37866
A
44#import <wtf/Vector.h>
45#import <wtf/TCSpinLock.h>
46#import <wtf/text/WTFString.h>
47#import <wtf/text/StringHash.h>
48
81345200
A
49#if ENABLE(REMOTE_INSPECTOR)
50#import "CallFrame.h"
51#import "JSGlobalObject.h"
52#import "JSGlobalObjectInspectorController.h"
53#endif
54
93a37866
A
55#if JSC_OBJC_API_ENABLED
56
57NSString * const JSPropertyDescriptorWritableKey = @"writable";
58NSString * const JSPropertyDescriptorEnumerableKey = @"enumerable";
59NSString * const JSPropertyDescriptorConfigurableKey = @"configurable";
60NSString * const JSPropertyDescriptorValueKey = @"value";
61NSString * const JSPropertyDescriptorGetKey = @"get";
62NSString * const JSPropertyDescriptorSetKey = @"set";
63
64@implementation JSValue {
65 JSValueRef m_value;
66}
67
68- (JSValueRef)JSValueRef
69{
70 return m_value;
71}
72
73+ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context
74{
75 return [JSValue valueWithJSValueRef:objectToValue(context, value) inContext:context];
76}
77
78+ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context
79{
80 return [JSValue valueWithJSValueRef:JSValueMakeBoolean([context JSGlobalContextRef], value) inContext:context];
81}
82
83+ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context
84{
85 return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
86}
87
88+ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context
89{
90 return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
91}
92
93+ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context
94{
95 return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
96}
97
98+ (JSValue *)valueWithNewObjectInContext:(JSContext *)context
99{
100 return [JSValue valueWithJSValueRef:JSObjectMake([context JSGlobalContextRef], 0, 0) inContext:context];
101}
102
103+ (JSValue *)valueWithNewArrayInContext:(JSContext *)context
104{
105 return [JSValue valueWithJSValueRef:JSObjectMakeArray([context JSGlobalContextRef], 0, NULL, 0) inContext:context];
106}
107
108+ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context
109{
110 JSStringRef patternString = JSStringCreateWithCFString((CFStringRef)pattern);
111 JSStringRef flagsString = JSStringCreateWithCFString((CFStringRef)flags);
112 JSValueRef arguments[2] = { JSValueMakeString([context JSGlobalContextRef], patternString), JSValueMakeString([context JSGlobalContextRef], flagsString) };
113 JSStringRelease(patternString);
114 JSStringRelease(flagsString);
115
116 return [JSValue valueWithJSValueRef:JSObjectMakeRegExp([context JSGlobalContextRef], 2, arguments, 0) inContext:context];
117}
118
119+ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context
120{
121 JSStringRef string = JSStringCreateWithCFString((CFStringRef)message);
122 JSValueRef argument = JSValueMakeString([context JSGlobalContextRef], string);
123 JSStringRelease(string);
124
125 return [JSValue valueWithJSValueRef:JSObjectMakeError([context JSGlobalContextRef], 1, &argument, 0) inContext:context];
126}
127
128+ (JSValue *)valueWithNullInContext:(JSContext *)context
129{
130 return [JSValue valueWithJSValueRef:JSValueMakeNull([context JSGlobalContextRef]) inContext:context];
131}
132
133+ (JSValue *)valueWithUndefinedInContext:(JSContext *)context
134{
135 return [JSValue valueWithJSValueRef:JSValueMakeUndefined([context JSGlobalContextRef]) inContext:context];
136}
137
138- (id)toObject
139{
140 return valueToObject(_context, m_value);
141}
142
143- (id)toObjectOfClass:(Class)expectedClass
144{
145 id result = [self toObject];
146 return [result isKindOfClass:expectedClass] ? result : nil;
147}
148
149- (BOOL)toBool
150{
151 return JSValueToBoolean([_context JSGlobalContextRef], m_value);
152}
153
154- (double)toDouble
155{
156 JSValueRef exception = 0;
157 double result = JSValueToNumber([_context JSGlobalContextRef], m_value, &exception);
158 if (exception) {
159 [_context notifyException:exception];
160 return std::numeric_limits<double>::quiet_NaN();
161 }
162
163 return result;
164}
165
166- (int32_t)toInt32
167{
168 return JSC::toInt32([self toDouble]);
169}
170
171- (uint32_t)toUInt32
172{
173 return JSC::toUInt32([self toDouble]);
174}
175
176- (NSNumber *)toNumber
177{
178 JSValueRef exception = 0;
179 id result = valueToNumber([_context JSGlobalContextRef], m_value, &exception);
180 if (exception)
181 [_context notifyException:exception];
182 return result;
183}
184
185- (NSString *)toString
186{
187 JSValueRef exception = 0;
188 id result = valueToString([_context JSGlobalContextRef], m_value, &exception);
189 if (exception)
190 [_context notifyException:exception];
191 return result;
192}
193
194- (NSDate *)toDate
195{
196 JSValueRef exception = 0;
197 id result = valueToDate([_context JSGlobalContextRef], m_value, &exception);
198 if (exception)
199 [_context notifyException:exception];
200 return result;
201}
202
203- (NSArray *)toArray
204{
205 JSValueRef exception = 0;
206 id result = valueToArray([_context JSGlobalContextRef], m_value, &exception);
207 if (exception)
208 [_context notifyException:exception];
209 return result;
210}
211
212- (NSDictionary *)toDictionary
213{
214 JSValueRef exception = 0;
215 id result = valueToDictionary([_context JSGlobalContextRef], m_value, &exception);
216 if (exception)
217 [_context notifyException:exception];
218 return result;
219}
220
221- (JSValue *)valueForProperty:(NSString *)propertyName
222{
223 JSValueRef exception = 0;
224 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
225 if (exception)
226 return [_context valueFromNotifyException:exception];
227
228 JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
229 JSValueRef result = JSObjectGetProperty([_context JSGlobalContextRef], object, name, &exception);
230 JSStringRelease(name);
231 if (exception)
232 return [_context valueFromNotifyException:exception];
233
234 return [JSValue valueWithJSValueRef:result inContext:_context];
235}
236
237- (void)setValue:(id)value forProperty:(NSString *)propertyName
238{
239 JSValueRef exception = 0;
240 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
241 if (exception) {
242 [_context notifyException:exception];
243 return;
244 }
245
246 JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
247 JSObjectSetProperty([_context JSGlobalContextRef], object, name, objectToValue(_context, value), 0, &exception);
248 JSStringRelease(name);
249 if (exception) {
250 [_context notifyException:exception];
251 return;
252 }
253}
254
255- (BOOL)deleteProperty:(NSString *)propertyName
256{
257 JSValueRef exception = 0;
258 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
259 if (exception)
260 return [_context boolFromNotifyException:exception];
261
262 JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
263 BOOL result = JSObjectDeleteProperty([_context JSGlobalContextRef], object, name, &exception);
264 JSStringRelease(name);
265 if (exception)
266 return [_context boolFromNotifyException:exception];
267
268 return result;
269}
270
271- (BOOL)hasProperty:(NSString *)propertyName
272{
273 JSValueRef exception = 0;
274 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
275 if (exception)
276 return [_context boolFromNotifyException:exception];
277
278 JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
279 BOOL result = JSObjectHasProperty([_context JSGlobalContextRef], object, name);
280 JSStringRelease(name);
281 return result;
282}
283
284- (void)defineProperty:(NSString *)property descriptor:(id)descriptor
285{
286 [[_context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, property, descriptor ]];
287}
288
289- (JSValue *)valueAtIndex:(NSUInteger)index
290{
291 // Properties that are higher than an unsigned value can hold are converted to a double then inserted as a normal property.
292 // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in get().
293 if (index != (unsigned)index)
294 return [self valueForProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
295
296 JSValueRef exception = 0;
297 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
298 if (exception)
299 return [_context valueFromNotifyException:exception];
300
301 JSValueRef result = JSObjectGetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, &exception);
302 if (exception)
303 return [_context valueFromNotifyException:exception];
304
305 return [JSValue valueWithJSValueRef:result inContext:_context];
306}
307
308- (void)setValue:(id)value atIndex:(NSUInteger)index
309{
310 // Properties that are higher than an unsigned value can hold are converted to a double, then inserted as a normal property.
311 // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in putByIndex().
312 if (index != (unsigned)index)
313 return [self setValue:value forProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
314
315 JSValueRef exception = 0;
316 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
317 if (exception) {
318 [_context notifyException:exception];
319 return;
320 }
321
322 JSObjectSetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, objectToValue(_context, value), &exception);
323 if (exception) {
324 [_context notifyException:exception];
325 return;
326 }
327}
328
329- (BOOL)isUndefined
330{
331 return JSValueIsUndefined([_context JSGlobalContextRef], m_value);
332}
333
334- (BOOL)isNull
335{
336 return JSValueIsNull([_context JSGlobalContextRef], m_value);
337}
338
339- (BOOL)isBoolean
340{
341 return JSValueIsBoolean([_context JSGlobalContextRef], m_value);
342}
343
344- (BOOL)isNumber
345{
346 return JSValueIsNumber([_context JSGlobalContextRef], m_value);
347}
348
349- (BOOL)isString
350{
351 return JSValueIsString([_context JSGlobalContextRef], m_value);
352}
353
354- (BOOL)isObject
355{
356 return JSValueIsObject([_context JSGlobalContextRef], m_value);
357}
358
359- (BOOL)isEqualToObject:(id)value
360{
361 return JSValueIsStrictEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value));
362}
363
364- (BOOL)isEqualWithTypeCoercionToObject:(id)value
365{
366 JSValueRef exception = 0;
367 BOOL result = JSValueIsEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value), &exception);
368 if (exception)
369 return [_context boolFromNotifyException:exception];
370
371 return result;
372}
373
374- (BOOL)isInstanceOf:(id)value
375{
376 JSValueRef exception = 0;
377 JSObjectRef constructor = JSValueToObject([_context JSGlobalContextRef], objectToValue(_context, value), &exception);
378 if (exception)
379 return [_context boolFromNotifyException:exception];
380
381 BOOL result = JSValueIsInstanceOfConstructor([_context JSGlobalContextRef], m_value, constructor, &exception);
382 if (exception)
383 return [_context boolFromNotifyException:exception];
384
385 return result;
386}
387
388- (JSValue *)callWithArguments:(NSArray *)argumentArray
389{
390 NSUInteger argumentCount = [argumentArray count];
391 JSValueRef arguments[argumentCount];
392 for (unsigned i = 0; i < argumentCount; ++i)
393 arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
394
395 JSValueRef exception = 0;
396 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
397 if (exception)
398 return [_context valueFromNotifyException:exception];
399
400 JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, 0, argumentCount, arguments, &exception);
401 if (exception)
402 return [_context valueFromNotifyException:exception];
403
404 return [JSValue valueWithJSValueRef:result inContext:_context];
405}
406
407- (JSValue *)constructWithArguments:(NSArray *)argumentArray
408{
409 NSUInteger argumentCount = [argumentArray count];
410 JSValueRef arguments[argumentCount];
411 for (unsigned i = 0; i < argumentCount; ++i)
412 arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
413
414 JSValueRef exception = 0;
415 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
416 if (exception)
417 return [_context valueFromNotifyException:exception];
418
419 JSObjectRef result = JSObjectCallAsConstructor([_context JSGlobalContextRef], object, argumentCount, arguments, &exception);
420 if (exception)
421 return [_context valueFromNotifyException:exception];
422
423 return [JSValue valueWithJSValueRef:result inContext:_context];
424}
425
426- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments
427{
428 NSUInteger argumentCount = [arguments count];
429 JSValueRef argumentArray[argumentCount];
430 for (unsigned i = 0; i < argumentCount; ++i)
431 argumentArray[i] = objectToValue(_context, [arguments objectAtIndex:i]);
432
433 JSValueRef exception = 0;
434 JSObjectRef thisObject = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
435 if (exception)
436 return [_context valueFromNotifyException:exception];
437
438 JSStringRef name = JSStringCreateWithCFString((CFStringRef)method);
439 JSValueRef function = JSObjectGetProperty([_context JSGlobalContextRef], thisObject, name, &exception);
440 JSStringRelease(name);
441 if (exception)
442 return [_context valueFromNotifyException:exception];
443
444 JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], function, &exception);
445 if (exception)
446 return [_context valueFromNotifyException:exception];
447
448 JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, thisObject, argumentCount, argumentArray, &exception);
449 if (exception)
450 return [_context valueFromNotifyException:exception];
451
452 return [JSValue valueWithJSValueRef:result inContext:_context];
453}
454
455@end
456
457@implementation JSValue(StructSupport)
458
459- (CGPoint)toPoint
460{
461 return (CGPoint){
462 static_cast<CGFloat>([self[@"x"] toDouble]),
463 static_cast<CGFloat>([self[@"y"] toDouble])
464 };
465}
466
467- (NSRange)toRange
468{
469 return (NSRange){
470 [[self[@"location"] toNumber] unsignedIntegerValue],
471 [[self[@"length"] toNumber] unsignedIntegerValue]
472 };
473}
474
475- (CGRect)toRect
476{
477 return (CGRect){
478 [self toPoint],
479 [self toSize]
480 };
481}
482
483- (CGSize)toSize
484{
485 return (CGSize){
486 static_cast<CGFloat>([self[@"width"] toDouble]),
487 static_cast<CGFloat>([self[@"height"] toDouble])
488 };
489}
490
491+ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context
492{
493 return [JSValue valueWithObject:@{
494 @"x":@(point.x),
495 @"y":@(point.y)
496 } inContext:context];
497}
498
499+ (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context
500{
501 return [JSValue valueWithObject:@{
502 @"location":@(range.location),
503 @"length":@(range.length)
504 } inContext:context];
505}
506
507+ (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context
508{
509 return [JSValue valueWithObject:@{
510 @"x":@(rect.origin.x),
511 @"y":@(rect.origin.y),
512 @"width":@(rect.size.width),
513 @"height":@(rect.size.height)
514 } inContext:context];
515}
516
517+ (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context
518{
519 return [JSValue valueWithObject:@{
520 @"width":@(size.width),
521 @"height":@(size.height)
522 } inContext:context];
523}
524
525@end
526
527@implementation JSValue(SubscriptSupport)
528
529- (JSValue *)objectForKeyedSubscript:(id)key
530{
531 if (![key isKindOfClass:[NSString class]]) {
532 key = [[JSValue valueWithObject:key inContext:_context] toString];
533 if (!key)
534 return [JSValue valueWithUndefinedInContext:_context];
535 }
536
537 return [self valueForProperty:(NSString *)key];
538}
539
540- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index
541{
542 return [self valueAtIndex:index];
543}
544
545- (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key
546{
547 if (![key isKindOfClass:[NSString class]]) {
548 key = [[JSValue valueWithObject:key inContext:_context] toString];
549 if (!key)
550 return;
551 }
552
553 [self setValue:object forProperty:(NSString *)key];
554}
555
556- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index
557{
558 [self setValue:object atIndex:index];
559}
560
561@end
562
563inline bool isDate(JSObjectRef object, JSGlobalContextRef context)
564{
81345200
A
565 JSC::JSLockHolder locker(toJS(context));
566 return toJS(object)->inherits(JSC::DateInstance::info());
93a37866
A
567}
568
569inline bool isArray(JSObjectRef object, JSGlobalContextRef context)
570{
81345200
A
571 JSC::JSLockHolder locker(toJS(context));
572 return toJS(object)->inherits(JSC::JSArray::info());
93a37866
A
573}
574
575@implementation JSValue(Internal)
576
577enum ConversionType {
578 ContainerNone,
579 ContainerArray,
580 ContainerDictionary
581};
582
583class JSContainerConvertor {
584public:
585 struct Task {
586 JSValueRef js;
587 id objc;
588 ConversionType type;
589 };
590
591 JSContainerConvertor(JSGlobalContextRef context)
592 : m_context(context)
593 {
594 }
595
596 id convert(JSValueRef property);
597 void add(Task);
598 Task take();
599 bool isWorkListEmpty() const { return !m_worklist.size(); }
600
601private:
602 JSGlobalContextRef m_context;
603 HashMap<JSValueRef, id> m_objectMap;
604 Vector<Task> m_worklist;
12899fa2 605 Vector<JSC::Strong<JSC::Unknown>> m_jsValues;
93a37866
A
606};
607
608inline id JSContainerConvertor::convert(JSValueRef value)
609{
610 HashMap<JSValueRef, id>::iterator iter = m_objectMap.find(value);
611 if (iter != m_objectMap.end())
612 return iter->value;
613
614 Task result = valueToObjectWithoutCopy(m_context, value);
615 if (result.js)
616 add(result);
617 return result.objc;
618}
619
620void JSContainerConvertor::add(Task task)
621{
12899fa2
A
622 JSC::ExecState* exec = toJS(m_context);
623 m_jsValues.append(JSC::Strong<JSC::Unknown>(exec->vm(), toJSForGC(exec, task.js)));
93a37866
A
624 m_objectMap.add(task.js, task.objc);
625 if (task.type != ContainerNone)
626 m_worklist.append(task);
627}
628
629JSContainerConvertor::Task JSContainerConvertor::take()
630{
631 ASSERT(!isWorkListEmpty());
632 Task last = m_worklist.last();
633 m_worklist.removeLast();
634 return last;
635}
636
81345200
A
637#if ENABLE(REMOTE_INSPECTOR)
638static void reportExceptionToInspector(JSGlobalContextRef context, JSC::JSValue exception)
639{
640 JSC::ExecState* exec = toJS(context);
641 exec->vmEntryGlobalObject()->inspectorController().reportAPIException(exec, exception);
642}
643#endif
644
93a37866
A
645static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef context, JSValueRef value)
646{
647 if (!JSValueIsObject(context, value)) {
648 id primitive;
649 if (JSValueIsBoolean(context, value))
650 primitive = JSValueToBoolean(context, value) ? @YES : @NO;
651 else if (JSValueIsNumber(context, value)) {
652 // Normalize the number, so it will unique correctly in the hash map -
653 // it's nicer not to leak this internal implementation detail!
654 value = JSValueMakeNumber(context, JSValueToNumber(context, value, 0));
655 primitive = [NSNumber numberWithDouble:JSValueToNumber(context, value, 0)];
656 } else if (JSValueIsString(context, value)) {
657 // Would be nice to unique strings, too.
658 JSStringRef jsstring = JSValueToStringCopy(context, value, 0);
659 NSString * stringNS = (NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring);
660 JSStringRelease(jsstring);
661 primitive = [stringNS autorelease];
662 } else if (JSValueIsNull(context, value))
663 primitive = [NSNull null];
664 else {
665 ASSERT(JSValueIsUndefined(context, value));
666 primitive = nil;
667 }
668 return (JSContainerConvertor::Task){ value, primitive, ContainerNone };
669 }
670
671 JSObjectRef object = JSValueToObject(context, value, 0);
672
673 if (id wrapped = tryUnwrapObjcObject(context, object))
674 return (JSContainerConvertor::Task){ object, wrapped, ContainerNone };
675
676 if (isDate(object, context))
81345200 677 return (JSContainerConvertor::Task){ object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0) / 1000.0], ContainerNone };
93a37866
A
678
679 if (isArray(object, context))
680 return (JSContainerConvertor::Task){ object, [NSMutableArray array], ContainerArray };
681
682 return (JSContainerConvertor::Task){ object, [NSMutableDictionary dictionary], ContainerDictionary };
683}
684
685static id containerValueToObject(JSGlobalContextRef context, JSContainerConvertor::Task task)
686{
687 ASSERT(task.type != ContainerNone);
81345200 688 JSC::JSLockHolder locker(toJS(context));
93a37866
A
689 JSContainerConvertor convertor(context);
690 convertor.add(task);
691 ASSERT(!convertor.isWorkListEmpty());
692
693 do {
694 JSContainerConvertor::Task current = convertor.take();
695 ASSERT(JSValueIsObject(context, current.js));
696 JSObjectRef js = JSValueToObject(context, current.js, 0);
697
698 if (current.type == ContainerArray) {
699 ASSERT([current.objc isKindOfClass:[NSMutableArray class]]);
700 NSMutableArray *array = (NSMutableArray *)current.objc;
701
702 JSStringRef lengthString = JSStringCreateWithUTF8CString("length");
703 unsigned length = JSC::toUInt32(JSValueToNumber(context, JSObjectGetProperty(context, js, lengthString, 0), 0));
704 JSStringRelease(lengthString);
705
706 for (unsigned i = 0; i < length; ++i) {
707 id objc = convertor.convert(JSObjectGetPropertyAtIndex(context, js, i, 0));
708 [array addObject:objc ? objc : [NSNull null]];
709 }
710 } else {
711 ASSERT([current.objc isKindOfClass:[NSMutableDictionary class]]);
712 NSMutableDictionary *dictionary = (NSMutableDictionary *)current.objc;
713
81345200
A
714 JSC::JSLockHolder locker(toJS(context));
715
93a37866
A
716 JSPropertyNameArrayRef propertyNameArray = JSObjectCopyPropertyNames(context, js);
717 size_t length = JSPropertyNameArrayGetCount(propertyNameArray);
718
719 for (size_t i = 0; i < length; ++i) {
720 JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNameArray, i);
721 if (id objc = convertor.convert(JSObjectGetProperty(context, js, propertyName, 0)))
722 dictionary[[(NSString *)JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]] = objc;
723 }
724
725 JSPropertyNameArrayRelease(propertyNameArray);
726 }
727
728 } while (!convertor.isWorkListEmpty());
729
730 return task.objc;
731}
732
733id valueToObject(JSContext *context, JSValueRef value)
734{
735 JSContainerConvertor::Task result = valueToObjectWithoutCopy([context JSGlobalContextRef], value);
736 if (result.type == ContainerNone)
737 return result.objc;
738 return containerValueToObject([context JSGlobalContextRef], result);
739}
740
741id valueToNumber(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
742{
743 ASSERT(!*exception);
744 if (id wrapped = tryUnwrapObjcObject(context, value)) {
745 if ([wrapped isKindOfClass:[NSNumber class]])
746 return wrapped;
747 }
748
749 if (JSValueIsBoolean(context, value))
750 return JSValueToBoolean(context, value) ? @YES : @NO;
751
752 double result = JSValueToNumber(context, value, exception);
753 return [NSNumber numberWithDouble:*exception ? std::numeric_limits<double>::quiet_NaN() : result];
754}
755
756id valueToString(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
757{
758 ASSERT(!*exception);
759 if (id wrapped = tryUnwrapObjcObject(context, value)) {
760 if ([wrapped isKindOfClass:[NSString class]])
761 return wrapped;
762 }
763
764 JSStringRef jsstring = JSValueToStringCopy(context, value, exception);
765 if (*exception) {
766 ASSERT(!jsstring);
767 return nil;
768 }
769
81345200 770 NSString *stringNS = CFBridgingRelease(JSStringCopyCFString(kCFAllocatorDefault, jsstring));
93a37866
A
771 JSStringRelease(jsstring);
772 return stringNS;
773}
774
775id valueToDate(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
776{
777 ASSERT(!*exception);
778 if (id wrapped = tryUnwrapObjcObject(context, value)) {
779 if ([wrapped isKindOfClass:[NSDate class]])
780 return wrapped;
781 }
782
81345200 783 double result = JSValueToNumber(context, value, exception) / 1000.0;
93a37866
A
784 return *exception ? nil : [NSDate dateWithTimeIntervalSince1970:result];
785}
786
787id valueToArray(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
788{
789 ASSERT(!*exception);
790 if (id wrapped = tryUnwrapObjcObject(context, value)) {
791 if ([wrapped isKindOfClass:[NSArray class]])
792 return wrapped;
793 }
794
795 if (JSValueIsObject(context, value))
796 return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableArray array], ContainerArray});
797
81345200
A
798 JSC::JSLockHolder locker(toJS(context));
799 if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value))) {
800 JSC::JSObject* exceptionObject = JSC::createTypeError(toJS(context), ASCIILiteral("Cannot convert primitive to NSArray"));
801 *exception = toRef(exceptionObject);
802#if ENABLE(REMOTE_INSPECTOR)
803 reportExceptionToInspector(context, exceptionObject);
804#endif
805 }
93a37866
A
806 return nil;
807}
808
809id valueToDictionary(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
810{
811 ASSERT(!*exception);
812 if (id wrapped = tryUnwrapObjcObject(context, value)) {
813 if ([wrapped isKindOfClass:[NSDictionary class]])
814 return wrapped;
815 }
816
817 if (JSValueIsObject(context, value))
818 return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableDictionary dictionary], ContainerDictionary});
819
81345200
A
820 JSC::JSLockHolder locker(toJS(context));
821 if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value))) {
822 JSC::JSObject* exceptionObject = JSC::createTypeError(toJS(context), ASCIILiteral("Cannot convert primitive to NSDictionary"));
823 *exception = toRef(exceptionObject);
824#if ENABLE(REMOTE_INSPECTOR)
825 reportExceptionToInspector(context, exceptionObject);
826#endif
827 }
93a37866
A
828 return nil;
829}
830
831class ObjcContainerConvertor {
832public:
833 struct Task {
834 id objc;
835 JSValueRef js;
836 ConversionType type;
837 };
838
839 ObjcContainerConvertor(JSContext *context)
840 : m_context(context)
841 {
842 }
843
844 JSValueRef convert(id object);
845 void add(Task);
846 Task take();
847 bool isWorkListEmpty() const { return !m_worklist.size(); }
848
849private:
850 JSContext *m_context;
851 HashMap<id, JSValueRef> m_objectMap;
852 Vector<Task> m_worklist;
12899fa2 853 Vector<JSC::Strong<JSC::Unknown>> m_jsValues;
93a37866
A
854};
855
856JSValueRef ObjcContainerConvertor::convert(id object)
857{
858 ASSERT(object);
859
860 auto it = m_objectMap.find(object);
861 if (it != m_objectMap.end())
862 return it->value;
863
864 ObjcContainerConvertor::Task task = objectToValueWithoutCopy(m_context, object);
865 add(task);
866 return task.js;
867}
868
869void ObjcContainerConvertor::add(ObjcContainerConvertor::Task task)
870{
12899fa2
A
871 JSC::ExecState* exec = toJS(m_context.JSGlobalContextRef);
872 m_jsValues.append(JSC::Strong<JSC::Unknown>(exec->vm(), toJSForGC(exec, task.js)));
93a37866
A
873 m_objectMap.add(task.objc, task.js);
874 if (task.type != ContainerNone)
875 m_worklist.append(task);
876}
877
878ObjcContainerConvertor::Task ObjcContainerConvertor::take()
879{
880 ASSERT(!isWorkListEmpty());
881 Task last = m_worklist.last();
882 m_worklist.removeLast();
883 return last;
884}
885
886inline bool isNSBoolean(id object)
887{
888 ASSERT([@YES class] == [@NO class]);
889 ASSERT([@YES class] != [NSNumber class]);
890 ASSERT([[@YES class] isSubclassOfClass:[NSNumber class]]);
891 return [object isKindOfClass:[@YES class]];
892}
893
894static ObjcContainerConvertor::Task objectToValueWithoutCopy(JSContext *context, id object)
895{
896 JSGlobalContextRef contextRef = [context JSGlobalContextRef];
897
898 if (!object)
899 return (ObjcContainerConvertor::Task){ object, JSValueMakeUndefined(contextRef), ContainerNone };
900
901 if (!class_conformsToProtocol(object_getClass(object), getJSExportProtocol())) {
902 if ([object isKindOfClass:[NSArray class]])
903 return (ObjcContainerConvertor::Task){ object, JSObjectMakeArray(contextRef, 0, NULL, 0), ContainerArray };
904
905 if ([object isKindOfClass:[NSDictionary class]])
906 return (ObjcContainerConvertor::Task){ object, JSObjectMake(contextRef, 0, 0), ContainerDictionary };
907
908 if ([object isKindOfClass:[NSNull class]])
909 return (ObjcContainerConvertor::Task){ object, JSValueMakeNull(contextRef), ContainerNone };
910
911 if ([object isKindOfClass:[JSValue class]])
912 return (ObjcContainerConvertor::Task){ object, ((JSValue *)object)->m_value, ContainerNone };
913
914 if ([object isKindOfClass:[NSString class]]) {
915 JSStringRef string = JSStringCreateWithCFString((CFStringRef)object);
916 JSValueRef js = JSValueMakeString(contextRef, string);
917 JSStringRelease(string);
918 return (ObjcContainerConvertor::Task){ object, js, ContainerNone };
919 }
920
921 if ([object isKindOfClass:[NSNumber class]]) {
922 if (isNSBoolean(object))
923 return (ObjcContainerConvertor::Task){ object, JSValueMakeBoolean(contextRef, [object boolValue]), ContainerNone };
924 return (ObjcContainerConvertor::Task){ object, JSValueMakeNumber(contextRef, [object doubleValue]), ContainerNone };
925 }
926
927 if ([object isKindOfClass:[NSDate class]]) {
81345200 928 JSValueRef argument = JSValueMakeNumber(contextRef, [object timeIntervalSince1970] * 1000.0);
93a37866
A
929 JSObjectRef result = JSObjectMakeDate(contextRef, 1, &argument, 0);
930 return (ObjcContainerConvertor::Task){ object, result, ContainerNone };
931 }
932
933 if ([object isKindOfClass:[JSManagedValue class]]) {
934 JSValue *value = [static_cast<JSManagedValue *>(object) value];
935 if (!value)
936 return (ObjcContainerConvertor::Task) { object, JSValueMakeUndefined(contextRef), ContainerNone };
937 return (ObjcContainerConvertor::Task){ object, value->m_value, ContainerNone };
938 }
939 }
940
941 return (ObjcContainerConvertor::Task){ object, valueInternalValue([context wrapperForObjCObject:object]), ContainerNone };
942}
943
944JSValueRef objectToValue(JSContext *context, id object)
945{
946 JSGlobalContextRef contextRef = [context JSGlobalContextRef];
947
948 ObjcContainerConvertor::Task task = objectToValueWithoutCopy(context, object);
949 if (task.type == ContainerNone)
950 return task.js;
951
81345200 952 JSC::JSLockHolder locker(toJS(contextRef));
93a37866
A
953 ObjcContainerConvertor convertor(context);
954 convertor.add(task);
955 ASSERT(!convertor.isWorkListEmpty());
956
957 do {
958 ObjcContainerConvertor::Task current = convertor.take();
959 ASSERT(JSValueIsObject(contextRef, current.js));
960 JSObjectRef js = JSValueToObject(contextRef, current.js, 0);
961
962 if (current.type == ContainerArray) {
963 ASSERT([current.objc isKindOfClass:[NSArray class]]);
964 NSArray *array = (NSArray *)current.objc;
965 NSUInteger count = [array count];
966 for (NSUInteger index = 0; index < count; ++index)
967 JSObjectSetPropertyAtIndex(contextRef, js, index, convertor.convert([array objectAtIndex:index]), 0);
968 } else {
969 ASSERT(current.type == ContainerDictionary);
970 ASSERT([current.objc isKindOfClass:[NSDictionary class]]);
971 NSDictionary *dictionary = (NSDictionary *)current.objc;
972 for (id key in [dictionary keyEnumerator]) {
973 if ([key isKindOfClass:[NSString class]]) {
974 JSStringRef propertyName = JSStringCreateWithCFString((CFStringRef)key);
975 JSObjectSetProperty(contextRef, js, propertyName, convertor.convert([dictionary objectForKey:key]), 0, 0);
976 JSStringRelease(propertyName);
977 }
978 }
979 }
980
981 } while (!convertor.isWorkListEmpty());
982
983 return task.js;
984}
985
986JSValueRef valueInternalValue(JSValue * value)
987{
988 return value->m_value;
989}
990
991+ (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context
992{
993 return [context wrapperForJSObject:value];
994}
995
996- (JSValue *)init
997{
998 return nil;
999}
1000
1001- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context
1002{
1003 if (!value || !context)
1004 return nil;
1005
1006 self = [super init];
1007 if (!self)
1008 return nil;
1009
1010 _context = [context retain];
1011 m_value = value;
1012 JSValueProtect([_context JSGlobalContextRef], m_value);
1013 return self;
1014}
1015
1016struct StructTagHandler {
1017 SEL typeToValueSEL;
1018 SEL valueToTypeSEL;
1019};
1020typedef HashMap<String, StructTagHandler> StructHandlers;
1021
1022static StructHandlers* createStructHandlerMap()
1023{
1024 StructHandlers* structHandlers = new StructHandlers();
1025
1026 size_t valueWithXinContextLength = strlen("valueWithX:inContext:");
1027 size_t toXLength = strlen("toX");
1028
1029 // Step 1: find all valueWith<Foo>:inContext: class methods in JSValue.
1030 forEachMethodInClass(object_getClass([JSValue class]), ^(Method method){
1031 SEL selector = method_getName(method);
1032 const char* name = sel_getName(selector);
1033 size_t nameLength = strlen(name);
1034 // Check for valueWith<Foo>:context:
1035 if (nameLength < valueWithXinContextLength || memcmp(name, "valueWith", 9) || memcmp(name + nameLength - 11, ":inContext:", 11))
1036 return;
1037 // Check for [ id, SEL, <type>, <contextType> ]
1038 if (method_getNumberOfArguments(method) != 4)
1039 return;
1040 char idType[3];
1041 // Check 2nd argument type is "@"
1042 char* secondType = method_copyArgumentType(method, 3);
1043 if (strcmp(secondType, "@") != 0) {
1044 free(secondType);
1045 return;
1046 }
1047 free(secondType);
1048 // Check result type is also "@"
1049 method_getReturnType(method, idType, 3);
1050 if (strcmp(idType, "@") != 0)
1051 return;
1052 char* type = method_copyArgumentType(method, 2);
1053 structHandlers->add(StringImpl::create(type), (StructTagHandler){ selector, 0 });
1054 free(type);
1055 });
1056
1057 // Step 2: find all to<Foo> instance methods in JSValue.
1058 forEachMethodInClass([JSValue class], ^(Method method){
1059 SEL selector = method_getName(method);
1060 const char* name = sel_getName(selector);
1061 size_t nameLength = strlen(name);
1062 // Check for to<Foo>
1063 if (nameLength < toXLength || memcmp(name, "to", 2))
1064 return;
1065 // Check for [ id, SEL ]
1066 if (method_getNumberOfArguments(method) != 2)
1067 return;
1068 // Try to find a matching valueWith<Foo>:context: method.
1069 char* type = method_copyReturnType(method);
1070
1071 StructHandlers::iterator iter = structHandlers->find(type);
1072 free(type);
1073 if (iter == structHandlers->end())
1074 return;
1075 StructTagHandler& handler = iter->value;
1076
1077 // check that strlen(<foo>) == strlen(<Foo>)
1078 const char* valueWithName = sel_getName(handler.typeToValueSEL);
1079 size_t valueWithLength = strlen(valueWithName);
1080 if (valueWithLength - valueWithXinContextLength != nameLength - toXLength)
1081 return;
1082 // Check that <Foo> == <Foo>
1083 if (memcmp(valueWithName + 9, name + 2, nameLength - toXLength - 1))
1084 return;
1085 handler.valueToTypeSEL = selector;
1086 });
1087
1088 // Step 3: clean up - remove entries where we found prospective valueWith<Foo>:inContext: conversions, but no matching to<Foo> methods.
1089 typedef HashSet<String> RemoveSet;
1090 RemoveSet removeSet;
1091 for (StructHandlers::iterator iter = structHandlers->begin(); iter != structHandlers->end(); ++iter) {
1092 StructTagHandler& handler = iter->value;
1093 if (!handler.valueToTypeSEL)
1094 removeSet.add(iter->key);
1095 }
1096
1097 for (RemoveSet::iterator iter = removeSet.begin(); iter != removeSet.end(); ++iter)
1098 structHandlers->remove(*iter);
1099
1100 return structHandlers;
1101}
1102
1103static StructTagHandler* handerForStructTag(const char* encodedType)
1104{
1105 static SpinLock handerForStructTagLock = SPINLOCK_INITIALIZER;
1106 SpinLockHolder lockHolder(&handerForStructTagLock);
1107
1108 static StructHandlers* structHandlers = createStructHandlerMap();
1109
1110 StructHandlers::iterator iter = structHandlers->find(encodedType);
1111 if (iter == structHandlers->end())
1112 return 0;
1113 return &iter->value;
1114}
1115
1116+ (SEL)selectorForStructToValue:(const char *)structTag
1117{
1118 StructTagHandler* handler = handerForStructTag(structTag);
1119 return handler ? handler->typeToValueSEL : nil;
1120}
1121
1122+ (SEL)selectorForValueToStruct:(const char *)structTag
1123{
1124 StructTagHandler* handler = handerForStructTag(structTag);
1125 return handler ? handler->valueToTypeSEL : nil;
1126}
1127
1128- (void)dealloc
1129{
1130 JSValueUnprotect([_context JSGlobalContextRef], m_value);
1131 [_context release];
1132 _context = nil;
1133 [super dealloc];
1134}
1135
1136- (NSString *)description
1137{
1138 if (id wrapped = tryUnwrapObjcObject([_context JSGlobalContextRef], m_value))
1139 return [wrapped description];
1140 return [self toString];
1141}
1142
1143NSInvocation *typeToValueInvocationFor(const char* encodedType)
1144{
1145 SEL selector = [JSValue selectorForStructToValue:encodedType];
1146 if (!selector)
1147 return 0;
1148
1149 const char* methodTypes = method_getTypeEncoding(class_getClassMethod([JSValue class], selector));
1150 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
1151 [invocation setSelector:selector];
1152 return invocation;
1153}
1154
1155NSInvocation *valueToTypeInvocationFor(const char* encodedType)
1156{
1157 SEL selector = [JSValue selectorForValueToStruct:encodedType];
1158 if (!selector)
1159 return 0;
1160
1161 const char* methodTypes = method_getTypeEncoding(class_getInstanceMethod([JSValue class], selector));
1162 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
1163 [invocation setSelector:selector];
1164 return invocation;
1165}
1166
1167@end
1168
1169#endif