Add MessageQueue method for executing function and returning its result
Reviewed By: majak Differential Revision: D3175793 fbshipit-source-id: e1e66e3dcde8b1fb35973340e12d947a0e955775
This commit is contained in:
parent
ca5d1aebab
commit
7fa677f7c3
|
@ -38,11 +38,10 @@
|
||||||
if (getenv("CI_USE_PACKAGER")) {
|
if (getenv("CI_USE_PACKAGER")) {
|
||||||
NSString *app = @"IntegrationTests/IntegrationTestsApp";
|
NSString *app = @"IntegrationTests/IntegrationTestsApp";
|
||||||
scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]];
|
scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]];
|
||||||
RCTAssert(scriptURL != nil, @"No scriptURL set");
|
|
||||||
} else {
|
} else {
|
||||||
scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"];
|
scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||||
RCTAssert(scriptURL != nil, @"Could not locate main.jsBundle");
|
|
||||||
}
|
}
|
||||||
|
RCTAssert(scriptURL != nil, @"No scriptURL set");
|
||||||
|
|
||||||
_bridge = [[RCTBridge alloc] initWithBundleURL:scriptURL moduleProvider:NULL launchOptions:nil];
|
_bridge = [[RCTBridge alloc] initWithBundleURL:scriptURL moduleProvider:NULL launchOptions:nil];
|
||||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:60];
|
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:60];
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
#import "RCTUIManager.h"
|
#import "RCTUIManager.h"
|
||||||
#import "RCTViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
#import "RCTJavaScriptExecutor.h"
|
||||||
|
|
||||||
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||||
{ \
|
{ \
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#import "RCTBridge+Private.h"
|
#import "RCTBridge+Private.h"
|
||||||
#import "RCTBridgeModule.h"
|
#import "RCTBridgeModule.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
#import "RCTJavaScriptExecutor.h"
|
||||||
|
|
||||||
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||||
{ \
|
{ \
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
|
|
||||||
#import "RCTImageLoader.h"
|
#import "RCTImageLoader.h"
|
||||||
|
|
||||||
#import <libkern/OSAtomic.h>
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import <ImageIO/ImageIO.h>
|
#import <ImageIO/ImageIO.h>
|
||||||
|
|
||||||
|
#import <libkern/OSAtomic.h>
|
||||||
|
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
#import "RCTConvert.h"
|
#import "RCTConvert.h"
|
||||||
#import "RCTDefines.h"
|
#import "RCTDefines.h"
|
||||||
#import "RCTImageUtils.h"
|
#import "RCTImageUtils.h"
|
||||||
|
|
|
@ -47,11 +47,10 @@ static const NSTimeInterval kTestTeardownTimeoutSeconds = 30;
|
||||||
|
|
||||||
if (getenv("CI_USE_PACKAGER")) {
|
if (getenv("CI_USE_PACKAGER")) {
|
||||||
_scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]];
|
_scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]];
|
||||||
RCTAssert(_scriptURL != nil, @"No scriptURL set");
|
|
||||||
} else {
|
} else {
|
||||||
_scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"];
|
_scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||||
RCTAssert(_scriptURL != nil, @"Could not locate main.jsBundle");
|
|
||||||
}
|
}
|
||||||
|
RCTAssert(_scriptURL != nil, @"No scriptURL set");
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ class MessageQueue {
|
||||||
[
|
[
|
||||||
'invokeCallbackAndReturnFlushedQueue',
|
'invokeCallbackAndReturnFlushedQueue',
|
||||||
'callFunctionReturnFlushedQueue',
|
'callFunctionReturnFlushedQueue',
|
||||||
|
'callFunction',
|
||||||
'flushedQueue',
|
'flushedQueue',
|
||||||
].forEach((fn) => (this[fn] = this[fn].bind(this)));
|
].forEach((fn) => (this[fn] = this[fn].bind(this)));
|
||||||
|
|
||||||
|
@ -98,6 +99,16 @@ class MessageQueue {
|
||||||
return this.flushedQueue();
|
return this.flushedQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callFunction(module, method, args) {
|
||||||
|
let result;
|
||||||
|
guard(() => {
|
||||||
|
result = this.__callFunction(module, method, args);
|
||||||
|
this.__callImmediates();
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
invokeCallbackAndReturnFlushedQueue(cbID, args) {
|
invokeCallbackAndReturnFlushedQueue(cbID, args) {
|
||||||
guard(() => {
|
guard(() => {
|
||||||
this.__invokeCallback(cbID, args);
|
this.__invokeCallback(cbID, args);
|
||||||
|
@ -190,8 +201,9 @@ class MessageQueue {
|
||||||
'Module %s is not a registered callable module.',
|
'Module %s is not a registered callable module.',
|
||||||
module
|
module
|
||||||
);
|
);
|
||||||
moduleMethods[method].apply(moduleMethods, args);
|
const result = moduleMethods[method].apply(moduleMethods, args);
|
||||||
Systrace.endEvent();
|
Systrace.endEvent();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
__invokeCallback(cbID, args) {
|
__invokeCallback(cbID, args) {
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#import "RCTWebSocketModule.h"
|
#import "RCTWebSocketModule.h"
|
||||||
|
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
#import "RCTConvert.h"
|
#import "RCTConvert.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
|
|
|
@ -589,7 +589,24 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
queue:(dispatch_queue_t)queue
|
queue:(dispatch_queue_t)queue
|
||||||
{
|
{
|
||||||
if (queue == RCTJSThread) {
|
if (queue == RCTJSThread) {
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
__weak __typeof(self) weakSelf = self;
|
||||||
|
RCTProfileBeginFlowEvent();
|
||||||
|
RCTAssert(_javaScriptExecutor != nil, @"Need JS executor to schedule JS work");
|
||||||
|
|
||||||
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
|
RCTProfileEndFlowEvent();
|
||||||
|
|
||||||
|
RCTBatchedBridge *strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strongSelf.loading) {
|
||||||
|
[strongSelf->_pendingCalls addObject:block];
|
||||||
|
} else {
|
||||||
|
block();
|
||||||
|
}
|
||||||
|
}];
|
||||||
} else if (queue) {
|
} else if (queue) {
|
||||||
dispatch_async(queue, block);
|
dispatch_async(queue, block);
|
||||||
}
|
}
|
||||||
|
@ -680,32 +697,19 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
*/
|
*/
|
||||||
|
|
||||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTBatchedBridge enqueueJSCall:]", nil);
|
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTBatchedBridge enqueueJSCall:]", nil);
|
||||||
|
if (!_valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
NSArray<NSString *> *ids = [moduleDotMethod componentsSeparatedByString:@"."];
|
NSArray<NSString *> *ids = [moduleDotMethod componentsSeparatedByString:@"."];
|
||||||
|
|
||||||
NSString *module = ids[0];
|
NSString *module = ids[0];
|
||||||
NSString *method = ids[1];
|
NSString *method = ids[1];
|
||||||
|
|
||||||
RCTProfileBeginFlowEvent();
|
__weak __typeof(self) weakSelf = self;
|
||||||
|
[self dispatchBlock:^{
|
||||||
__weak RCTBatchedBridge *weakSelf = self;
|
[weakSelf _actuallyInvokeAndProcessModule:module method:method arguments:args ?: @[]];
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
} queue:RCTJSThread];
|
||||||
RCTProfileEndFlowEvent();
|
|
||||||
|
|
||||||
RCTBatchedBridge *strongSelf = weakSelf;
|
|
||||||
if (!strongSelf || !strongSelf.valid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strongSelf.loading) {
|
|
||||||
dispatch_block_t pendingCall = ^{
|
|
||||||
[weakSelf _actuallyInvokeAndProcessModule:module method:method arguments:args ?: @[]];
|
|
||||||
};
|
|
||||||
[strongSelf->_pendingCalls addObject:pendingCall];
|
|
||||||
} else {
|
|
||||||
[strongSelf _actuallyInvokeAndProcessModule:module method:method arguments:args ?: @[]];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil);
|
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil);
|
||||||
}
|
}
|
||||||
|
@ -718,27 +722,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
/**
|
/**
|
||||||
* AnyThread
|
* AnyThread
|
||||||
*/
|
*/
|
||||||
|
if (!_valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RCTProfileBeginFlowEvent();
|
__weak __typeof(self) weakSelf = self;
|
||||||
|
[self dispatchBlock:^{
|
||||||
__weak RCTBatchedBridge *weakSelf = self;
|
[weakSelf _actuallyInvokeCallback:cbID arguments:args];
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
} queue:RCTJSThread];
|
||||||
RCTProfileEndFlowEvent();
|
|
||||||
|
|
||||||
RCTBatchedBridge *strongSelf = weakSelf;
|
|
||||||
if (!strongSelf || !strongSelf.valid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strongSelf.loading) {
|
|
||||||
dispatch_block_t pendingCall = ^{
|
|
||||||
[weakSelf _actuallyInvokeCallback:cbID arguments:args ?: @[]];
|
|
||||||
};
|
|
||||||
[strongSelf->_pendingCalls addObject:pendingCall];
|
|
||||||
} else {
|
|
||||||
[strongSelf _actuallyInvokeCallback:cbID arguments:args];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -922,11 +913,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (queue == RCTJSThread) {
|
[self dispatchBlock:block queue:queue];
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
|
||||||
} else if (queue) {
|
|
||||||
dispatch_async(queue, block);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_flowID = callID;
|
_flowID = callID;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
@class RCTModuleData;
|
@class RCTModuleData;
|
||||||
@class RCTPerformanceLogger;
|
@class RCTPerformanceLogger;
|
||||||
|
@protocol RCTJavaScriptExecutor;
|
||||||
|
|
||||||
@interface RCTBridge ()
|
@interface RCTBridge ()
|
||||||
{
|
{
|
||||||
|
@ -67,7 +68,9 @@
|
||||||
@interface RCTBridge (RCTBatchedBridge)
|
@interface RCTBridge (RCTBatchedBridge)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for unit testing, to detect when executor has been invalidated.
|
* Access the underlying JavaScript executor. You can use this in unit tests to detect
|
||||||
|
* when the executor has been invalidated, or when you want to schedule calls on the
|
||||||
|
* JS VM outside of React Native. Use with care!
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, weak, readonly) id<RCTJavaScriptExecutor> javaScriptExecutor;
|
@property (nonatomic, weak, readonly) id<RCTJavaScriptExecutor> javaScriptExecutor;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#import "RCTDefines.h"
|
#import "RCTDefines.h"
|
||||||
#import "RCTFrameUpdate.h"
|
#import "RCTFrameUpdate.h"
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
#import "RCTJavaScriptExecutor.h"
|
|
||||||
|
|
||||||
@class RCTBridge;
|
@class RCTBridge;
|
||||||
@class RCTEventDispatcher;
|
@class RCTEventDispatcher;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
typedef void (^RCTJavaScriptCompleteBlock)(NSError *error);
|
typedef void (^RCTJavaScriptCompleteBlock)(NSError *error);
|
||||||
typedef void (^RCTJavaScriptCallback)(id json, NSError *error);
|
typedef void (^RCTJavaScriptCallback)(id result, NSError *error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstracts away a JavaScript execution context - we may be running code in a
|
* Abstracts away a JavaScript execution context - we may be running code in a
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#import "RCTModuleData.h"
|
#import "RCTModuleData.h"
|
||||||
|
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTBridge+Private.h"
|
#import "RCTBridge+Private.h"
|
||||||
#import "RCTModuleMethod.h"
|
#import "RCTModuleMethod.h"
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#import "RCTJavaScriptExecutor.h"
|
#import "RCTJavaScriptExecutor.h"
|
||||||
|
|
||||||
|
typedef void (^RCTJavaScriptValueCallback)(JSValue *result, NSError *error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default name for the JS thread
|
* Default name for the JS thread
|
||||||
*/
|
*/
|
||||||
|
@ -75,4 +77,15 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification;
|
||||||
JSContext:(JSContext **)JSContext
|
JSContext:(JSContext **)JSContext
|
||||||
error:(NSError **)error;
|
error:(NSError **)error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the given module/method directly. The completion block will be called with the
|
||||||
|
* JSValue returned by the JS context.
|
||||||
|
*
|
||||||
|
* Currently this does not flush the JS-to-native message queue.
|
||||||
|
*/
|
||||||
|
- (void)callFunctionOnModule:(NSString *)module
|
||||||
|
method:(NSString *)method
|
||||||
|
arguments:(NSArray *)args
|
||||||
|
jsValueCallback:(RCTJavaScriptValueCallback)onComplete;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -595,16 +595,29 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
|
||||||
- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
|
- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
|
||||||
{
|
{
|
||||||
// TODO: Make this function handle first class instead of dynamically dispatching it. #9317773
|
// TODO: Make this function handle first class instead of dynamically dispatching it. #9317773
|
||||||
[self _executeJSCall:@"flushedQueue" arguments:@[] callback:onComplete];
|
[self _executeJSCall:@"flushedQueue" arguments:@[] unwrapResult:YES callback:onComplete];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)callFunctionOnModule:(NSString *)module
|
- (void)_callFunctionOnModule:(NSString *)module
|
||||||
method:(NSString *)method
|
method:(NSString *)method
|
||||||
arguments:(NSArray *)args
|
arguments:(NSArray *)args
|
||||||
callback:(RCTJavaScriptCallback)onComplete
|
flushQueue:(BOOL)flushQueue
|
||||||
|
unwrapResult:(BOOL)unwrapResult
|
||||||
|
callback:(RCTJavaScriptCallback)onComplete
|
||||||
{
|
{
|
||||||
// TODO: Make this function handle first class instead of dynamically dispatching it. #9317773
|
// TODO: Make this function handle first class instead of dynamically dispatching it. #9317773
|
||||||
[self _executeJSCall:@"callFunctionReturnFlushedQueue" arguments:@[module, method, args] callback:onComplete];
|
NSString *bridgeMethod = flushQueue ? @"callFunctionReturnFlushedQueue" : @"callFunction";
|
||||||
|
[self _executeJSCall:bridgeMethod arguments:@[module, method, args] unwrapResult:unwrapResult callback:onComplete];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)callFunctionOnModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args callback:(RCTJavaScriptCallback)onComplete
|
||||||
|
{
|
||||||
|
[self _callFunctionOnModule:module method:method arguments:args flushQueue:YES unwrapResult:YES callback:onComplete];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)callFunctionOnModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args jsValueCallback:(RCTJavaScriptValueCallback)onComplete
|
||||||
|
{
|
||||||
|
[self _callFunctionOnModule:module method:method arguments:args flushQueue:NO unwrapResult:NO callback:onComplete];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invokeCallbackID:(NSNumber *)cbID
|
- (void)invokeCallbackID:(NSNumber *)cbID
|
||||||
|
@ -612,11 +625,12 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
|
||||||
callback:(RCTJavaScriptCallback)onComplete
|
callback:(RCTJavaScriptCallback)onComplete
|
||||||
{
|
{
|
||||||
// TODO: Make this function handle first class instead of dynamically dispatching it. #9317773
|
// TODO: Make this function handle first class instead of dynamically dispatching it. #9317773
|
||||||
[self _executeJSCall:@"invokeCallbackAndReturnFlushedQueue" arguments:@[cbID, args] callback:onComplete];
|
[self _executeJSCall:@"invokeCallbackAndReturnFlushedQueue" arguments:@[cbID, args] unwrapResult:YES callback:onComplete];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_executeJSCall:(NSString *)method
|
- (void)_executeJSCall:(NSString *)method
|
||||||
arguments:(NSArray *)arguments
|
arguments:(NSArray *)arguments
|
||||||
|
unwrapResult:(BOOL)unwrapResult
|
||||||
callback:(RCTJavaScriptCallback)onComplete
|
callback:(RCTJavaScriptCallback)onComplete
|
||||||
{
|
{
|
||||||
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
|
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
|
||||||
|
@ -673,20 +687,16 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
|
||||||
error = RCTNSErrorFromJSError(jscWrapper, contextJSRef, errorJSRef);
|
error = RCTNSErrorFromJSError(jscWrapper, contextJSRef, errorJSRef);
|
||||||
}
|
}
|
||||||
onComplete(nil, error);
|
onComplete(nil, error);
|
||||||
return;
|
} else {
|
||||||
|
id objcValue = nil;
|
||||||
|
// We often return `null` from JS when there is nothing for native side. [JSValue toValue]
|
||||||
|
// returns [NSNull null] in this case, which we don't want.
|
||||||
|
if (!jscWrapper->JSValueIsNull(contextJSRef, resultJSRef)) {
|
||||||
|
JSValue *result = [jscWrapper->JSValue valueWithJSValueRef:resultJSRef inContext:context];
|
||||||
|
objcValue = unwrapResult ? [result toObject] : result;
|
||||||
|
}
|
||||||
|
onComplete(objcValue, nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looks like making lots of JSC API calls is slower than communicating by using a JSON
|
|
||||||
// string. Also it ensures that data stuctures don't have cycles and non-serializable fields.
|
|
||||||
// see [RCTJSCExecutorTests testDeserializationPerf]
|
|
||||||
id objcValue;
|
|
||||||
// We often return `null` from JS when there is nothing for native side. JSONKit takes an extra hundred microseconds
|
|
||||||
// to handle this simple case, so we are adding a shortcut to make executeJSCall method even faster
|
|
||||||
if (!jscWrapper->JSValueIsNull(contextJSRef, resultJSRef)) {
|
|
||||||
objcValue = [[jscWrapper->JSValue valueWithJSValueRef:resultJSRef inContext:context] toObject];
|
|
||||||
}
|
|
||||||
|
|
||||||
onComplete(objcValue, nil);
|
|
||||||
}), 0, @"js_call", (@{@"method": method, @"args": arguments}))];
|
}), 0, @"js_call", (@{@"method": method, @"args": arguments}))];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue