1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132 |
- /*
- * 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
|