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")) {
|
||||
NSString *app = @"IntegrationTests/IntegrationTestsApp";
|
||||
scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]];
|
||||
RCTAssert(scriptURL != nil, @"No scriptURL set");
|
||||
} else {
|
||||
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];
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:60];
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#import "RCTUtils.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
#import "RCTJavaScriptExecutor.h"
|
||||
|
||||
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||
{ \
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#import "RCTBridge+Private.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTJavaScriptExecutor.h"
|
||||
|
||||
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||
{ \
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
|
||||
#import "RCTImageLoader.h"
|
||||
|
||||
#import <libkern/OSAtomic.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ImageIO/ImageIO.h>
|
||||
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTImageUtils.h"
|
||||
|
|
|
@ -47,11 +47,10 @@ static const NSTimeInterval kTestTeardownTimeoutSeconds = 30;
|
|||
|
||||
if (getenv("CI_USE_PACKAGER")) {
|
||||
_scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]];
|
||||
RCTAssert(_scriptURL != nil, @"No scriptURL set");
|
||||
} else {
|
||||
_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;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ class MessageQueue {
|
|||
[
|
||||
'invokeCallbackAndReturnFlushedQueue',
|
||||
'callFunctionReturnFlushedQueue',
|
||||
'callFunction',
|
||||
'flushedQueue',
|
||||
].forEach((fn) => (this[fn] = this[fn].bind(this)));
|
||||
|
||||
|
@ -98,6 +99,16 @@ class MessageQueue {
|
|||
return this.flushedQueue();
|
||||
}
|
||||
|
||||
callFunction(module, method, args) {
|
||||
let result;
|
||||
guard(() => {
|
||||
result = this.__callFunction(module, method, args);
|
||||
this.__callImmediates();
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
invokeCallbackAndReturnFlushedQueue(cbID, args) {
|
||||
guard(() => {
|
||||
this.__invokeCallback(cbID, args);
|
||||
|
@ -190,8 +201,9 @@ class MessageQueue {
|
|||
'Module %s is not a registered callable module.',
|
||||
module
|
||||
);
|
||||
moduleMethods[method].apply(moduleMethods, args);
|
||||
const result = moduleMethods[method].apply(moduleMethods, args);
|
||||
Systrace.endEvent();
|
||||
return result;
|
||||
}
|
||||
|
||||
__invokeCallback(cbID, args) {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#import "RCTWebSocketModule.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
|
|
@ -589,7 +589,24 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
|||
queue:(dispatch_queue_t)queue
|
||||
{
|
||||
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) {
|
||||
dispatch_async(queue, block);
|
||||
}
|
||||
|
@ -680,32 +697,19 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
|||
*/
|
||||
|
||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTBatchedBridge enqueueJSCall:]", nil);
|
||||
if (!_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray<NSString *> *ids = [moduleDotMethod componentsSeparatedByString:@"."];
|
||||
|
||||
NSString *module = ids[0];
|
||||
NSString *method = ids[1];
|
||||
|
||||
RCTProfileBeginFlowEvent();
|
||||
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
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 ?: @[]];
|
||||
}
|
||||
}];
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
[self dispatchBlock:^{
|
||||
[weakSelf _actuallyInvokeAndProcessModule:module method:method arguments:args ?: @[]];
|
||||
} queue:RCTJSThread];
|
||||
|
||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"", nil);
|
||||
}
|
||||
|
@ -718,27 +722,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
|||
/**
|
||||
* AnyThread
|
||||
*/
|
||||
if (!_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCTProfileBeginFlowEvent();
|
||||
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
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];
|
||||
}
|
||||
}];
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
[self dispatchBlock:^{
|
||||
[weakSelf _actuallyInvokeCallback:cbID arguments:args];
|
||||
} queue:RCTJSThread];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -922,11 +913,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
|||
});
|
||||
};
|
||||
|
||||
if (queue == RCTJSThread) {
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||
} else if (queue) {
|
||||
dispatch_async(queue, block);
|
||||
}
|
||||
[self dispatchBlock:block queue:queue];
|
||||
}
|
||||
|
||||
_flowID = callID;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
@class RCTModuleData;
|
||||
@class RCTPerformanceLogger;
|
||||
@protocol RCTJavaScriptExecutor;
|
||||
|
||||
@interface RCTBridge ()
|
||||
{
|
||||
|
@ -67,7 +68,9 @@
|
|||
@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;
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#import "RCTDefines.h"
|
||||
#import "RCTFrameUpdate.h"
|
||||
#import "RCTInvalidating.h"
|
||||
#import "RCTJavaScriptExecutor.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTEventDispatcher;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#import "RCTInvalidating.h"
|
||||
|
||||
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
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#import "RCTModuleData.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTBridge+Private.h"
|
||||
#import "RCTModuleMethod.h"
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#import "RCTJavaScriptExecutor.h"
|
||||
|
||||
typedef void (^RCTJavaScriptValueCallback)(JSValue *result, NSError *error);
|
||||
|
||||
/**
|
||||
* Default name for the JS thread
|
||||
*/
|
||||
|
@ -75,4 +77,15 @@ RCT_EXTERN NSString *const RCTJavaScriptContextCreatedNotification;
|
|||
JSContext:(JSContext **)JSContext
|
||||
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
|
||||
|
|
|
@ -595,16 +595,29 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
|
|||
- (void)flushedQueue:(RCTJavaScriptCallback)onComplete
|
||||
{
|
||||
// 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
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
callback:(RCTJavaScriptCallback)onComplete
|
||||
- (void)_callFunctionOnModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
flushQueue:(BOOL)flushQueue
|
||||
unwrapResult:(BOOL)unwrapResult
|
||||
callback:(RCTJavaScriptCallback)onComplete
|
||||
{
|
||||
// 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
|
||||
|
@ -612,11 +625,12 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
|
|||
callback:(RCTJavaScriptCallback)onComplete
|
||||
{
|
||||
// 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
|
||||
arguments:(NSArray *)arguments
|
||||
unwrapResult:(BOOL)unwrapResult
|
||||
callback:(RCTJavaScriptCallback)onComplete
|
||||
{
|
||||
RCTAssert(onComplete != nil, @"onComplete block should not be nil");
|
||||
|
@ -673,20 +687,16 @@ static void installBasicSynchronousHooksOnContext(JSContext *context)
|
|||
error = RCTNSErrorFromJSError(jscWrapper, contextJSRef, errorJSRef);
|
||||
}
|
||||
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}))];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue