Support sync method calls in the objc bridge

Reviewed By: mhorowitz

Differential Revision: D3801188

fbshipit-source-id: b990680049a46840472a25e66882f8a29890ae90
This commit is contained in:
Pieter De Baets 2016-09-05 07:32:20 -07:00 committed by Facebook Github Bot 2
parent 753b37e479
commit dda3c5f48d
9 changed files with 75 additions and 69 deletions

View File

@ -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);

View File

@ -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

View File

@ -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
*/

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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) {