Implemented lazy parsing of method signatures to improve TTI
This commit is contained in:
Родитель
57a6a02dff
Коммит
a5e9f83a0a
|
@ -162,8 +162,8 @@
|
||||||
- (void)testFamilyStyleAndWeight
|
- (void)testFamilyStyleAndWeight
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-UltraLightItalic" size:14];
|
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-LightItalic" size:14];
|
||||||
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Helvetica Neue", @"fontStyle": @"italic", @"fontWeight": @"100"}];
|
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Helvetica Neue", @"fontStyle": @"italic", @"fontWeight": @"300"}];
|
||||||
RCTAssertEqualFonts(expected, result);
|
RCTAssertEqualFonts(expected, result);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
@ -63,9 +63,11 @@ static BOOL RCTLogsError(void (^block)(void))
|
||||||
// Specifying an NSNumber param without nonnull isn't allowed
|
// Specifying an NSNumber param without nonnull isn't allowed
|
||||||
XCTAssertTrue(RCTLogsError(^{
|
XCTAssertTrue(RCTLogsError(^{
|
||||||
NSString *methodName = @"doFooWithNumber:(NSNumber *)n";
|
NSString *methodName = @"doFooWithNumber:(NSNumber *)n";
|
||||||
(void)[[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithObjCMethodName:methodName
|
||||||
JSMethodName:nil
|
JSMethodName:nil
|
||||||
moduleClass:[self class]];
|
moduleClass:[self class]];
|
||||||
|
// Invoke method to trigger parsing
|
||||||
|
[method invokeWithBridge:nil module:self arguments:@[@1]];
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ RCT_NOT_IMPLEMENTED(-init);
|
||||||
[self.methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger idx, __unused BOOL *stop) {
|
[self.methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger idx, __unused BOOL *stop) {
|
||||||
methodconfig[method.JSMethodName] = @{
|
methodconfig[method.JSMethodName] = @{
|
||||||
@"methodID": @(idx),
|
@"methodID": @(idx),
|
||||||
@"type": method.functionKind == RCTJavaScriptFunctionKindAsync ? @"remoteAsync" : @"remote",
|
@"type": method.functionType == RCTFunctionTypePromise ? @"remoteAsync" : @"remote",
|
||||||
};
|
};
|
||||||
}];
|
}];
|
||||||
config[@"methods"] = [methodconfig copy];
|
config[@"methods"] = [methodconfig copy];
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
@class RCTBridge;
|
@class RCTBridge;
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, RCTJavaScriptFunctionKind) {
|
typedef NS_ENUM(NSUInteger, RCTFunctionType) {
|
||||||
RCTJavaScriptFunctionKindNormal,
|
RCTFunctionTypeNormal,
|
||||||
RCTJavaScriptFunctionKindAsync,
|
RCTFunctionTypePromise,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, RCTNullability) {
|
typedef NS_ENUM(NSUInteger, RCTNullability) {
|
||||||
|
@ -35,7 +35,7 @@ typedef NS_ENUM(NSUInteger, RCTNullability) {
|
||||||
@property (nonatomic, copy, readonly) NSString *JSMethodName;
|
@property (nonatomic, copy, readonly) NSString *JSMethodName;
|
||||||
@property (nonatomic, readonly) Class moduleClass;
|
@property (nonatomic, readonly) Class moduleClass;
|
||||||
@property (nonatomic, readonly) SEL selector;
|
@property (nonatomic, readonly) SEL selector;
|
||||||
@property (nonatomic, readonly) RCTJavaScriptFunctionKind functionKind;
|
@property (nonatomic, readonly) RCTFunctionType functionType;
|
||||||
|
|
||||||
- (instancetype)initWithObjCMethodName:(NSString *)objCMethodName
|
- (instancetype)initWithObjCMethodName:(NSString *)objCMethodName
|
||||||
JSMethodName:(NSString *)JSMethodName
|
JSMethodName:(NSString *)JSMethodName
|
||||||
|
|
|
@ -46,9 +46,10 @@ typedef void (^RCTArgumentBlock)(RCTBridge *, NSUInteger, id);
|
||||||
@implementation RCTModuleMethod
|
@implementation RCTModuleMethod
|
||||||
{
|
{
|
||||||
Class _moduleClass;
|
Class _moduleClass;
|
||||||
SEL _selector;
|
|
||||||
NSInvocation *_invocation;
|
NSInvocation *_invocation;
|
||||||
NSArray *_argumentBlocks;
|
NSArray *_argumentBlocks;
|
||||||
|
NSString *_objCMethodName;
|
||||||
|
SEL _selector;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RCTLogArgumentError(RCTModuleMethod *method, NSUInteger index,
|
static void RCTLogArgumentError(RCTModuleMethod *method, NSUInteger index,
|
||||||
|
@ -117,13 +118,8 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
|
||||||
NSArray *arguments;
|
|
||||||
RCTParseObjCMethodName(&objCMethodName, &arguments);
|
|
||||||
|
|
||||||
_moduleClass = moduleClass;
|
_moduleClass = moduleClass;
|
||||||
_selector = NSSelectorFromString(objCMethodName);
|
_objCMethodName = [objCMethodName copy];
|
||||||
RCTAssert(_selector, @"%@ is not a valid selector", objCMethodName);
|
|
||||||
|
|
||||||
_JSMethodName = JSMethodName.length > 0 ? JSMethodName : ({
|
_JSMethodName = JSMethodName.length > 0 ? JSMethodName : ({
|
||||||
NSString *methodName = objCMethodName;
|
NSString *methodName = objCMethodName;
|
||||||
NSRange colonRange = [methodName rangeOfString:@":"];
|
NSRange colonRange = [methodName rangeOfString:@":"];
|
||||||
|
@ -135,26 +131,139 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||||
methodName;
|
methodName;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create method invocation
|
if ([_objCMethodName rangeOfString:@"RCTPromise"].length) {
|
||||||
NSMethodSignature *methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
_functionType = RCTFunctionTypePromise;
|
||||||
RCTAssert(methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName);
|
} else {
|
||||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
_functionType = RCTFunctionTypeNormal;
|
||||||
[invocation setSelector:_selector];
|
}
|
||||||
[invocation retainArguments];
|
}
|
||||||
_invocation = invocation;
|
|
||||||
|
|
||||||
// Process arguments
|
return self;
|
||||||
NSUInteger numberOfArguments = methodSignature.numberOfArguments;
|
}
|
||||||
NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
|
|
||||||
|
- (void)processMethodSignature
|
||||||
|
{
|
||||||
|
NSArray *arguments;
|
||||||
|
NSString *objCMethodName = _objCMethodName;
|
||||||
|
RCTParseObjCMethodName(&objCMethodName, &arguments);
|
||||||
|
|
||||||
|
_selector = NSSelectorFromString(objCMethodName);
|
||||||
|
RCTAssert(_selector, @"%@ is not a valid selector", objCMethodName);
|
||||||
|
|
||||||
|
// Create method invocation
|
||||||
|
NSMethodSignature *methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
||||||
|
RCTAssert(methodSignature, @"%@ is not a recognized Objective-C method.", objCMethodName);
|
||||||
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||||
|
[invocation setSelector:_selector];
|
||||||
|
[invocation retainArguments];
|
||||||
|
_invocation = invocation;
|
||||||
|
|
||||||
|
// Process arguments
|
||||||
|
NSUInteger numberOfArguments = methodSignature.numberOfArguments;
|
||||||
|
NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
|
||||||
|
|
||||||
#define RCT_ARG_BLOCK(_logic) \
|
#define RCT_ARG_BLOCK(_logic) \
|
||||||
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \
|
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \
|
||||||
_logic \
|
_logic \
|
||||||
[invocation setArgument:&value atIndex:(index) + 2]; \
|
[invocation setArgument:&value atIndex:(index) + 2]; \
|
||||||
}];
|
}];
|
||||||
|
|
||||||
__weak RCTModuleMethod *weakSelf = self;
|
__weak RCTModuleMethod *weakSelf = self;
|
||||||
void (^addBlockArgument)(void) = ^{
|
void (^addBlockArgument)(void) = ^{
|
||||||
|
RCT_ARG_BLOCK(
|
||||||
|
|
||||||
|
if (RCT_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) {
|
||||||
|
RCTLogArgumentError(weakSelf, index, json, "should be a function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||||
|
__autoreleasing id value = (json ? ^(NSArray *args) {
|
||||||
|
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||||
|
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||||
|
arguments:@[json, args]];
|
||||||
|
} : ^(__unused NSArray *unused) {});
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (NSUInteger i = 2; i < numberOfArguments; i++) {
|
||||||
|
const char *objcType = [methodSignature getArgumentTypeAtIndex:i];
|
||||||
|
BOOL isNullableType = NO;
|
||||||
|
RCTMethodArgument *argument = arguments[i - 2];
|
||||||
|
NSString *typeName = argument.type;
|
||||||
|
SEL selector = NSSelectorFromString([typeName stringByAppendingString:@":"]);
|
||||||
|
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 *)
|
||||||
|
RCT_NULLABLE_CASE(_C_ID, id)
|
||||||
|
|
||||||
|
case _C_STRUCT_B: {
|
||||||
|
|
||||||
|
NSMethodSignature *typeSignature = [RCTConvert methodSignatureForSelector:selector];
|
||||||
|
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
|
||||||
|
[typeInvocation setSelector:selector];
|
||||||
|
[typeInvocation setTarget:[RCTConvert class]];
|
||||||
|
|
||||||
|
[argumentBlocks addObject:
|
||||||
|
^(__unused RCTBridge *bridge, NSUInteger index, id json) {
|
||||||
|
|
||||||
|
void *returnValue = malloc(typeSignature.methodReturnLength);
|
||||||
|
[typeInvocation setArgument:&json atIndex:2];
|
||||||
|
[typeInvocation invoke];
|
||||||
|
[typeInvocation getReturnValue:returnValue];
|
||||||
|
|
||||||
|
[invocation setArgument:returnValue atIndex:index + 2];
|
||||||
|
|
||||||
|
free(returnValue);
|
||||||
|
}];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
static const char *blockType = @encode(typeof(^{}));
|
||||||
|
if (!strcmp(objcType, blockType)) {
|
||||||
|
addBlockArgument();
|
||||||
|
} else {
|
||||||
|
RCTLogError(@"Unsupported argument type '%@' in method %@.",
|
||||||
|
typeName, [self methodName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ([typeName isEqualToString:@"RCTResponseSenderBlock"]) {
|
||||||
|
addBlockArgument();
|
||||||
|
} else if ([typeName isEqualToString:@"RCTResponseErrorBlock"]) {
|
||||||
RCT_ARG_BLOCK(
|
RCT_ARG_BLOCK(
|
||||||
|
|
||||||
if (RCT_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) {
|
if (RCT_DEBUG && json && ![json isKindOfClass:[NSNumber class]]) {
|
||||||
|
@ -163,202 +272,115 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||||
__autoreleasing id value = (json ? ^(NSArray *args) {
|
__autoreleasing id value = (json ? ^(NSError *error) {
|
||||||
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||||
method:@"invokeCallbackAndReturnFlushedQueue"
|
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||||
arguments:@[json, args]];
|
arguments:@[json, @[RCTJSErrorFromNSError(error)]]];
|
||||||
} : ^(__unused NSArray *unused) {});
|
} : ^(__unused NSError *error) {});
|
||||||
)
|
)
|
||||||
};
|
} else if ([typeName isEqualToString:@"RCTPromiseResolveBlock"]) {
|
||||||
|
RCTAssert(i == numberOfArguments - 2,
|
||||||
for (NSUInteger i = 2; i < numberOfArguments; i++) {
|
@"The RCTPromiseResolveBlock must be the second to last parameter in -[%@ %@]",
|
||||||
const char *objcType = [methodSignature getArgumentTypeAtIndex:i];
|
_moduleClass, objCMethodName);
|
||||||
BOOL isNullableType = NO;
|
RCT_ARG_BLOCK(
|
||||||
RCTMethodArgument *argument = arguments[i - 2];
|
if (RCT_DEBUG && ![json isKindOfClass:[NSNumber class]]) {
|
||||||
NSString *typeName = argument.type;
|
RCTLogArgumentError(weakSelf, index, json, "should be a promise resolver function");
|
||||||
SEL selector = NSSelectorFromString([typeName stringByAppendingString:@":"]);
|
return;
|
||||||
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 *)
|
|
||||||
RCT_NULLABLE_CASE(_C_ID, id)
|
|
||||||
|
|
||||||
case _C_STRUCT_B: {
|
|
||||||
|
|
||||||
NSMethodSignature *typeSignature = [RCTConvert methodSignatureForSelector:selector];
|
|
||||||
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
|
|
||||||
[typeInvocation setSelector:selector];
|
|
||||||
[typeInvocation setTarget:[RCTConvert class]];
|
|
||||||
|
|
||||||
[argumentBlocks addObject:
|
|
||||||
^(__unused RCTBridge *bridge, NSUInteger index, id json) {
|
|
||||||
|
|
||||||
void *returnValue = malloc(typeSignature.methodReturnLength);
|
|
||||||
[typeInvocation setArgument:&json atIndex:2];
|
|
||||||
[typeInvocation invoke];
|
|
||||||
[typeInvocation getReturnValue:returnValue];
|
|
||||||
|
|
||||||
[invocation setArgument:returnValue atIndex:index + 2];
|
|
||||||
|
|
||||||
free(returnValue);
|
|
||||||
}];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
static const char *blockType = @encode(typeof(^{}));
|
|
||||||
if (!strcmp(objcType, blockType)) {
|
|
||||||
addBlockArgument();
|
|
||||||
} else {
|
|
||||||
RCTLogError(@"Unsupported argument type '%@' in method %@.",
|
|
||||||
typeName, [self methodName]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
|
||||||
__autoreleasing id value = (json ? ^(NSError *error) {
|
|
||||||
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
|
||||||
method:@"invokeCallbackAndReturnFlushedQueue"
|
|
||||||
arguments:@[json, @[RCTJSErrorFromNSError(error)]]];
|
|
||||||
} : ^(__unused NSError *error) {});
|
|
||||||
)
|
|
||||||
} else if ([typeName isEqualToString:@"RCTPromiseResolveBlock"]) {
|
|
||||||
RCTAssert(i == numberOfArguments - 2,
|
|
||||||
@"The RCTPromiseResolveBlock must be the second to last parameter in -[%@ %@]",
|
|
||||||
_moduleClass, objCMethodName);
|
|
||||||
RCT_ARG_BLOCK(
|
|
||||||
if (RCT_DEBUG && ![json isKindOfClass:[NSNumber class]]) {
|
|
||||||
RCTLogArgumentError(weakSelf, index, json, "should be a promise resolver function");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
|
||||||
__autoreleasing RCTPromiseResolveBlock value = (^(id result) {
|
|
||||||
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
|
||||||
method:@"invokeCallbackAndReturnFlushedQueue"
|
|
||||||
arguments:@[json, result ? @[result] : @[]]];
|
|
||||||
});
|
|
||||||
)
|
|
||||||
_functionKind = RCTJavaScriptFunctionKindAsync;
|
|
||||||
} else if ([typeName isEqualToString:@"RCTPromiseRejectBlock"]) {
|
|
||||||
RCTAssert(i == numberOfArguments - 1,
|
|
||||||
@"The RCTPromiseRejectBlock must be the last parameter in -[%@ %@]",
|
|
||||||
_moduleClass, objCMethodName);
|
|
||||||
RCT_ARG_BLOCK(
|
|
||||||
if (RCT_DEBUG && ![json isKindOfClass:[NSNumber class]]) {
|
|
||||||
RCTLogArgumentError(weakSelf, index, json, "should be a promise rejecter function");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
|
||||||
__autoreleasing RCTPromiseRejectBlock value = (^(NSError *error) {
|
|
||||||
NSDictionary *errorJSON = RCTJSErrorFromNSError(error);
|
|
||||||
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
|
||||||
method:@"invokeCallbackAndReturnFlushedQueue"
|
|
||||||
arguments:@[json, @[errorJSON]]];
|
|
||||||
});
|
|
||||||
)
|
|
||||||
_functionKind = RCTJavaScriptFunctionKindAsync;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Unknown argument type
|
|
||||||
RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert"
|
|
||||||
" to support this type.", typeName, [self methodName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RCT_DEBUG) {
|
|
||||||
|
|
||||||
RCTNullability nullability = argument.nullability;
|
|
||||||
if (!isNullableType) {
|
|
||||||
if (nullability == RCTNullable) {
|
|
||||||
RCTLogArgumentError(weakSelf, i - 2, typeName, "is marked as "
|
|
||||||
"nullable, but is not a nullable type.");
|
|
||||||
}
|
|
||||||
nullability = RCTNonnullable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||||
* Special case - Numbers are not nullable in Android, so we
|
__autoreleasing RCTPromiseResolveBlock value = (^(id result) {
|
||||||
* don't support this for now. In future we may allow it.
|
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||||
*/
|
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||||
if ([typeName isEqualToString:@"NSNumber"]) {
|
arguments:@[json, result ? @[result] : @[]]];
|
||||||
BOOL unspecified = (nullability == RCTNullabilityUnspecified);
|
});
|
||||||
if (!argument.unused && (nullability == RCTNullable || unspecified)) {
|
)
|
||||||
RCTLogArgumentError(weakSelf, i - 2, typeName,
|
} else if ([typeName isEqualToString:@"RCTPromiseRejectBlock"]) {
|
||||||
[unspecified ? @"has unspecified nullability" : @"is marked as nullable"
|
RCTAssert(i == numberOfArguments - 1,
|
||||||
stringByAppendingString: @" but React requires that all NSNumber "
|
@"The RCTPromiseRejectBlock must be the last parameter in -[%@ %@]",
|
||||||
"arguments are explicitly marked as `nonnull` to ensure "
|
_moduleClass, objCMethodName);
|
||||||
"compatibility with Android."].UTF8String);
|
RCT_ARG_BLOCK(
|
||||||
}
|
if (RCT_DEBUG && ![json isKindOfClass:[NSNumber class]]) {
|
||||||
nullability = RCTNonnullable;
|
RCTLogArgumentError(weakSelf, index, json, "should be a promise rejecter function");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nullability == RCTNonnullable) {
|
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||||
RCTArgumentBlock oldBlock = argumentBlocks[i - 2];
|
__autoreleasing RCTPromiseRejectBlock value = (^(NSError *error) {
|
||||||
argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSUInteger index, id json) {
|
NSDictionary *errorJSON = RCTJSErrorFromNSError(error);
|
||||||
if (json == nil || json == (id)kCFNull) {
|
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||||
RCTLogArgumentError(weakSelf, index, typeName, "must not be null");
|
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||||
id null = nil;
|
arguments:@[json, @[errorJSON]]];
|
||||||
[invocation setArgument:&null atIndex:index + 2];
|
});
|
||||||
} else {
|
)
|
||||||
oldBlock(bridge, index, json);
|
} else {
|
||||||
}
|
|
||||||
};
|
// Unknown argument type
|
||||||
}
|
RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert"
|
||||||
}
|
" to support this type.", typeName, [self methodName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_argumentBlocks = [argumentBlocks copy];
|
if (RCT_DEBUG) {
|
||||||
|
|
||||||
|
RCTNullability nullability = argument.nullability;
|
||||||
|
if (!isNullableType) {
|
||||||
|
if (nullability == RCTNullable) {
|
||||||
|
RCTLogArgumentError(weakSelf, i - 2, typeName, "is marked as "
|
||||||
|
"nullable, but is not a nullable type.");
|
||||||
|
}
|
||||||
|
nullability = RCTNonnullable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special case - Numbers are not nullable in Android, so we
|
||||||
|
* don't support this for now. In future we may allow it.
|
||||||
|
*/
|
||||||
|
if ([typeName isEqualToString:@"NSNumber"]) {
|
||||||
|
BOOL unspecified = (nullability == RCTNullabilityUnspecified);
|
||||||
|
if (!argument.unused && (nullability == RCTNullable || unspecified)) {
|
||||||
|
RCTLogArgumentError(weakSelf, i - 2, typeName,
|
||||||
|
[unspecified ? @"has unspecified nullability" : @"is marked as nullable"
|
||||||
|
stringByAppendingString: @" but React requires that all NSNumber "
|
||||||
|
"arguments are explicitly marked as `nonnull` to ensure "
|
||||||
|
"compatibility with Android."].UTF8String);
|
||||||
|
}
|
||||||
|
nullability = RCTNonnullable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullability == RCTNonnullable) {
|
||||||
|
RCTArgumentBlock oldBlock = argumentBlocks[i - 2];
|
||||||
|
argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSUInteger index, id json) {
|
||||||
|
if (json == nil || json == (id)kCFNull) {
|
||||||
|
RCTLogArgumentError(weakSelf, index, typeName, "must not be null");
|
||||||
|
id null = nil;
|
||||||
|
[invocation setArgument:&null atIndex:index + 2];
|
||||||
|
} else {
|
||||||
|
oldBlock(bridge, index, json);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
_argumentBlocks = [argumentBlocks copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (SEL)selector
|
||||||
|
{
|
||||||
|
if (_selector == NULL) {
|
||||||
|
[self processMethodSignature];
|
||||||
|
}
|
||||||
|
return _selector;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invokeWithBridge:(RCTBridge *)bridge
|
- (void)invokeWithBridge:(RCTBridge *)bridge
|
||||||
module:(id)module
|
module:(id)module
|
||||||
arguments:(NSArray *)arguments
|
arguments:(NSArray *)arguments
|
||||||
{
|
{
|
||||||
|
if (_argumentBlocks == nil) {
|
||||||
|
[self processMethodSignature];
|
||||||
|
}
|
||||||
|
|
||||||
if (RCT_DEBUG) {
|
if (RCT_DEBUG) {
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
|
@ -371,7 +393,7 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||||
NSInteger expectedCount = _argumentBlocks.count;
|
NSInteger expectedCount = _argumentBlocks.count;
|
||||||
|
|
||||||
// Subtract the implicit Promise resolver and rejecter functions for implementations of async functions
|
// Subtract the implicit Promise resolver and rejecter functions for implementations of async functions
|
||||||
if (_functionKind == RCTJavaScriptFunctionKindAsync) {
|
if (_functionType == RCTFunctionTypePromise) {
|
||||||
actualCount -= 2;
|
actualCount -= 2;
|
||||||
expectedCount -= 2;
|
expectedCount -= 2;
|
||||||
}
|
}
|
||||||
|
@ -398,6 +420,9 @@ void RCTParseObjCMethodName(NSString **objCMethodName, NSArray **arguments)
|
||||||
|
|
||||||
- (NSString *)methodName
|
- (NSString *)methodName
|
||||||
{
|
{
|
||||||
|
if (_selector == NULL) {
|
||||||
|
[self processMethodSignature];
|
||||||
|
}
|
||||||
return [NSString stringWithFormat:@"-[%@ %@]", _moduleClass,
|
return [NSString stringWithFormat:@"-[%@ %@]", _moduleClass,
|
||||||
NSStringFromSelector(_selector)];
|
NSStringFromSelector(_selector)];
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче