Support sync method calls in the objc bridge
Reviewed By: mhorowitz Differential Revision: D3801188 fbshipit-source-id: b990680049a46840472a25e66882f8a29890ae90
This commit is contained in:
parent
753b37e479
commit
dda3c5f48d
|
@ -33,6 +33,10 @@
|
|||
} \
|
||||
}
|
||||
|
||||
static const NSUInteger kNameIndex = 0;
|
||||
static const NSUInteger kConstantsIndex = 1;
|
||||
static const NSUInteger kMethodsIndex = 2;
|
||||
|
||||
@interface TestExecutor : NSObject <RCTJavaScriptExecutor>
|
||||
|
||||
@property (nonatomic, readonly, copy) NSMutableDictionary<NSString *, id> *injectedStuff;
|
||||
|
@ -195,10 +199,10 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
|
||||
NSArray *remoteModuleConfig = RCTJSONParse(injectedStuff, NULL)[@"remoteModuleConfig"];
|
||||
[remoteModuleConfig enumerateObjectsUsingBlock:^(id moduleConfig, NSUInteger i, BOOL *stop) {
|
||||
if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[0] isEqualToString:@"TestModule"]) {
|
||||
if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[kNameIndex] isEqualToString:@"TestModule"]) {
|
||||
testModuleID = @(i);
|
||||
testConstants = moduleConfig[1];
|
||||
testMethodID = @([moduleConfig[2] indexOfObject:@"testMethod"]);
|
||||
testConstants = moduleConfig[kConstantsIndex];
|
||||
testMethodID = @([moduleConfig[kMethodsIndex] indexOfObject:@"testMethod"]);
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
@ -221,9 +225,9 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
|
||||
NSArray *remoteModuleConfig = RCTJSONParse(injectedStuff, NULL)[@"remoteModuleConfig"];
|
||||
[remoteModuleConfig enumerateObjectsUsingBlock:^(id moduleConfig, NSUInteger i, __unused BOOL *stop) {
|
||||
if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[0] isEqualToString:@"TestModule"]) {
|
||||
if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[kNameIndex] isEqualToString:@"TestModule"]) {
|
||||
testModuleID = @(i);
|
||||
testMethodID = @([moduleConfig[2] indexOfObject:@"testMethod"]);
|
||||
testMethodID = @([moduleConfig[kMethodsIndex] indexOfObject:@"testMethod"]);
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
@ -234,7 +238,7 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
NSArray *args = @[@1234, @5678, @"stringy", @{@"a": @1}, @42];
|
||||
NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args]];
|
||||
|
||||
[_bridge.batchedBridge handleBuffer:buffer];
|
||||
[_bridge.batchedBridge handleBuffer:buffer batchEnded:YES];
|
||||
|
||||
dispatch_sync(_methodQueue, ^{
|
||||
// clear the queue
|
||||
|
@ -253,9 +257,9 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
|
||||
NSArray *remoteModuleConfig = RCTJSONParse(injectedStuff, NULL)[@"remoteModuleConfig"];
|
||||
[remoteModuleConfig enumerateObjectsUsingBlock:^(id moduleConfig, NSUInteger i, __unused BOOL *stop) {
|
||||
if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[0] isEqualToString:@"UnregisteredTestModule"]) {
|
||||
if ([moduleConfig isKindOfClass:[NSArray class]] && [moduleConfig[kNameIndex] isEqualToString:@"UnregisteredTestModule"]) {
|
||||
testModuleID = @(i);
|
||||
testMethodID = @([moduleConfig[1] indexOfObject:@"testMethod"]);
|
||||
testMethodID = @([moduleConfig[kMethodsIndex] indexOfObject:@"testMethod"]);
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
@ -266,7 +270,7 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
NSArray *args = @[];
|
||||
NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args]];
|
||||
|
||||
[_bridge.batchedBridge handleBuffer:buffer];
|
||||
[_bridge.batchedBridge handleBuffer:buffer batchEnded:YES];
|
||||
|
||||
dispatch_sync(_unregisteredTestModule.methodQueue, ^{
|
||||
XCTAssertTrue(self->_unregisteredTestModule.testMethodCalled);
|
||||
|
|
|
@ -972,10 +972,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
|||
[self.flowIDMapLock unlock];
|
||||
}
|
||||
#endif
|
||||
[self _handleRequestNumber:index
|
||||
moduleID:[moduleIDs[index] integerValue]
|
||||
methodID:[methodIDs[index] integerValue]
|
||||
params:paramsArrays[index]];
|
||||
[self callNativeModule:[moduleIDs[index] integerValue]
|
||||
method:[methodIDs[index] integerValue]
|
||||
params:paramsArrays[index]];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1013,18 +1012,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
|||
}
|
||||
}
|
||||
|
||||
- (BOOL)_handleRequestNumber:(NSUInteger)i
|
||||
moduleID:(NSUInteger)moduleID
|
||||
methodID:(NSUInteger)methodID
|
||||
params:(NSArray *)params
|
||||
- (id)callNativeModule:(NSUInteger)moduleID
|
||||
method:(NSUInteger)methodID
|
||||
params:(NSArray *)params
|
||||
{
|
||||
if (!_valid) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) {
|
||||
RCTLogError(@"Invalid module/method/params tuple for request #%zd", i);
|
||||
return NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
RCTModuleData *moduleData = _moduleDataByID[moduleID];
|
||||
|
@ -1040,7 +1033,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
|||
}
|
||||
|
||||
@try {
|
||||
[method invokeWithBridge:self module:moduleData.instance arguments:params];
|
||||
return [method invokeWithBridge:self module:moduleData.instance arguments:params];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
// Pass on JS exceptions
|
||||
|
@ -1052,9 +1045,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
|||
@"Exception '%@' was thrown while invoking %@ on target %@ with params %@",
|
||||
exception, method.JSMethodName, moduleData.name, params];
|
||||
RCTFatal(RCTErrorWithMessage(message));
|
||||
return nil;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)startProfiling
|
||||
|
|
|
@ -99,17 +99,19 @@
|
|||
- (void)startProfiling;
|
||||
- (void)stopProfiling:(void (^)(NSData *))callback;
|
||||
|
||||
/**
|
||||
* Executes native calls sent by JavaScript. Exposed for testing purposes only
|
||||
*/
|
||||
- (void)handleBuffer:(NSArray<NSArray *> *)buffer;
|
||||
|
||||
/**
|
||||
* Exposed for the RCTJSCExecutor for sending native methods called from
|
||||
* JavaScript in the middle of a batch.
|
||||
*/
|
||||
- (void)handleBuffer:(NSArray<NSArray *> *)buffer batchEnded:(BOOL)hasEnded;
|
||||
|
||||
/**
|
||||
* Synchronously call a specific native module's method and return the result
|
||||
*/
|
||||
- (id)callNativeModule:(NSUInteger)moduleID
|
||||
method:(NSUInteger)methodID
|
||||
params:(NSArray *)params;
|
||||
|
||||
/**
|
||||
* Exposed for the RCTJSCExecutor for lazily loading native modules
|
||||
*/
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
typedef NS_ENUM(NSUInteger, RCTFunctionType) {
|
||||
RCTFunctionTypeNormal,
|
||||
RCTFunctionTypePromise,
|
||||
RCTFunctionTypeSync,
|
||||
};
|
||||
|
||||
@protocol RCTBridgeMethod <NSObject>
|
||||
|
@ -21,8 +22,8 @@ typedef NS_ENUM(NSUInteger, RCTFunctionType) {
|
|||
@property (nonatomic, copy, readonly) NSString *JSMethodName;
|
||||
@property (nonatomic, readonly) RCTFunctionType functionType;
|
||||
|
||||
- (void)invokeWithBridge:(RCTBridge *)bridge
|
||||
module:(id)module
|
||||
arguments:(NSArray *)arguments;
|
||||
- (id)invokeWithBridge:(RCTBridge *)bridge
|
||||
module:(id)module
|
||||
arguments:(NSArray *)arguments;
|
||||
|
||||
@end
|
||||
|
|
|
@ -245,8 +245,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
|||
NSMutableArray<id<RCTBridgeMethod>> *moduleMethods = [NSMutableArray new];
|
||||
|
||||
if ([_moduleClass instancesRespondToSelector:@selector(methodsToExport)]) {
|
||||
[self instance];
|
||||
[moduleMethods addObjectsFromArray:[_instance methodsToExport]];
|
||||
[moduleMethods addObjectsFromArray:[self.instance methodsToExport]];
|
||||
}
|
||||
|
||||
unsigned int methodCount;
|
||||
|
@ -310,29 +309,33 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
|||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData config] %@", _moduleClass], nil);
|
||||
|
||||
NSMutableArray<NSString *> *methods = self.methods.count ? [NSMutableArray new] : nil;
|
||||
NSMutableArray<NSNumber *> *asyncMethods = nil;
|
||||
NSMutableArray<NSNumber *> *promiseMethods = nil;
|
||||
NSMutableArray<NSNumber *> *syncMethods = nil;
|
||||
|
||||
for (id<RCTBridgeMethod> method in self.methods) {
|
||||
if (method.functionType == RCTFunctionTypePromise) {
|
||||
if (!asyncMethods) {
|
||||
asyncMethods = [NSMutableArray new];
|
||||
if (!promiseMethods) {
|
||||
promiseMethods = [NSMutableArray new];
|
||||
}
|
||||
[asyncMethods addObject:@(methods.count)];
|
||||
[promiseMethods addObject:@(methods.count)];
|
||||
}
|
||||
else if (method.functionType == RCTFunctionTypeSync) {
|
||||
if (!syncMethods) {
|
||||
syncMethods = [NSMutableArray new];
|
||||
}
|
||||
[syncMethods addObject:@(methods.count)];
|
||||
}
|
||||
[methods addObject:method.JSMethodName];
|
||||
}
|
||||
|
||||
NSMutableArray *config = [NSMutableArray new];
|
||||
[config addObject:self.name];
|
||||
if (constants.count) {
|
||||
[config addObject:constants];
|
||||
}
|
||||
if (methods) {
|
||||
[config addObject:methods];
|
||||
if (asyncMethods) {
|
||||
[config addObject:asyncMethods];
|
||||
}
|
||||
}
|
||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData config] %@", _moduleClass], nil);
|
||||
NSArray *config = @[
|
||||
self.name,
|
||||
RCTNullIfNil(constants),
|
||||
RCTNullIfNil(methods),
|
||||
RCTNullIfNil(promiseMethods),
|
||||
RCTNullIfNil(syncMethods)
|
||||
];
|
||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, ([NSString stringWithFormat:@"[RCTModuleData config] %@", _moduleClass]), nil);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
|
|
@ -429,9 +429,9 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArray<RCTMethodArgument
|
|||
}
|
||||
}
|
||||
|
||||
- (void)invokeWithBridge:(RCTBridge *)bridge
|
||||
module:(id)module
|
||||
arguments:(NSArray *)arguments
|
||||
- (id)invokeWithBridge:(RCTBridge *)bridge
|
||||
module:(id)module
|
||||
arguments:(NSArray *)arguments
|
||||
{
|
||||
if (_argumentBlocks == nil) {
|
||||
[self processMethodSignature];
|
||||
|
@ -459,7 +459,7 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArray<RCTMethodArgument
|
|||
Updating both should make this error go away.",
|
||||
RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName,
|
||||
actualCount, expectedCount);
|
||||
return;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,7 +471,7 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArray<RCTMethodArgument
|
|||
// Invalid argument, abort
|
||||
RCTLogArgumentError(self, index, json,
|
||||
"could not be processed. Aborting method call.");
|
||||
return;
|
||||
return nil;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
@ -497,6 +497,8 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArray<RCTMethodArgument
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)methodName
|
||||
|
|
|
@ -104,8 +104,8 @@ RCT_EXTERN UIAlertView *__nullable RCTAlertView(NSString *title,
|
|||
RCT_EXTERN NSError *RCTErrorWithMessage(NSString *message);
|
||||
|
||||
// Convert nil values to NSNull, and vice-versa
|
||||
RCT_EXTERN id __nullable RCTNilIfNull(id __nullable value);
|
||||
RCT_EXTERN id RCTNullIfNil(id __nullable value);
|
||||
#define RCTNullIfNil(value) (value ?: (id)kCFNull)
|
||||
#define RCTNilIfNull(value) (value == (id)kCFNull ? nil : value)
|
||||
|
||||
// Convert NaN or infinite values to zero, as these aren't JSON-safe
|
||||
RCT_EXTERN double RCTZeroIfNaN(double value);
|
||||
|
|
|
@ -522,16 +522,6 @@ NSError *RCTErrorWithMessage(NSString *message)
|
|||
return [[NSError alloc] initWithDomain:RCTErrorDomain code:0 userInfo:errorInfo];
|
||||
}
|
||||
|
||||
id RCTNullIfNil(id __nullable value)
|
||||
{
|
||||
return value ?: (id)kCFNull;
|
||||
}
|
||||
|
||||
id __nullable RCTNilIfNull(id __nullable value)
|
||||
{
|
||||
return value == (id)kCFNull ? nil : value;
|
||||
}
|
||||
|
||||
double RCTZeroIfNaN(double value)
|
||||
{
|
||||
return isnan(value) || isinf(value) ? 0 : value;
|
||||
|
|
|
@ -447,6 +447,18 @@ static NSThread *newJavaScriptThread(void)
|
|||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call", nil);
|
||||
};
|
||||
|
||||
context[@"nativeCallSyncHook"] = ^id(NSUInteger module, NSUInteger method, NSArray *args) {
|
||||
RCTJSCExecutor *strongSelf = weakSelf;
|
||||
if (!strongSelf.valid) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeCallSyncHook", nil);
|
||||
id result = [strongSelf->_bridge callNativeModule:module method:method params:args];
|
||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call,config", nil);
|
||||
return result;
|
||||
};
|
||||
|
||||
#if RCT_PROFILE
|
||||
__weak RCTBridge *weakBridge = self->_bridge;
|
||||
context[@"nativeTraceBeginAsyncFlow"] = ^(__unused uint64_t tag, __unused NSString *name, int64_t cookie) {
|
||||
|
|
Loading…
Reference in New Issue