| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- #import "EJConvert.h"
- NSString *JSValueToNSString( JSContextRef ctx, JSValueRef v ) {
- JSStringRef jsString = JSValueToStringCopy( ctx, v, NULL );
- if( !jsString ) return nil;
-
- NSString *string = (NSString *)JSStringCopyCFString( kCFAllocatorDefault, jsString );
- [string autorelease];
- JSStringRelease( jsString );
-
- return string;
- }
- JSValueRef NSStringToJSValue( JSContextRef ctx, NSString *string ) {
- JSStringRef jstr = JSStringCreateWithCFString((CFStringRef)string);
- JSValueRef ret = JSValueMakeString(ctx, jstr);
- JSStringRelease(jstr);
- return ret;
- }
- // JSValueToNumberFast blindly assumes that the given JSValueRef is a
- // a number. Everything else will be silently converted to 0.
- // This functions comes in a 64bit and 32bit flavor, since the NaN-Boxing
- // in JSC works a bit differently on each platforms. For an explanation
- // of the taggging refer to JSC/runtime/JSCJSValue.h
- // The 32bit version just calls the normal JSValueToNumber() function
- // and is thus a lot slower.
- double JSValueToNumberFast(JSContextRef ctx, JSValueRef v) {
- #if __LP64__ // arm64 version
- union {
- int64_t asInt64;
- double asDouble;
- struct { int32_t asInt; int32_t tag; } asBits;
- } taggedValue = { .asInt64 = (int64_t)v };
-
- #define DoubleEncodeOffset 0x1000000000000ll
- #define TagTypeNumber 0xffff0000
- #define ValueTrue 0x7
-
- if( (taggedValue.asBits.tag & TagTypeNumber) == TagTypeNumber ) {
- return taggedValue.asBits.asInt;
- }
- else if( taggedValue.asBits.tag & TagTypeNumber ) {
- taggedValue.asInt64 -= DoubleEncodeOffset;
- return taggedValue.asDouble;
- }
- else if( taggedValue.asBits.asInt == ValueTrue ) {
- return 1.0;
- }
- else {
- return 0; // false, undefined, null, object
- }
- #else // armv7 version
- return JSValueToNumber(ctx, v, NULL);
- #endif
- }
- void JSValueUnprotectSafe( JSContextRef ctx, JSValueRef v ) {
- if( ctx && v ) {
- JSValueUnprotect(ctx, v);
- }
- }
- JSValueRef NSObjectToJSValue( JSContextRef ctx, NSObject *obj ) {
- JSValueRef ret = NULL;
-
- // String
- if( [obj isKindOfClass:NSString.class] ) {
- ret = NSStringToJSValue(ctx, (NSString *)obj);
- }
-
- // Number or Bool
- else if( [obj isKindOfClass:NSNumber.class] ) {
- NSNumber *number = (NSNumber *)obj;
- if( strcmp(number.objCType, @encode(BOOL)) == 0 ) {
- ret = JSValueMakeBoolean(ctx, number.boolValue);
- }
- else {
- ret = JSValueMakeNumber(ctx, number.doubleValue);
- }
- }
-
- // Date
- else if( [obj isKindOfClass:NSDate.class] ) {
- NSDate *date = (NSDate *)obj;
- JSValueRef timestamp = JSValueMakeNumber(ctx, date.timeIntervalSince1970 * 1000.0);
- ret = JSObjectMakeDate(ctx, 1, ×tamp, NULL);
- }
-
- // Array
- else if( [obj isKindOfClass:NSArray.class] ) {
- NSArray *array = (NSArray *)obj;
- JSValueRef *args = malloc(array.count * sizeof(JSValueRef));
- for( int i = 0; i < array.count; i++ ) {
- args[i] = NSObjectToJSValue(ctx, array[i] );
- }
- ret = JSObjectMakeArray(ctx, array.count, args, NULL);
- free(args);
- }
-
- // Dictionary
- else if( [obj isKindOfClass:NSDictionary.class] ) {
- NSDictionary *dict = (NSDictionary *)obj;
- ret = JSObjectMake(ctx, NULL, NULL);
- for( NSString *key in dict ) {
- JSStringRef jsKey = JSStringCreateWithUTF8CString(key.UTF8String);
- JSValueRef value = NSObjectToJSValue(ctx, dict[key]);
- JSObjectSetProperty(ctx, (JSObjectRef)ret, jsKey, value, NULL, NULL);
- JSStringRelease(jsKey);
- }
- }
-
- return ret ? ret : JSValueMakeNull(ctx);
- }
- NSObject *JSValueToNSObject( JSContextRef ctx, JSValueRef value ) {
- JSType type = JSValueGetType(ctx, value);
-
- switch( type ) {
- case kJSTypeString: return JSValueToNSString(ctx, value);
- case kJSTypeBoolean: return [NSNumber numberWithBool:JSValueToBoolean(ctx, value)];
- case kJSTypeNumber: return [NSNumber numberWithDouble:JSValueToNumberFast(ctx, value)];
- case kJSTypeNull: return nil;
- case kJSTypeUndefined: return nil;
- case kJSTypeObject: break;
- }
-
- if( type == kJSTypeObject ) {
- JSObjectRef jsObj = (JSObjectRef)value;
-
- // Get the Array constructor to check if this Object is an Array
- JSStringRef arrayName = JSStringCreateWithUTF8CString("Array");
- JSObjectRef arrayConstructor = (JSObjectRef)JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), arrayName, NULL);
- JSStringRelease(arrayName);
-
- if( JSValueIsInstanceOfConstructor(ctx, jsObj, arrayConstructor, NULL) ) {
- // Array
- JSStringRef lengthName = JSStringCreateWithUTF8CString("length");
- int count = JSValueToNumberFast(ctx, JSObjectGetProperty(ctx, jsObj, lengthName, NULL));
- JSStringRelease(lengthName);
-
- NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
- for( int i = 0; i < count; i++ ) {
- NSObject *obj = JSValueToNSObject(ctx, JSObjectGetPropertyAtIndex(ctx, jsObj, i, NULL));
- [array addObject:(obj ? obj : NSNull.null)];
- }
- return array;
- }
- else {
- // Plain Object
- JSPropertyNameArrayRef properties = JSObjectCopyPropertyNames(ctx, jsObj);
- size_t count = JSPropertyNameArrayGetCount(properties);
-
- NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:count];
- for( size_t i = 0; i < count; i++ ) {
- JSStringRef jsName = JSPropertyNameArrayGetNameAtIndex(properties, i);
- NSObject *obj = JSValueToNSObject(ctx, JSObjectGetProperty(ctx, jsObj, jsName, NULL));
-
- NSString *name = (NSString *)JSStringCopyCFString( kCFAllocatorDefault, jsName );
- dict[name] = obj ? obj : NSNull.null;
- [name release];
- }
-
- JSPropertyNameArrayRelease(properties);
- return dict;
- }
- }
-
- return nil;
- }
|