iOS: use category method to translate JSValue to object
This commit is contained in:
parent
85a03bdd93
commit
aa837b807a
@ -8,9 +8,9 @@ target 'Example' do
|
|||||||
pod 'DoricCore', :path => '../../'
|
pod 'DoricCore', :path => '../../'
|
||||||
pod 'DoricDevkit', :path => '../../'
|
pod 'DoricDevkit', :path => '../../'
|
||||||
|
|
||||||
pod 'YYWebImage'
|
#pod 'YYWebImage'
|
||||||
|
|
||||||
pod 'YYImage/WebP'
|
#pod 'YYImage/WebP'
|
||||||
|
|
||||||
pod 'SDWebImage'
|
pod 'SDWebImage'
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#import "DoricContext.h"
|
#import "DoricContext.h"
|
||||||
#import "DoricContextManager.h"
|
#import "DoricContextManager.h"
|
||||||
#import "DoricPerformanceProfile.h"
|
#import "DoricPerformanceProfile.h"
|
||||||
|
#import "JSValue+Doric.h"
|
||||||
|
|
||||||
@interface DoricDefaultMonitor : NSObject <DoricMonitorProtocol>
|
@interface DoricDefaultMonitor : NSObject <DoricMonitorProtocol>
|
||||||
@end
|
@end
|
||||||
@ -210,8 +211,8 @@ - (void)initJSExecutor {
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self.jsExecutor injectGlobalJSObject:INJECT_BRIDGE obj:^(NSString *contextId, NSString *module, NSString *method, NSString *callbackId, id argument) {
|
[self.jsExecutor injectGlobalJSObject:INJECT_BRIDGE obj:^(NSString *contextId, NSString *module, NSString *method, NSString *callbackId, JSValue *argument) {
|
||||||
return [self.bridgeExtension callNativeWithContextId:contextId module:module method:method callbackId:callbackId argument:argument];
|
return [self.bridgeExtension callNativeWithContextId:contextId module:module method:method callbackId:callbackId argument:[self jsValueToObject:argument]];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +265,7 @@ - (JSValue *)invokeDoricMethod:(NSString *)method arguments:(va_list)args {
|
|||||||
- (JSValue *)invokeDoricMethod:(NSString *)method argumentsArray:(NSArray *)args {
|
- (JSValue *)invokeDoricMethod:(NSString *)method argumentsArray:(NSArray *)args {
|
||||||
JSValue *ret = [self.jsExecutor invokeObject:GLOBAL_DORIC method:method args:args];
|
JSValue *ret = [self.jsExecutor invokeObject:GLOBAL_DORIC method:method args:args];
|
||||||
if (![method isEqualToString:@"pureCallEntityMethod"]) {
|
if (![method isEqualToString:@"pureCallEntityMethod"]) {
|
||||||
[self.jsExecutor invokeObject:GLOBAL_DORIC method:DORIC_HOOK_NATIVE_CALL args:nil];
|
[self.jsExecutor invokeObject:GLOBAL_DORIC method:DORIC_HOOK_NATIVE_CALL args:@[]];
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -311,4 +312,8 @@ - (void)callbackTimer:(NSTimer *)timer {
|
|||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id)jsValueToObject:(JSValue *)jsValue {
|
||||||
|
return [jsValue toObjectWithArrayBuffer];
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -9,4 +9,6 @@
|
|||||||
- (BOOL)isArrayBuffer;
|
- (BOOL)isArrayBuffer;
|
||||||
|
|
||||||
- (NSData *)toArrayBuffer;
|
- (NSData *)toArrayBuffer;
|
||||||
|
|
||||||
|
- (id)toObjectWithArrayBuffer;
|
||||||
@end
|
@end
|
@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by pengfei.zhou on 2021/11/19.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "JSValue+Doric.h"
|
|
||||||
|
|
||||||
@implementation JSValue (Doric)
|
|
||||||
- (BOOL)isArrayBuffer {
|
|
||||||
JSContextRef ctx = self.context.JSGlobalContextRef;
|
|
||||||
JSValueRef jsValueRef = self.JSValueRef;
|
|
||||||
if (self.isObject) {
|
|
||||||
JSTypedArrayType type = JSValueGetTypedArrayType(ctx, jsValueRef, NULL);
|
|
||||||
return type == kJSTypedArrayTypeArrayBuffer;
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSData *)toArrayBuffer {
|
|
||||||
if (!self.isArrayBuffer) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
JSContextRef ctx = self.context.JSGlobalContextRef;
|
|
||||||
JSValueRef jsValueRef = self.JSValueRef;
|
|
||||||
JSObjectRef ref = JSValueToObject(ctx, jsValueRef, NULL);
|
|
||||||
size_t size = JSObjectGetArrayBufferByteLength(ctx, ref, NULL);
|
|
||||||
void *ptr = JSObjectGetArrayBufferBytesPtr(ctx, ref, NULL);
|
|
||||||
|
|
||||||
return [[NSData alloc] initWithBytesNoCopy:ptr length:size freeWhenDone:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
189
doric-iOS/Pod/Classes/Engine/JSValue+Doric.mm
Normal file
189
doric-iOS/Pod/Classes/Engine/JSValue+Doric.mm
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
//
|
||||||
|
// Created by pengfei.zhou on 2021/11/19.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "JSValue+Doric.h"
|
||||||
|
#import <vector>
|
||||||
|
#import <unordered_map>
|
||||||
|
|
||||||
|
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;
|
||||||
|
std::unordered_map<JSValueRef, __unsafe_unretained id> m_objectMap;
|
||||||
|
std::vector<Task> m_worklist;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
auto lengthString = JSStringCreateWithUTF8CString("length");
|
||||||
|
unsigned length = static_cast<unsigned int>(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))) {
|
||||||
|
CFStringRef cfString = JSStringCopyCFString(kCFAllocatorDefault, propertyName);
|
||||||
|
NSString *key = (__bridge NSString *) cfString;
|
||||||
|
dictionary[key] = objc;
|
||||||
|
CFRelease(cfString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSPropertyNameArrayRelease(propertyNameArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (!convertor.isWorkListEmpty());
|
||||||
|
|
||||||
|
return task.objc;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = @(JSValueToNumber(context, value, 0));
|
||||||
|
} else if (JSValueIsString(context, value)) {
|
||||||
|
// Would be nice to unique strings, too.
|
||||||
|
JSStringRef jsString = JSValueToStringCopy(context, value, 0);
|
||||||
|
primitive = CFBridgingRelease(JSStringCopyCFString(kCFAllocatorDefault, jsString));
|
||||||
|
JSStringRelease(jsString);
|
||||||
|
} else if (JSValueIsNull(context, value)) {
|
||||||
|
primitive = [NSNull null];
|
||||||
|
} else {
|
||||||
|
primitive = nil;
|
||||||
|
}
|
||||||
|
return {value, primitive, ContainerNone};
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObjectRef object = JSValueToObject(context, value, 0);
|
||||||
|
JSTypedArrayType type = JSValueGetTypedArrayType(context, value, NULL);
|
||||||
|
if (type == kJSTypedArrayTypeArrayBuffer) {
|
||||||
|
size_t size = JSObjectGetArrayBufferByteLength(context, object, NULL);
|
||||||
|
void *ptr = JSObjectGetArrayBufferBytesPtr(context, object, NULL);
|
||||||
|
id primitive = [[NSData alloc] initWithBytesNoCopy:ptr length:size freeWhenDone:NO];
|
||||||
|
return {value, primitive, ContainerNone};
|
||||||
|
}
|
||||||
|
if (JSValueIsArray(context, value))
|
||||||
|
return {object, [NSMutableArray array], ContainerArray};
|
||||||
|
|
||||||
|
return {object, [NSMutableDictionary dictionary], ContainerDictionary};
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation JSValue (Doric)
|
||||||
|
|
||||||
|
|
||||||
|
inline id JSContainerConvertor::convert(JSValueRef value) {
|
||||||
|
auto iter = m_objectMap.find(value);
|
||||||
|
if (iter != m_objectMap.end()) {
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task result = valueToObjectWithoutCopy(m_context, value);
|
||||||
|
if (result.js)
|
||||||
|
add(result);
|
||||||
|
return result.objc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JSContainerConvertor::add(Task task) {
|
||||||
|
if (task.type != ContainerNone)
|
||||||
|
m_worklist.push_back(task);
|
||||||
|
else
|
||||||
|
m_objectMap.insert({task.js, task.objc});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
JSContainerConvertor::Task JSContainerConvertor::take() {
|
||||||
|
assert(!isWorkListEmpty());
|
||||||
|
Task last = m_worklist.back();
|
||||||
|
m_worklist.pop_back();
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isArrayBuffer {
|
||||||
|
JSContextRef ctx = self.context.JSGlobalContextRef;
|
||||||
|
JSValueRef jsValueRef = self.JSValueRef;
|
||||||
|
if (self.isObject) {
|
||||||
|
JSTypedArrayType type = JSValueGetTypedArrayType(ctx, jsValueRef, NULL);
|
||||||
|
return type == kJSTypedArrayTypeArrayBuffer;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)toArrayBuffer {
|
||||||
|
if (!self.isArrayBuffer) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
JSContextRef ctx = self.context.JSGlobalContextRef;
|
||||||
|
JSValueRef jsValueRef = self.JSValueRef;
|
||||||
|
JSObjectRef ref = JSValueToObject(ctx, jsValueRef, NULL);
|
||||||
|
size_t size = JSObjectGetArrayBufferByteLength(ctx, ref, NULL);
|
||||||
|
void *ptr = JSObjectGetArrayBufferBytesPtr(ctx, ref, NULL);
|
||||||
|
|
||||||
|
return [[NSData alloc] initWithBytesNoCopy:ptr length:size freeWhenDone:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)toObjectWithArrayBuffer {
|
||||||
|
return valueToObject(self.context, self.JSValueRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Reference in New Issue
Block a user