Simplify -[RCTModuleMethod processMethodSignature]
Reviewed By: fromcelticpark Differential Revision: D5397371 fbshipit-source-id: d341d55fd8bd1a67a0980543f8defedfd12b5dd4
This commit is contained in:
Родитель
cb12080179
Коммит
98258b437c
|
@ -103,7 +103,8 @@ static RCTNullability RCTParseNullabilityPostfix(const char **input)
|
|||
}
|
||||
|
||||
// returns YES if execution is safe to proceed (enqueue callback invocation), NO if callback has already been invoked
|
||||
static BOOL RCTCheckCallbackMultipleInvocations(BOOL *didInvoke) {
|
||||
#if RCT_DEBUG
|
||||
static BOOL checkCallbackMultipleInvocations(BOOL *didInvoke) {
|
||||
if (*didInvoke) {
|
||||
RCTFatal(RCTErrorWithMessage(@"Illegal callback invocation from native module. This callback type only permits a single invocation from native code."));
|
||||
return NO;
|
||||
|
@ -112,6 +113,7 @@ static BOOL RCTCheckCallbackMultipleInvocations(BOOL *didInvoke) {
|
|||
return YES;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SEL RCTParseMethodSignature(const char *, NSArray<RCTMethodArgument *> **);
|
||||
SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **arguments)
|
||||
|
@ -190,36 +192,47 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
NSMutableArray<RCTArgumentBlock> *argumentBlocks =
|
||||
[[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
|
||||
|
||||
#if RCT_DEBUG
|
||||
__weak RCTModuleMethod *weakSelf = self;
|
||||
#endif
|
||||
|
||||
#define RCT_ARG_BLOCK(_logic) \
|
||||
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \
|
||||
_logic \
|
||||
[invocation setArgument:&value atIndex:(index) + 2]; \
|
||||
return YES; \
|
||||
}];
|
||||
}]
|
||||
|
||||
/**
|
||||
* Explicitly copy the block and retain it, since NSInvocation doesn't retain them.
|
||||
*/
|
||||
#define RCT_BLOCK_ARGUMENT(block...) \
|
||||
id value = json ? [block copy] : (id)^(__unused NSArray *_){}; \
|
||||
#define __PRIMITIVE_CASE(_type, _nullable) { \
|
||||
isNullableType = _nullable; \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ); \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define PRIMITIVE_CASE(_type) __PRIMITIVE_CASE(_type, NO)
|
||||
#define NULLABLE_PRIMITIVE_CASE(_type) __PRIMITIVE_CASE(_type, YES)
|
||||
|
||||
// Explicitly copy the block and retain it, since NSInvocation doesn't retain them
|
||||
#define __COPY_BLOCK(block...) \
|
||||
id value = [block copy]; \
|
||||
CFBridgingRetain(value)
|
||||
|
||||
__weak RCTModuleMethod *weakSelf = self;
|
||||
void (^addBlockArgument)(void) = ^{
|
||||
RCT_ARG_BLOCK(
|
||||
if (RCT_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) {
|
||||
RCTLogArgumentError(weakSelf, index, json, "should be a function");
|
||||
return NO;
|
||||
}
|
||||
|
||||
__block BOOL didInvoke = NO;
|
||||
RCT_BLOCK_ARGUMENT(^(NSArray *args) {
|
||||
if (RCTCheckCallbackMultipleInvocations(&didInvoke)) {
|
||||
[bridge enqueueCallback:json args:args];
|
||||
}
|
||||
});
|
||||
#if RCT_DEBUG
|
||||
#define BLOCK_CASE(_block_args, _block) RCT_ARG_BLOCK( \
|
||||
if (json && ![json isKindOfClass:[NSNumber class]]) { \
|
||||
RCTLogArgumentError(weakSelf, index, json, "should be a function"); \
|
||||
return NO; \
|
||||
} \
|
||||
__block BOOL didInvoke = NO; \
|
||||
__COPY_BLOCK(^_block_args { \
|
||||
if (checkCallbackMultipleInvocations(&didInvoke)) _block \
|
||||
}); \
|
||||
)
|
||||
};
|
||||
#else
|
||||
#define BLOCK_CASE(_block_args, _block) \
|
||||
RCT_ARG_BLOCK( __COPY_BLOCK(^_block_args { _block }); )
|
||||
#endif
|
||||
|
||||
for (NSUInteger i = 2; i < numberOfArguments; i++) {
|
||||
const char *objcType = [methodSignature getArgumentTypeAtIndex:i];
|
||||
|
@ -229,39 +242,23 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
SEL selector = RCTConvertSelectorForType(typeName);
|
||||
if ([RCTConvert respondsToSelector:selector]) {
|
||||
switch (objcType[0]) {
|
||||
|
||||
#define RCT_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_CASE(_C_CHR, char)
|
||||
RCT_CASE(_C_UCHR, unsigned char)
|
||||
RCT_CASE(_C_SHT, short)
|
||||
RCT_CASE(_C_USHT, unsigned short)
|
||||
RCT_CASE(_C_INT, int)
|
||||
RCT_CASE(_C_UINT, unsigned int)
|
||||
RCT_CASE(_C_LNG, long)
|
||||
RCT_CASE(_C_ULNG, unsigned long)
|
||||
RCT_CASE(_C_LNG_LNG, long long)
|
||||
RCT_CASE(_C_ULNG_LNG, unsigned long long)
|
||||
RCT_CASE(_C_FLT, float)
|
||||
RCT_CASE(_C_DBL, double)
|
||||
RCT_CASE(_C_BOOL, BOOL)
|
||||
|
||||
#define RCT_NULLABLE_CASE(_value, _type) \
|
||||
case _value: { \
|
||||
isNullableType = YES; \
|
||||
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
|
||||
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_NULLABLE_CASE(_C_SEL, SEL)
|
||||
RCT_NULLABLE_CASE(_C_CHARPTR, const char *)
|
||||
RCT_NULLABLE_CASE(_C_PTR, void *)
|
||||
// Primitives
|
||||
case _C_CHR: PRIMITIVE_CASE(char)
|
||||
case _C_UCHR: PRIMITIVE_CASE(unsigned char)
|
||||
case _C_SHT: PRIMITIVE_CASE(short)
|
||||
case _C_USHT: PRIMITIVE_CASE(unsigned short)
|
||||
case _C_INT: PRIMITIVE_CASE(int)
|
||||
case _C_UINT: PRIMITIVE_CASE(unsigned int)
|
||||
case _C_LNG: PRIMITIVE_CASE(long)
|
||||
case _C_ULNG: PRIMITIVE_CASE(unsigned long)
|
||||
case _C_LNG_LNG: PRIMITIVE_CASE(long long)
|
||||
case _C_ULNG_LNG: PRIMITIVE_CASE(unsigned long long)
|
||||
case _C_FLT: PRIMITIVE_CASE(float)
|
||||
case _C_DBL: PRIMITIVE_CASE(double)
|
||||
case _C_BOOL: PRIMITIVE_CASE(BOOL)
|
||||
case _C_SEL: NULLABLE_PRIMITIVE_CASE(SEL)
|
||||
case _C_CHARPTR: NULLABLE_PRIMITIVE_CASE(const char *)
|
||||
case _C_PTR: NULLABLE_PRIMITIVE_CASE(void *)
|
||||
|
||||
case _C_ID: {
|
||||
isNullableType = YES;
|
||||
|
@ -269,12 +266,11 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
RCT_ARG_BLOCK(
|
||||
id value = convert([RCTConvert class], selector, json);
|
||||
CFBridgingRetain(value);
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case _C_STRUCT_B: {
|
||||
|
||||
NSMethodSignature *typeSignature = [RCTConvert methodSignatureForSelector:selector];
|
||||
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
|
||||
typeInvocation.selector = selector;
|
||||
|
@ -295,7 +291,9 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
default: {
|
||||
static const char *blockType = @encode(typeof(^{}));
|
||||
if (!strcmp(objcType, blockType)) {
|
||||
addBlockArgument();
|
||||
BLOCK_CASE((NSArray *args), {
|
||||
[bridge enqueueCallback:json args:args];
|
||||
});
|
||||
} else {
|
||||
RCTLogError(@"Unsupported argument type '%@' in method %@.",
|
||||
typeName, [self methodName]);
|
||||
|
@ -303,66 +301,35 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
}
|
||||
}
|
||||
} else if ([typeName isEqualToString:@"RCTResponseSenderBlock"]) {
|
||||
addBlockArgument();
|
||||
} else if ([typeName isEqualToString:@"RCTResponseErrorBlock"]) {
|
||||
RCT_ARG_BLOCK(
|
||||
|
||||
if (RCT_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) {
|
||||
RCTLogArgumentError(weakSelf, index, json, "should be a function");
|
||||
return NO;
|
||||
}
|
||||
|
||||
__block BOOL didInvoke = NO;
|
||||
RCT_BLOCK_ARGUMENT(^(NSError *error) {
|
||||
if (RCTCheckCallbackMultipleInvocations(&didInvoke)) {
|
||||
[bridge enqueueCallback:json args:@[RCTJSErrorFromNSError(error)]];
|
||||
}
|
||||
BLOCK_CASE((NSArray *args), {
|
||||
[bridge enqueueCallback:json args:args];
|
||||
});
|
||||
} else if ([typeName isEqualToString:@"RCTResponseErrorBlock"]) {
|
||||
BLOCK_CASE((NSError *error), {
|
||||
[bridge enqueueCallback:json args:@[RCTJSErrorFromNSError(error)]];
|
||||
});
|
||||
)
|
||||
} else if ([typeName isEqualToString:@"RCTPromiseResolveBlock"]) {
|
||||
RCTAssert(i == numberOfArguments - 2,
|
||||
@"The RCTPromiseResolveBlock must be the second to last parameter in %@",
|
||||
[self methodName]);
|
||||
RCT_ARG_BLOCK(
|
||||
if (RCT_DEBUG && ![json isKindOfClass:[NSNumber class]]) {
|
||||
RCTLogArgumentError(weakSelf, index, json, "should be a promise resolver function");
|
||||
return NO;
|
||||
}
|
||||
|
||||
__block BOOL didInvoke = NO;
|
||||
RCT_BLOCK_ARGUMENT(^(id result) {
|
||||
if (RCTCheckCallbackMultipleInvocations(&didInvoke)) {
|
||||
BLOCK_CASE((id result), {
|
||||
[bridge enqueueCallback:json args:result ? @[result] : @[]];
|
||||
}
|
||||
});
|
||||
)
|
||||
} else if ([typeName isEqualToString:@"RCTPromiseRejectBlock"]) {
|
||||
RCTAssert(i == numberOfArguments - 1,
|
||||
@"The RCTPromiseRejectBlock must be the last parameter in %@",
|
||||
[self methodName]);
|
||||
RCT_ARG_BLOCK(
|
||||
if (RCT_DEBUG && ![json isKindOfClass:[NSNumber class]]) {
|
||||
RCTLogArgumentError(weakSelf, index, json, "should be a promise rejecter function");
|
||||
return NO;
|
||||
}
|
||||
|
||||
__block BOOL didInvoke = NO;
|
||||
RCT_BLOCK_ARGUMENT(^(NSString *code, NSString *message, NSError *error) {
|
||||
if (RCTCheckCallbackMultipleInvocations(&didInvoke)) {
|
||||
BLOCK_CASE((NSString *code, NSString *message, NSError *error), {
|
||||
NSDictionary *errorJSON = RCTJSErrorFromCodeMessageAndNSError(code, message, error);
|
||||
[bridge enqueueCallback:json args:@[errorJSON]];
|
||||
}
|
||||
});
|
||||
)
|
||||
} else {
|
||||
|
||||
// Unknown argument type
|
||||
RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert"
|
||||
" to support this type.", typeName, [self methodName]);
|
||||
RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert to support this type.",
|
||||
typeName, [self methodName]);
|
||||
}
|
||||
|
||||
if (RCT_DEBUG) {
|
||||
|
||||
#if RCT_DEBUG
|
||||
RCTNullability nullability = argument.nullability;
|
||||
if (!isNullableType) {
|
||||
if (nullability == RCTNullable) {
|
||||
|
@ -410,17 +377,18 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
return NO;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (RCT_DEBUG) {
|
||||
#if RCT_DEBUG
|
||||
const char *objcType = _invocation.methodSignature.methodReturnType;
|
||||
if (_methodInfo->isSync && objcType[0] != _C_ID)
|
||||
if (_methodInfo->isSync && objcType[0] != _C_ID) {
|
||||
RCTLogError(@"Return type of %@.%s should be (id) as the method is \"sync\"",
|
||||
RCTBridgeModuleNameForClass(_moduleClass), self.JSMethodName);
|
||||
}
|
||||
#endif
|
||||
|
||||
_argumentBlocks = [argumentBlocks copy];
|
||||
_argumentBlocks = argumentBlocks;
|
||||
}
|
||||
|
||||
- (SEL)selector
|
||||
|
@ -476,7 +444,7 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
[self processMethodSignature];
|
||||
}
|
||||
|
||||
if (RCT_DEBUG) {
|
||||
#if RCT_DEBUG
|
||||
// Sanity check
|
||||
RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \
|
||||
%@ on a module of class %@", [self methodName], [module class]);
|
||||
|
@ -500,7 +468,7 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
actualCount, expectedCount);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set arguments
|
||||
NSUInteger index = 0;
|
||||
|
@ -508,8 +476,7 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
RCTArgumentBlock block = _argumentBlocks[index];
|
||||
if (!block(bridge, index, RCTNilIfNull(json))) {
|
||||
// Invalid argument, abort
|
||||
RCTLogArgumentError(self, index, json,
|
||||
"could not be processed. Aborting method call.");
|
||||
RCTLogArgumentError(self, index, json, "could not be processed. Aborting method call.");
|
||||
return nil;
|
||||
}
|
||||
index++;
|
||||
|
@ -537,19 +504,17 @@ SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **ar
|
|||
}
|
||||
}
|
||||
|
||||
id result = nil;
|
||||
if (_methodInfo->isSync) {
|
||||
void *pointer;
|
||||
[_invocation getReturnValue:&pointer];
|
||||
result = (__bridge id)pointer;
|
||||
void *returnValue;
|
||||
[_invocation getReturnValue:&returnValue];
|
||||
return (__bridge id)returnValue;
|
||||
}
|
||||
|
||||
return result;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)methodName
|
||||
{
|
||||
if (_selector == NULL) {
|
||||
if (!_selector) {
|
||||
[self processMethodSignature];
|
||||
}
|
||||
return [NSString stringWithFormat:@"-[%@ %s]", _moduleClass, sel_getName(_selector)];
|
||||
|
|
Загрузка…
Ссылка в новой задаче