summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/API/JSValue.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/API/JSValue.mm')
-rw-r--r--Source/JavaScriptCore/API/JSValue.mm1131
1 files changed, 0 insertions, 1131 deletions
diff --git a/Source/JavaScriptCore/API/JSValue.mm b/Source/JavaScriptCore/API/JSValue.mm
deleted file mode 100644
index e708cc674..000000000
--- a/Source/JavaScriptCore/API/JSValue.mm
+++ /dev/null
@@ -1,1131 +0,0 @@
-/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#import "APICast.h"
-#import "APIShims.h"
-#import "DateInstance.h"
-#import "Error.h"
-#import "JavaScriptCore.h"
-#import "JSContextInternal.h"
-#import "JSVirtualMachineInternal.h"
-#import "JSValueInternal.h"
-#import "JSWrapperMap.h"
-#import "ObjcRuntimeExtras.h"
-#import "Operations.h"
-#import "JSCJSValue.h"
-#import <wtf/HashMap.h>
-#import <wtf/HashSet.h>
-#import <wtf/Vector.h>
-#import <wtf/TCSpinLock.h>
-#import <wtf/text/WTFString.h>
-#import <wtf/text/StringHash.h>
-
-#if JSC_OBJC_API_ENABLED
-
-NSString * const JSPropertyDescriptorWritableKey = @"writable";
-NSString * const JSPropertyDescriptorEnumerableKey = @"enumerable";
-NSString * const JSPropertyDescriptorConfigurableKey = @"configurable";
-NSString * const JSPropertyDescriptorValueKey = @"value";
-NSString * const JSPropertyDescriptorGetKey = @"get";
-NSString * const JSPropertyDescriptorSetKey = @"set";
-
-@implementation JSValue {
- JSValueRef m_value;
-}
-
-- (JSValueRef)JSValueRef
-{
- return m_value;
-}
-
-+ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:objectToValue(context, value) inContext:context];
-}
-
-+ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:JSValueMakeBoolean([context JSGlobalContextRef], value) inContext:context];
-}
-
-+ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
-}
-
-+ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
-}
-
-+ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
-}
-
-+ (JSValue *)valueWithNewObjectInContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:JSObjectMake([context JSGlobalContextRef], 0, 0) inContext:context];
-}
-
-+ (JSValue *)valueWithNewArrayInContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:JSObjectMakeArray([context JSGlobalContextRef], 0, NULL, 0) inContext:context];
-}
-
-+ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context
-{
- JSStringRef patternString = JSStringCreateWithCFString((CFStringRef)pattern);
- JSStringRef flagsString = JSStringCreateWithCFString((CFStringRef)flags);
- JSValueRef arguments[2] = { JSValueMakeString([context JSGlobalContextRef], patternString), JSValueMakeString([context JSGlobalContextRef], flagsString) };
- JSStringRelease(patternString);
- JSStringRelease(flagsString);
-
- return [JSValue valueWithJSValueRef:JSObjectMakeRegExp([context JSGlobalContextRef], 2, arguments, 0) inContext:context];
-}
-
-+ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context
-{
- JSStringRef string = JSStringCreateWithCFString((CFStringRef)message);
- JSValueRef argument = JSValueMakeString([context JSGlobalContextRef], string);
- JSStringRelease(string);
-
- return [JSValue valueWithJSValueRef:JSObjectMakeError([context JSGlobalContextRef], 1, &argument, 0) inContext:context];
-}
-
-+ (JSValue *)valueWithNullInContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:JSValueMakeNull([context JSGlobalContextRef]) inContext:context];
-}
-
-+ (JSValue *)valueWithUndefinedInContext:(JSContext *)context
-{
- return [JSValue valueWithJSValueRef:JSValueMakeUndefined([context JSGlobalContextRef]) inContext:context];
-}
-
-- (id)toObject
-{
- return valueToObject(_context, m_value);
-}
-
-- (id)toObjectOfClass:(Class)expectedClass
-{
- id result = [self toObject];
- return [result isKindOfClass:expectedClass] ? result : nil;
-}
-
-- (BOOL)toBool
-{
- return JSValueToBoolean([_context JSGlobalContextRef], m_value);
-}
-
-- (double)toDouble
-{
- JSValueRef exception = 0;
- double result = JSValueToNumber([_context JSGlobalContextRef], m_value, &exception);
- if (exception) {
- [_context notifyException:exception];
- return std::numeric_limits<double>::quiet_NaN();
- }
-
- return result;
-}
-
-- (int32_t)toInt32
-{
- return JSC::toInt32([self toDouble]);
-}
-
-- (uint32_t)toUInt32
-{
- return JSC::toUInt32([self toDouble]);
-}
-
-- (NSNumber *)toNumber
-{
- JSValueRef exception = 0;
- id result = valueToNumber([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- [_context notifyException:exception];
- return result;
-}
-
-- (NSString *)toString
-{
- JSValueRef exception = 0;
- id result = valueToString([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- [_context notifyException:exception];
- return result;
-}
-
-- (NSDate *)toDate
-{
- JSValueRef exception = 0;
- id result = valueToDate([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- [_context notifyException:exception];
- return result;
-}
-
-- (NSArray *)toArray
-{
- JSValueRef exception = 0;
- id result = valueToArray([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- [_context notifyException:exception];
- return result;
-}
-
-- (NSDictionary *)toDictionary
-{
- JSValueRef exception = 0;
- id result = valueToDictionary([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- [_context notifyException:exception];
- return result;
-}
-
-- (JSValue *)valueForProperty:(NSString *)propertyName
-{
- JSValueRef exception = 0;
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
- JSValueRef result = JSObjectGetProperty([_context JSGlobalContextRef], object, name, &exception);
- JSStringRelease(name);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- return [JSValue valueWithJSValueRef:result inContext:_context];
-}
-
-- (void)setValue:(id)value forProperty:(NSString *)propertyName
-{
- JSValueRef exception = 0;
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception) {
- [_context notifyException:exception];
- return;
- }
-
- JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
- JSObjectSetProperty([_context JSGlobalContextRef], object, name, objectToValue(_context, value), 0, &exception);
- JSStringRelease(name);
- if (exception) {
- [_context notifyException:exception];
- return;
- }
-}
-
-- (BOOL)deleteProperty:(NSString *)propertyName
-{
- JSValueRef exception = 0;
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- return [_context boolFromNotifyException:exception];
-
- JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
- BOOL result = JSObjectDeleteProperty([_context JSGlobalContextRef], object, name, &exception);
- JSStringRelease(name);
- if (exception)
- return [_context boolFromNotifyException:exception];
-
- return result;
-}
-
-- (BOOL)hasProperty:(NSString *)propertyName
-{
- JSValueRef exception = 0;
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- return [_context boolFromNotifyException:exception];
-
- JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
- BOOL result = JSObjectHasProperty([_context JSGlobalContextRef], object, name);
- JSStringRelease(name);
- return result;
-}
-
-- (void)defineProperty:(NSString *)property descriptor:(id)descriptor
-{
- [[_context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, property, descriptor ]];
-}
-
-- (JSValue *)valueAtIndex:(NSUInteger)index
-{
- // Properties that are higher than an unsigned value can hold are converted to a double then inserted as a normal property.
- // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in get().
- if (index != (unsigned)index)
- return [self valueForProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
-
- JSValueRef exception = 0;
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- JSValueRef result = JSObjectGetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- return [JSValue valueWithJSValueRef:result inContext:_context];
-}
-
-- (void)setValue:(id)value atIndex:(NSUInteger)index
-{
- // Properties that are higher than an unsigned value can hold are converted to a double, then inserted as a normal property.
- // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in putByIndex().
- if (index != (unsigned)index)
- return [self setValue:value forProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
-
- JSValueRef exception = 0;
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception) {
- [_context notifyException:exception];
- return;
- }
-
- JSObjectSetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, objectToValue(_context, value), &exception);
- if (exception) {
- [_context notifyException:exception];
- return;
- }
-}
-
-- (BOOL)isUndefined
-{
- return JSValueIsUndefined([_context JSGlobalContextRef], m_value);
-}
-
-- (BOOL)isNull
-{
- return JSValueIsNull([_context JSGlobalContextRef], m_value);
-}
-
-- (BOOL)isBoolean
-{
- return JSValueIsBoolean([_context JSGlobalContextRef], m_value);
-}
-
-- (BOOL)isNumber
-{
- return JSValueIsNumber([_context JSGlobalContextRef], m_value);
-}
-
-- (BOOL)isString
-{
- return JSValueIsString([_context JSGlobalContextRef], m_value);
-}
-
-- (BOOL)isObject
-{
- return JSValueIsObject([_context JSGlobalContextRef], m_value);
-}
-
-- (BOOL)isEqualToObject:(id)value
-{
- return JSValueIsStrictEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value));
-}
-
-- (BOOL)isEqualWithTypeCoercionToObject:(id)value
-{
- JSValueRef exception = 0;
- BOOL result = JSValueIsEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value), &exception);
- if (exception)
- return [_context boolFromNotifyException:exception];
-
- return result;
-}
-
-- (BOOL)isInstanceOf:(id)value
-{
- JSValueRef exception = 0;
- JSObjectRef constructor = JSValueToObject([_context JSGlobalContextRef], objectToValue(_context, value), &exception);
- if (exception)
- return [_context boolFromNotifyException:exception];
-
- BOOL result = JSValueIsInstanceOfConstructor([_context JSGlobalContextRef], m_value, constructor, &exception);
- if (exception)
- return [_context boolFromNotifyException:exception];
-
- return result;
-}
-
-- (JSValue *)callWithArguments:(NSArray *)argumentArray
-{
- NSUInteger argumentCount = [argumentArray count];
- JSValueRef arguments[argumentCount];
- for (unsigned i = 0; i < argumentCount; ++i)
- arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
-
- JSValueRef exception = 0;
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, 0, argumentCount, arguments, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- return [JSValue valueWithJSValueRef:result inContext:_context];
-}
-
-- (JSValue *)constructWithArguments:(NSArray *)argumentArray
-{
- NSUInteger argumentCount = [argumentArray count];
- JSValueRef arguments[argumentCount];
- for (unsigned i = 0; i < argumentCount; ++i)
- arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
-
- JSValueRef exception = 0;
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- JSObjectRef result = JSObjectCallAsConstructor([_context JSGlobalContextRef], object, argumentCount, arguments, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- return [JSValue valueWithJSValueRef:result inContext:_context];
-}
-
-- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments
-{
- NSUInteger argumentCount = [arguments count];
- JSValueRef argumentArray[argumentCount];
- for (unsigned i = 0; i < argumentCount; ++i)
- argumentArray[i] = objectToValue(_context, [arguments objectAtIndex:i]);
-
- JSValueRef exception = 0;
- JSObjectRef thisObject = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- JSStringRef name = JSStringCreateWithCFString((CFStringRef)method);
- JSValueRef function = JSObjectGetProperty([_context JSGlobalContextRef], thisObject, name, &exception);
- JSStringRelease(name);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], function, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, thisObject, argumentCount, argumentArray, &exception);
- if (exception)
- return [_context valueFromNotifyException:exception];
-
- return [JSValue valueWithJSValueRef:result inContext:_context];
-}
-
-@end
-
-@implementation JSValue(StructSupport)
-
-- (CGPoint)toPoint
-{
- return (CGPoint){
- [self[@"x"] toDouble],
- [self[@"y"] toDouble]
- };
-}
-
-- (NSRange)toRange
-{
- return (NSRange){
- [[self[@"location"] toNumber] unsignedIntegerValue],
- [[self[@"length"] toNumber] unsignedIntegerValue]
- };
-}
-
-- (CGRect)toRect
-{
- return (CGRect){
- [self toPoint],
- [self toSize]
- };
-}
-
-- (CGSize)toSize
-{
- return (CGSize){
- [self[@"width"] toDouble],
- [self[@"height"] toDouble]
- };
-}
-
-+ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context
-{
- return [JSValue valueWithObject:@{
- @"x":@(point.x),
- @"y":@(point.y)
- } inContext:context];
-}
-
-+ (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context
-{
- return [JSValue valueWithObject:@{
- @"location":@(range.location),
- @"length":@(range.length)
- } inContext:context];
-}
-
-+ (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context
-{
- return [JSValue valueWithObject:@{
- @"x":@(rect.origin.x),
- @"y":@(rect.origin.y),
- @"width":@(rect.size.width),
- @"height":@(rect.size.height)
- } inContext:context];
-}
-
-+ (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context
-{
- return [JSValue valueWithObject:@{
- @"width":@(size.width),
- @"height":@(size.height)
- } inContext:context];
-}
-
-@end
-
-@implementation JSValue(SubscriptSupport)
-
-- (JSValue *)objectForKeyedSubscript:(id)key
-{
- if (![key isKindOfClass:[NSString class]]) {
- key = [[JSValue valueWithObject:key inContext:_context] toString];
- if (!key)
- return [JSValue valueWithUndefinedInContext:_context];
- }
-
- return [self valueForProperty:(NSString *)key];
-}
-
-- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index
-{
- return [self valueAtIndex:index];
-}
-
-- (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key
-{
- if (![key isKindOfClass:[NSString class]]) {
- key = [[JSValue valueWithObject:key inContext:_context] toString];
- if (!key)
- return;
- }
-
- [self setValue:object forProperty:(NSString *)key];
-}
-
-- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index
-{
- [self setValue:object atIndex:index];
-}
-
-@end
-
-inline bool isDate(JSObjectRef object, JSGlobalContextRef context)
-{
- JSC::APIEntryShim entryShim(toJS(context));
- return toJS(object)->inherits(&JSC::DateInstance::s_info);
-}
-
-inline bool isArray(JSObjectRef object, JSGlobalContextRef context)
-{
- JSC::APIEntryShim entryShim(toJS(context));
- return toJS(object)->inherits(&JSC::JSArray::s_info);
-}
-
-@implementation JSValue(Internal)
-
-enum ConversionType {
- ContainerNone,
- ContainerArray,
- ContainerDictionary
-};
-
-class JSContainerConvertor {
-public:
- struct Task {
- JSValueRef js;
- id objc;
- ConversionType type;
- };
-
- JSContainerConvertor(JSGlobalContextRef context)
- : m_context(context)
- {
- }
-
- id convert(JSValueRef property);
- void add(Task);
- Task take();
- bool isWorkListEmpty() const { return !m_worklist.size(); }
-
-private:
- JSGlobalContextRef m_context;
- HashMap<JSValueRef, id> m_objectMap;
- Vector<Task> m_worklist;
-};
-
-inline id JSContainerConvertor::convert(JSValueRef value)
-{
- HashMap<JSValueRef, id>::iterator iter = m_objectMap.find(value);
- if (iter != m_objectMap.end())
- return iter->value;
-
- Task result = valueToObjectWithoutCopy(m_context, value);
- if (result.js)
- add(result);
- return result.objc;
-}
-
-void JSContainerConvertor::add(Task task)
-{
- m_objectMap.add(task.js, task.objc);
- if (task.type != ContainerNone)
- m_worklist.append(task);
-}
-
-JSContainerConvertor::Task JSContainerConvertor::take()
-{
- ASSERT(!isWorkListEmpty());
- Task last = m_worklist.last();
- m_worklist.removeLast();
- return last;
-}
-
-static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef context, JSValueRef value)
-{
- if (!JSValueIsObject(context, value)) {
- id primitive;
- if (JSValueIsBoolean(context, value))
- primitive = JSValueToBoolean(context, value) ? @YES : @NO;
- else if (JSValueIsNumber(context, value)) {
- // Normalize the number, so it will unique correctly in the hash map -
- // it's nicer not to leak this internal implementation detail!
- value = JSValueMakeNumber(context, JSValueToNumber(context, value, 0));
- primitive = [NSNumber numberWithDouble:JSValueToNumber(context, value, 0)];
- } else if (JSValueIsString(context, value)) {
- // Would be nice to unique strings, too.
- JSStringRef jsstring = JSValueToStringCopy(context, value, 0);
- NSString * stringNS = (NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring);
- JSStringRelease(jsstring);
- primitive = [stringNS autorelease];
- } else if (JSValueIsNull(context, value))
- primitive = [NSNull null];
- else {
- ASSERT(JSValueIsUndefined(context, value));
- primitive = nil;
- }
- return (JSContainerConvertor::Task){ value, primitive, ContainerNone };
- }
-
- JSObjectRef object = JSValueToObject(context, value, 0);
-
- if (id wrapped = tryUnwrapObjcObject(context, object))
- return (JSContainerConvertor::Task){ object, wrapped, ContainerNone };
-
- if (isDate(object, context))
- return (JSContainerConvertor::Task){ object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0)], ContainerNone };
-
- if (isArray(object, context))
- return (JSContainerConvertor::Task){ object, [NSMutableArray array], ContainerArray };
-
- return (JSContainerConvertor::Task){ object, [NSMutableDictionary dictionary], ContainerDictionary };
-}
-
-static id containerValueToObject(JSGlobalContextRef context, JSContainerConvertor::Task task)
-{
- ASSERT(task.type != ContainerNone);
- JSContainerConvertor convertor(context);
- convertor.add(task);
- ASSERT(!convertor.isWorkListEmpty());
-
- do {
- JSContainerConvertor::Task current = convertor.take();
- ASSERT(JSValueIsObject(context, current.js));
- JSObjectRef js = JSValueToObject(context, current.js, 0);
-
- if (current.type == ContainerArray) {
- ASSERT([current.objc isKindOfClass:[NSMutableArray class]]);
- NSMutableArray *array = (NSMutableArray *)current.objc;
-
- JSStringRef lengthString = JSStringCreateWithUTF8CString("length");
- unsigned length = JSC::toUInt32(JSValueToNumber(context, JSObjectGetProperty(context, js, lengthString, 0), 0));
- JSStringRelease(lengthString);
-
- for (unsigned i = 0; i < length; ++i) {
- id objc = convertor.convert(JSObjectGetPropertyAtIndex(context, js, i, 0));
- [array addObject:objc ? objc : [NSNull null]];
- }
- } else {
- ASSERT([current.objc isKindOfClass:[NSMutableDictionary class]]);
- NSMutableDictionary *dictionary = (NSMutableDictionary *)current.objc;
-
- JSPropertyNameArrayRef propertyNameArray = JSObjectCopyPropertyNames(context, js);
- size_t length = JSPropertyNameArrayGetCount(propertyNameArray);
-
- for (size_t i = 0; i < length; ++i) {
- JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNameArray, i);
- if (id objc = convertor.convert(JSObjectGetProperty(context, js, propertyName, 0)))
- dictionary[[(NSString *)JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]] = objc;
- }
-
- JSPropertyNameArrayRelease(propertyNameArray);
- }
-
- } while (!convertor.isWorkListEmpty());
-
- return task.objc;
-}
-
-id valueToObject(JSContext *context, JSValueRef value)
-{
- JSContainerConvertor::Task result = valueToObjectWithoutCopy([context JSGlobalContextRef], value);
- if (result.type == ContainerNone)
- return result.objc;
- return containerValueToObject([context JSGlobalContextRef], result);
-}
-
-id valueToNumber(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
-{
- ASSERT(!*exception);
- if (id wrapped = tryUnwrapObjcObject(context, value)) {
- if ([wrapped isKindOfClass:[NSNumber class]])
- return wrapped;
- }
-
- if (JSValueIsBoolean(context, value))
- return JSValueToBoolean(context, value) ? @YES : @NO;
-
- double result = JSValueToNumber(context, value, exception);
- return [NSNumber numberWithDouble:*exception ? std::numeric_limits<double>::quiet_NaN() : result];
-}
-
-id valueToString(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
-{
- ASSERT(!*exception);
- if (id wrapped = tryUnwrapObjcObject(context, value)) {
- if ([wrapped isKindOfClass:[NSString class]])
- return wrapped;
- }
-
- JSStringRef jsstring = JSValueToStringCopy(context, value, exception);
- if (*exception) {
- ASSERT(!jsstring);
- return nil;
- }
-
- NSString *stringNS = [(NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring) autorelease];
- JSStringRelease(jsstring);
- return stringNS;
-}
-
-id valueToDate(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
-{
- ASSERT(!*exception);
- if (id wrapped = tryUnwrapObjcObject(context, value)) {
- if ([wrapped isKindOfClass:[NSDate class]])
- return wrapped;
- }
-
- double result = JSValueToNumber(context, value, exception);
- return *exception ? nil : [NSDate dateWithTimeIntervalSince1970:result];
-}
-
-id valueToArray(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
-{
- ASSERT(!*exception);
- if (id wrapped = tryUnwrapObjcObject(context, value)) {
- if ([wrapped isKindOfClass:[NSArray class]])
- return wrapped;
- }
-
- if (JSValueIsObject(context, value))
- return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableArray array], ContainerArray});
-
- if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value)))
- *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSArray"));
- return nil;
-}
-
-id valueToDictionary(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
-{
- ASSERT(!*exception);
- if (id wrapped = tryUnwrapObjcObject(context, value)) {
- if ([wrapped isKindOfClass:[NSDictionary class]])
- return wrapped;
- }
-
- if (JSValueIsObject(context, value))
- return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableDictionary dictionary], ContainerDictionary});
-
- if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value)))
- *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSDictionary"));
- return nil;
-}
-
-class ObjcContainerConvertor {
-public:
- struct Task {
- id objc;
- JSValueRef js;
- ConversionType type;
- };
-
- ObjcContainerConvertor(JSContext *context)
- : m_context(context)
- {
- }
-
- JSValueRef convert(id object);
- void add(Task);
- Task take();
- bool isWorkListEmpty() const { return !m_worklist.size(); }
-
-private:
- JSContext *m_context;
- HashMap<id, JSValueRef> m_objectMap;
- Vector<Task> m_worklist;
-};
-
-JSValueRef ObjcContainerConvertor::convert(id object)
-{
- ASSERT(object);
-
- auto it = m_objectMap.find(object);
- if (it != m_objectMap.end())
- return it->value;
-
- ObjcContainerConvertor::Task task = objectToValueWithoutCopy(m_context, object);
- add(task);
- return task.js;
-}
-
-void ObjcContainerConvertor::add(ObjcContainerConvertor::Task task)
-{
- m_objectMap.add(task.objc, task.js);
- if (task.type != ContainerNone)
- m_worklist.append(task);
-}
-
-ObjcContainerConvertor::Task ObjcContainerConvertor::take()
-{
- ASSERT(!isWorkListEmpty());
- Task last = m_worklist.last();
- m_worklist.removeLast();
- return last;
-}
-
-inline bool isNSBoolean(id object)
-{
- ASSERT([@YES class] == [@NO class]);
- ASSERT([@YES class] != [NSNumber class]);
- ASSERT([[@YES class] isSubclassOfClass:[NSNumber class]]);
- return [object isKindOfClass:[@YES class]];
-}
-
-static ObjcContainerConvertor::Task objectToValueWithoutCopy(JSContext *context, id object)
-{
- JSGlobalContextRef contextRef = [context JSGlobalContextRef];
-
- if (!object)
- return (ObjcContainerConvertor::Task){ object, JSValueMakeUndefined(contextRef), ContainerNone };
-
- if (!class_conformsToProtocol(object_getClass(object), getJSExportProtocol())) {
- if ([object isKindOfClass:[NSArray class]])
- return (ObjcContainerConvertor::Task){ object, JSObjectMakeArray(contextRef, 0, NULL, 0), ContainerArray };
-
- if ([object isKindOfClass:[NSDictionary class]])
- return (ObjcContainerConvertor::Task){ object, JSObjectMake(contextRef, 0, 0), ContainerDictionary };
-
- if ([object isKindOfClass:[NSNull class]])
- return (ObjcContainerConvertor::Task){ object, JSValueMakeNull(contextRef), ContainerNone };
-
- if ([object isKindOfClass:[JSValue class]])
- return (ObjcContainerConvertor::Task){ object, ((JSValue *)object)->m_value, ContainerNone };
-
- if ([object isKindOfClass:[NSString class]]) {
- JSStringRef string = JSStringCreateWithCFString((CFStringRef)object);
- JSValueRef js = JSValueMakeString(contextRef, string);
- JSStringRelease(string);
- return (ObjcContainerConvertor::Task){ object, js, ContainerNone };
- }
-
- if ([object isKindOfClass:[NSNumber class]]) {
- if (isNSBoolean(object))
- return (ObjcContainerConvertor::Task){ object, JSValueMakeBoolean(contextRef, [object boolValue]), ContainerNone };
- return (ObjcContainerConvertor::Task){ object, JSValueMakeNumber(contextRef, [object doubleValue]), ContainerNone };
- }
-
- if ([object isKindOfClass:[NSDate class]]) {
- JSValueRef argument = JSValueMakeNumber(contextRef, [object timeIntervalSince1970]);
- JSObjectRef result = JSObjectMakeDate(contextRef, 1, &argument, 0);
- return (ObjcContainerConvertor::Task){ object, result, ContainerNone };
- }
-
- if ([object isKindOfClass:[JSManagedValue class]]) {
- JSValue *value = [static_cast<JSManagedValue *>(object) value];
- if (!value)
- return (ObjcContainerConvertor::Task) { object, JSValueMakeUndefined(contextRef), ContainerNone };
- return (ObjcContainerConvertor::Task){ object, value->m_value, ContainerNone };
- }
- }
-
- return (ObjcContainerConvertor::Task){ object, valueInternalValue([context wrapperForObjCObject:object]), ContainerNone };
-}
-
-JSValueRef objectToValue(JSContext *context, id object)
-{
- JSGlobalContextRef contextRef = [context JSGlobalContextRef];
-
- ObjcContainerConvertor::Task task = objectToValueWithoutCopy(context, object);
- if (task.type == ContainerNone)
- return task.js;
-
- ObjcContainerConvertor convertor(context);
- convertor.add(task);
- ASSERT(!convertor.isWorkListEmpty());
-
- do {
- ObjcContainerConvertor::Task current = convertor.take();
- ASSERT(JSValueIsObject(contextRef, current.js));
- JSObjectRef js = JSValueToObject(contextRef, current.js, 0);
-
- if (current.type == ContainerArray) {
- ASSERT([current.objc isKindOfClass:[NSArray class]]);
- NSArray *array = (NSArray *)current.objc;
- NSUInteger count = [array count];
- for (NSUInteger index = 0; index < count; ++index)
- JSObjectSetPropertyAtIndex(contextRef, js, index, convertor.convert([array objectAtIndex:index]), 0);
- } else {
- ASSERT(current.type == ContainerDictionary);
- ASSERT([current.objc isKindOfClass:[NSDictionary class]]);
- NSDictionary *dictionary = (NSDictionary *)current.objc;
- for (id key in [dictionary keyEnumerator]) {
- if ([key isKindOfClass:[NSString class]]) {
- JSStringRef propertyName = JSStringCreateWithCFString((CFStringRef)key);
- JSObjectSetProperty(contextRef, js, propertyName, convertor.convert([dictionary objectForKey:key]), 0, 0);
- JSStringRelease(propertyName);
- }
- }
- }
-
- } while (!convertor.isWorkListEmpty());
-
- return task.js;
-}
-
-JSValueRef valueInternalValue(JSValue * value)
-{
- return value->m_value;
-}
-
-+ (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context
-{
- return [context wrapperForJSObject:value];
-}
-
-- (JSValue *)init
-{
- return nil;
-}
-
-- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context
-{
- if (!value || !context)
- return nil;
-
- self = [super init];
- if (!self)
- return nil;
-
- _context = [context retain];
- m_value = value;
- JSValueProtect([_context JSGlobalContextRef], m_value);
- return self;
-}
-
-struct StructTagHandler {
- SEL typeToValueSEL;
- SEL valueToTypeSEL;
-};
-typedef HashMap<String, StructTagHandler> StructHandlers;
-
-static StructHandlers* createStructHandlerMap()
-{
- StructHandlers* structHandlers = new StructHandlers();
-
- size_t valueWithXinContextLength = strlen("valueWithX:inContext:");
- size_t toXLength = strlen("toX");
-
- // Step 1: find all valueWith<Foo>:inContext: class methods in JSValue.
- forEachMethodInClass(object_getClass([JSValue class]), ^(Method method){
- SEL selector = method_getName(method);
- const char* name = sel_getName(selector);
- size_t nameLength = strlen(name);
- // Check for valueWith<Foo>:context:
- if (nameLength < valueWithXinContextLength || memcmp(name, "valueWith", 9) || memcmp(name + nameLength - 11, ":inContext:", 11))
- return;
- // Check for [ id, SEL, <type>, <contextType> ]
- if (method_getNumberOfArguments(method) != 4)
- return;
- char idType[3];
- // Check 2nd argument type is "@"
- char* secondType = method_copyArgumentType(method, 3);
- if (strcmp(secondType, "@") != 0) {
- free(secondType);
- return;
- }
- free(secondType);
- // Check result type is also "@"
- method_getReturnType(method, idType, 3);
- if (strcmp(idType, "@") != 0)
- return;
- char* type = method_copyArgumentType(method, 2);
- structHandlers->add(StringImpl::create(type), (StructTagHandler){ selector, 0 });
- free(type);
- });
-
- // Step 2: find all to<Foo> instance methods in JSValue.
- forEachMethodInClass([JSValue class], ^(Method method){
- SEL selector = method_getName(method);
- const char* name = sel_getName(selector);
- size_t nameLength = strlen(name);
- // Check for to<Foo>
- if (nameLength < toXLength || memcmp(name, "to", 2))
- return;
- // Check for [ id, SEL ]
- if (method_getNumberOfArguments(method) != 2)
- return;
- // Try to find a matching valueWith<Foo>:context: method.
- char* type = method_copyReturnType(method);
-
- StructHandlers::iterator iter = structHandlers->find(type);
- free(type);
- if (iter == structHandlers->end())
- return;
- StructTagHandler& handler = iter->value;
-
- // check that strlen(<foo>) == strlen(<Foo>)
- const char* valueWithName = sel_getName(handler.typeToValueSEL);
- size_t valueWithLength = strlen(valueWithName);
- if (valueWithLength - valueWithXinContextLength != nameLength - toXLength)
- return;
- // Check that <Foo> == <Foo>
- if (memcmp(valueWithName + 9, name + 2, nameLength - toXLength - 1))
- return;
- handler.valueToTypeSEL = selector;
- });
-
- // Step 3: clean up - remove entries where we found prospective valueWith<Foo>:inContext: conversions, but no matching to<Foo> methods.
- typedef HashSet<String> RemoveSet;
- RemoveSet removeSet;
- for (StructHandlers::iterator iter = structHandlers->begin(); iter != structHandlers->end(); ++iter) {
- StructTagHandler& handler = iter->value;
- if (!handler.valueToTypeSEL)
- removeSet.add(iter->key);
- }
-
- for (RemoveSet::iterator iter = removeSet.begin(); iter != removeSet.end(); ++iter)
- structHandlers->remove(*iter);
-
- return structHandlers;
-}
-
-static StructTagHandler* handerForStructTag(const char* encodedType)
-{
- static SpinLock handerForStructTagLock = SPINLOCK_INITIALIZER;
- SpinLockHolder lockHolder(&handerForStructTagLock);
-
- static StructHandlers* structHandlers = createStructHandlerMap();
-
- StructHandlers::iterator iter = structHandlers->find(encodedType);
- if (iter == structHandlers->end())
- return 0;
- return &iter->value;
-}
-
-+ (SEL)selectorForStructToValue:(const char *)structTag
-{
- StructTagHandler* handler = handerForStructTag(structTag);
- return handler ? handler->typeToValueSEL : nil;
-}
-
-+ (SEL)selectorForValueToStruct:(const char *)structTag
-{
- StructTagHandler* handler = handerForStructTag(structTag);
- return handler ? handler->valueToTypeSEL : nil;
-}
-
-- (void)dealloc
-{
- JSValueUnprotect([_context JSGlobalContextRef], m_value);
- [_context release];
- _context = nil;
- [super dealloc];
-}
-
-- (NSString *)description
-{
- if (id wrapped = tryUnwrapObjcObject([_context JSGlobalContextRef], m_value))
- return [wrapped description];
- return [self toString];
-}
-
-NSInvocation *typeToValueInvocationFor(const char* encodedType)
-{
- SEL selector = [JSValue selectorForStructToValue:encodedType];
- if (!selector)
- return 0;
-
- const char* methodTypes = method_getTypeEncoding(class_getClassMethod([JSValue class], selector));
- NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
- [invocation setSelector:selector];
- return invocation;
-}
-
-NSInvocation *valueToTypeInvocationFor(const char* encodedType)
-{
- SEL selector = [JSValue selectorForValueToStruct:encodedType];
- if (!selector)
- return 0;
-
- const char* methodTypes = method_getTypeEncoding(class_getInstanceMethod([JSValue class], selector));
- NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
- [invocation setSelector:selector];
- return invocation;
-}
-
-@end
-
-#endif