EJConvert.m 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #import "EJConvert.h"
  2. NSString *JSValueToNSString( JSContextRef ctx, JSValueRef v ) {
  3. JSStringRef jsString = JSValueToStringCopy( ctx, v, NULL );
  4. if( !jsString ) return nil;
  5. NSString *string = (NSString *)JSStringCopyCFString( kCFAllocatorDefault, jsString );
  6. [string autorelease];
  7. JSStringRelease( jsString );
  8. return string;
  9. }
  10. JSValueRef NSStringToJSValue( JSContextRef ctx, NSString *string ) {
  11. JSStringRef jstr = JSStringCreateWithCFString((CFStringRef)string);
  12. JSValueRef ret = JSValueMakeString(ctx, jstr);
  13. JSStringRelease(jstr);
  14. return ret;
  15. }
  16. // JSValueToNumberFast blindly assumes that the given JSValueRef is a
  17. // a number. Everything else will be silently converted to 0.
  18. // This functions comes in a 64bit and 32bit flavor, since the NaN-Boxing
  19. // in JSC works a bit differently on each platforms. For an explanation
  20. // of the taggging refer to JSC/runtime/JSCJSValue.h
  21. // The 32bit version just calls the normal JSValueToNumber() function
  22. // and is thus a lot slower.
  23. double JSValueToNumberFast(JSContextRef ctx, JSValueRef v) {
  24. #if __LP64__ // arm64 version
  25. union {
  26. int64_t asInt64;
  27. double asDouble;
  28. struct { int32_t asInt; int32_t tag; } asBits;
  29. } taggedValue = { .asInt64 = (int64_t)v };
  30. #define DoubleEncodeOffset 0x1000000000000ll
  31. #define TagTypeNumber 0xffff0000
  32. #define ValueTrue 0x7
  33. if( (taggedValue.asBits.tag & TagTypeNumber) == TagTypeNumber ) {
  34. return taggedValue.asBits.asInt;
  35. }
  36. else if( taggedValue.asBits.tag & TagTypeNumber ) {
  37. taggedValue.asInt64 -= DoubleEncodeOffset;
  38. return taggedValue.asDouble;
  39. }
  40. else if( taggedValue.asBits.asInt == ValueTrue ) {
  41. return 1.0;
  42. }
  43. else {
  44. return 0; // false, undefined, null, object
  45. }
  46. #else // armv7 version
  47. return JSValueToNumber(ctx, v, NULL);
  48. #endif
  49. }
  50. void JSValueUnprotectSafe( JSContextRef ctx, JSValueRef v ) {
  51. if( ctx && v ) {
  52. JSValueUnprotect(ctx, v);
  53. }
  54. }
  55. JSValueRef NSObjectToJSValue( JSContextRef ctx, NSObject *obj ) {
  56. JSValueRef ret = NULL;
  57. // String
  58. if( [obj isKindOfClass:NSString.class] ) {
  59. ret = NSStringToJSValue(ctx, (NSString *)obj);
  60. }
  61. // Number or Bool
  62. else if( [obj isKindOfClass:NSNumber.class] ) {
  63. NSNumber *number = (NSNumber *)obj;
  64. if( strcmp(number.objCType, @encode(BOOL)) == 0 ) {
  65. ret = JSValueMakeBoolean(ctx, number.boolValue);
  66. }
  67. else {
  68. ret = JSValueMakeNumber(ctx, number.doubleValue);
  69. }
  70. }
  71. // Date
  72. else if( [obj isKindOfClass:NSDate.class] ) {
  73. NSDate *date = (NSDate *)obj;
  74. JSValueRef timestamp = JSValueMakeNumber(ctx, date.timeIntervalSince1970 * 1000.0);
  75. ret = JSObjectMakeDate(ctx, 1, &timestamp, NULL);
  76. }
  77. // Array
  78. else if( [obj isKindOfClass:NSArray.class] ) {
  79. NSArray *array = (NSArray *)obj;
  80. JSValueRef *args = malloc(array.count * sizeof(JSValueRef));
  81. for( int i = 0; i < array.count; i++ ) {
  82. args[i] = NSObjectToJSValue(ctx, array[i] );
  83. }
  84. ret = JSObjectMakeArray(ctx, array.count, args, NULL);
  85. free(args);
  86. }
  87. // Dictionary
  88. else if( [obj isKindOfClass:NSDictionary.class] ) {
  89. NSDictionary *dict = (NSDictionary *)obj;
  90. ret = JSObjectMake(ctx, NULL, NULL);
  91. for( NSString *key in dict ) {
  92. JSStringRef jsKey = JSStringCreateWithUTF8CString(key.UTF8String);
  93. JSValueRef value = NSObjectToJSValue(ctx, dict[key]);
  94. JSObjectSetProperty(ctx, (JSObjectRef)ret, jsKey, value, NULL, NULL);
  95. JSStringRelease(jsKey);
  96. }
  97. }
  98. return ret ? ret : JSValueMakeNull(ctx);
  99. }
  100. NSObject *JSValueToNSObject( JSContextRef ctx, JSValueRef value ) {
  101. JSType type = JSValueGetType(ctx, value);
  102. switch( type ) {
  103. case kJSTypeString: return JSValueToNSString(ctx, value);
  104. case kJSTypeBoolean: return [NSNumber numberWithBool:JSValueToBoolean(ctx, value)];
  105. case kJSTypeNumber: return [NSNumber numberWithDouble:JSValueToNumberFast(ctx, value)];
  106. case kJSTypeNull: return nil;
  107. case kJSTypeUndefined: return nil;
  108. case kJSTypeObject: break;
  109. }
  110. if( type == kJSTypeObject ) {
  111. JSObjectRef jsObj = (JSObjectRef)value;
  112. // Get the Array constructor to check if this Object is an Array
  113. JSStringRef arrayName = JSStringCreateWithUTF8CString("Array");
  114. JSObjectRef arrayConstructor = (JSObjectRef)JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), arrayName, NULL);
  115. JSStringRelease(arrayName);
  116. if( JSValueIsInstanceOfConstructor(ctx, jsObj, arrayConstructor, NULL) ) {
  117. // Array
  118. JSStringRef lengthName = JSStringCreateWithUTF8CString("length");
  119. int count = JSValueToNumberFast(ctx, JSObjectGetProperty(ctx, jsObj, lengthName, NULL));
  120. JSStringRelease(lengthName);
  121. NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
  122. for( int i = 0; i < count; i++ ) {
  123. NSObject *obj = JSValueToNSObject(ctx, JSObjectGetPropertyAtIndex(ctx, jsObj, i, NULL));
  124. [array addObject:(obj ? obj : NSNull.null)];
  125. }
  126. return array;
  127. }
  128. else {
  129. // Plain Object
  130. JSPropertyNameArrayRef properties = JSObjectCopyPropertyNames(ctx, jsObj);
  131. size_t count = JSPropertyNameArrayGetCount(properties);
  132. NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:count];
  133. for( size_t i = 0; i < count; i++ ) {
  134. JSStringRef jsName = JSPropertyNameArrayGetNameAtIndex(properties, i);
  135. NSObject *obj = JSValueToNSObject(ctx, JSObjectGetProperty(ctx, jsObj, jsName, NULL));
  136. NSString *name = (NSString *)JSStringCopyCFString( kCFAllocatorDefault, jsName );
  137. dict[name] = obj ? obj : NSNull.null;
  138. [name release];
  139. }
  140. JSPropertyNameArrayRelease(properties);
  141. return dict;
  142. }
  143. }
  144. return nil;
  145. }