[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTModuleMethod.h"
#import <objc/message.h>
2015-07-28 20:09:22 +00:00
#import "RCTAssert.h"
2015-12-15 13:39:30 +00:00
#import "RCTBridge+Private.h"
2016-11-23 15:47:52 +00:00
#import "RCTBridge.h"
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
#import "RCTConvert.h"
2017-08-14 12:16:45 +00:00
#import "RCTCxxConvert.h"
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
#import "RCTLog.h"
2017-08-14 12:16:45 +00:00
#import "RCTManagedPointer.h"
2015-12-10 18:09:04 +00:00
#import "RCTParserUtils.h"
2016-09-05 18:11:37 +00:00
#import "RCTProfile.h"
2016-11-23 15:47:52 +00:00
#import "RCTUtils.h"
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2015-08-11 20:18:08 +00:00
typedef BOOL (^RCTArgumentBlock)(RCTBridge *, NSUInteger, id);
2015-07-29 12:54:59 +00:00
@implementation RCTMethodArgument
- (instancetype)initWithType:(NSString *)type
nullability:(RCTNullability)nullability
2015-07-31 13:55:47 +00:00
unused:(BOOL)unused
2015-07-29 12:54:59 +00:00
{
2016-09-05 14:32:19 +00:00
if (self = [super init]) {
2015-07-29 12:54:59 +00:00
_type = [type copy];
_nullability = nullability;
2015-07-31 13:55:47 +00:00
_unused = unused;
2015-07-29 12:54:59 +00:00
}
return self;
}
@end
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
@implementation RCTModuleMethod
{
Class _moduleClass;
2017-07-24 13:46:01 +00:00
const RCTMethodInfo *_methodInfo;
NSString *_JSMethodName;
SEL _selector;
2015-08-06 22:44:15 +00:00
NSInvocation *_invocation;
2015-11-03 22:45:46 +00:00
NSArray<RCTArgumentBlock> *_argumentBlocks;
2017-08-14 12:16:43 +00:00
NSMutableArray *_retainedObjects;
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
}
2015-08-03 11:37:47 +00:00
static void RCTLogArgumentError(RCTModuleMethod *method, NSUInteger index,
id valueOrType, const char *issue)
{
2017-07-24 13:46:01 +00:00
RCTLogError(@"Argument %tu (%@) of %@.%s %s", index, valueOrType,
2015-08-03 11:37:47 +00:00
RCTBridgeModuleNameForClass(method->_moduleClass),
2017-07-24 13:46:01 +00:00
method.JSMethodName, issue);
2015-08-03 11:37:47 +00:00
}
2015-08-24 10:14:33 +00:00
RCT_NOT_IMPLEMENTED(- (instancetype)init)
2015-07-09 23:45:48 +00:00
2017-08-14 12:16:45 +00:00
RCT_EXTERN_C_BEGIN
2015-12-10 18:09:04 +00:00
// returns YES if the selector ends in a colon (indicating that there is at
// least one argument, and maybe more selector parts) or NO if it doesn't.
static BOOL RCTParseSelectorPart(const char **input, NSMutableString *selector)
2015-07-28 20:09:22 +00:00
{
2015-12-10 18:09:04 +00:00
NSString *selectorPart;
2017-08-14 12:16:45 +00:00
if (RCTParseSelectorIdentifier(input, &selectorPart)) {
2015-12-10 18:09:04 +00:00
[selector appendString:selectorPart];
}
RCTSkipWhitespace(input);
if (RCTReadChar(input, ':')) {
[selector appendString:@":"];
RCTSkipWhitespace(input);
return YES;
}
return NO;
}
static BOOL RCTParseUnused(const char **input)
{
return RCTReadString(input, "__unused") ||
RCTReadString(input, "__attribute__((unused))");
}
static RCTNullability RCTParseNullability(const char **input)
{
if (RCTReadString(input, "nullable")) {
return RCTNullable;
} else if (RCTReadString(input, "nonnull")) {
return RCTNonnullable;
}
return RCTNullabilityUnspecified;
}
2016-04-13 00:44:31 +00:00
static RCTNullability RCTParseNullabilityPostfix(const char **input)
{
if (RCTReadString(input, "_Nullable")) {
return RCTNullable;
} else if (RCTReadString(input, "_Nonnull")) {
return RCTNonnullable;
}
return RCTNullabilityUnspecified;
}
2016-12-08 21:31:52 +00:00
// returns YES if execution is safe to proceed (enqueue callback invocation), NO if callback has already been invoked
2017-07-24 13:46:03 +00:00
#if RCT_DEBUG
static BOOL checkCallbackMultipleInvocations(BOOL *didInvoke) {
2017-03-31 12:21:17 +00:00
if (*didInvoke) {
RCTFatal(RCTErrorWithMessage(@"Illegal callback invocation from native module. This callback type only permits a single invocation from native code."));
return NO;
} else {
*didInvoke = YES;
return YES;
}
2016-12-08 21:31:52 +00:00
}
2017-07-24 13:46:03 +00:00
#endif
2016-12-08 21:31:52 +00:00
2017-07-24 13:46:01 +00:00
SEL RCTParseMethodSignature(const char *, NSArray<RCTMethodArgument *> **);
SEL RCTParseMethodSignature(const char *input, NSArray<RCTMethodArgument *> **arguments)
2015-12-10 18:09:04 +00:00
{
RCTSkipWhitespace(&input);
NSMutableArray *args;
NSMutableString *selector = [NSMutableString new];
while (RCTParseSelectorPart(&input, selector)) {
if (!args) {
args = [NSMutableArray new];
}
// Parse type
if (RCTReadChar(&input, '(')) {
RCTSkipWhitespace(&input);
2015-07-28 20:09:22 +00:00
2015-12-10 18:09:04 +00:00
BOOL unused = RCTParseUnused(&input);
RCTSkipWhitespace(&input);
2015-07-28 20:09:22 +00:00
2015-12-10 18:09:04 +00:00
RCTNullability nullability = RCTParseNullability(&input);
RCTSkipWhitespace(&input);
2015-07-28 20:09:22 +00:00
2015-12-10 18:09:04 +00:00
NSString *type = RCTParseType(&input);
2016-04-13 00:44:31 +00:00
RCTSkipWhitespace(&input);
if (nullability == RCTNullabilityUnspecified) {
nullability = RCTParseNullabilityPostfix(&input);
}
2015-12-10 18:09:04 +00:00
[args addObject:[[RCTMethodArgument alloc] initWithType:type
nullability:nullability
unused:unused]];
RCTSkipWhitespace(&input);
RCTReadChar(&input, ')');
RCTSkipWhitespace(&input);
} else {
// Type defaults to id if unspecified
[args addObject:[[RCTMethodArgument alloc] initWithType:@"id"
nullability:RCTNullable
unused:NO]];
}
// Argument name
2017-08-14 12:16:45 +00:00
RCTParseArgumentIdentifier(&input, NULL);
2015-12-10 18:09:04 +00:00
RCTSkipWhitespace(&input);
2015-07-28 20:09:22 +00:00
}
2015-12-10 18:09:04 +00:00
*arguments = [args copy];
return NSSelectorFromString(selector);
2015-07-28 20:09:22 +00:00
}
2017-08-14 12:16:45 +00:00
RCT_EXTERN_C_END
2017-07-24 13:46:01 +00:00
- (instancetype)initWithExportedMethod:(const RCTMethodInfo *)exportedMethod
moduleClass:(Class)moduleClass
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
{
2016-09-05 14:32:19 +00:00
if (self = [super init]) {
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
_moduleClass = moduleClass;
2017-07-24 13:46:01 +00:00
_methodInfo = exportedMethod;
2015-08-11 15:33:28 +00:00
}
return self;
}
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2015-08-11 15:33:28 +00:00
- (void)processMethodSignature
{
2015-11-03 22:45:46 +00:00
NSArray<RCTMethodArgument *> *arguments;
2017-07-24 13:46:01 +00:00
_selector = RCTParseMethodSignature(_methodInfo->objcName, &arguments);
RCTAssert(_selector, @"%s is not a valid selector", _methodInfo->objcName);
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2015-08-11 15:33:28 +00:00
// Create method invocation
NSMethodSignature *methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
2017-07-24 13:46:01 +00:00
RCTAssert(methodSignature, @"%s is not a recognized Objective-C method.", sel_getName(_selector));
2015-08-11 15:33:28 +00:00
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
2015-08-24 10:14:33 +00:00
invocation.selector = _selector;
2015-08-11 15:33:28 +00:00
_invocation = invocation;
2017-08-14 12:16:43 +00:00
NSMutableArray *retainedObjects = [NSMutableArray array];
_retainedObjects = retainedObjects;
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2015-08-11 15:33:28 +00:00
// Process arguments
NSUInteger numberOfArguments = methodSignature.numberOfArguments;
2015-11-03 22:45:46 +00:00
NSMutableArray<RCTArgumentBlock> *argumentBlocks =
[[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2017-07-24 13:46:03 +00:00
#if RCT_DEBUG
__weak RCTModuleMethod *weakSelf = self;
#endif
2017-08-14 12:16:43 +00:00
#define RCT_RETAINED_ARG_BLOCK(_logic) \
2015-08-11 15:33:28 +00:00
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \
2017-07-24 13:46:03 +00:00
_logic \
[invocation setArgument:&value atIndex:(index) + 2]; \
2017-08-14 12:16:43 +00:00
if (value) { \
[retainedObjects addObject:value]; \
} \
2017-07-24 13:46:03 +00:00
return YES; \
}]
2017-08-14 12:16:43 +00:00
#define __PRIMITIVE_CASE(_type, _nullable) { \
isNullableType = _nullable; \
2017-08-14 12:16:45 +00:00
_type (*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend; \
2017-08-14 12:16:43 +00:00
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) { \
_type value = convert([RCTConvert class], selector, json); \
[invocation setArgument:&value atIndex:(index) + 2]; \
return YES; \
}]; \
break; \
2017-07-24 13:46:03 +00:00
}
2015-08-11 15:33:28 +00:00
2017-07-24 13:46:03 +00:00
#define PRIMITIVE_CASE(_type) __PRIMITIVE_CASE(_type, NO)
#define NULLABLE_PRIMITIVE_CASE(_type) __PRIMITIVE_CASE(_type, YES)
2015-10-22 11:02:51 +00:00
2017-08-14 12:16:43 +00:00
// Explicitly copy the block
#define __COPY_BLOCK(block...) \
id value = [block copy]; \
if (value) { \
[retainedObjects addObject:value]; \
} \
2015-08-11 15:33:28 +00:00
2017-07-24 13:46:03 +00:00
#if RCT_DEBUG
2017-08-14 12:16:43 +00:00
#define BLOCK_CASE(_block_args, _block) RCT_RETAINED_ARG_BLOCK( \
2017-07-24 13:46:03 +00:00
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) \
2017-08-14 12:16:43 +00:00
RCT_RETAINED_ARG_BLOCK( __COPY_BLOCK(^_block_args { _block }); )
2017-07-24 13:46:03 +00:00
#endif
2015-08-11 15:33:28 +00:00
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;
2015-12-10 18:09:04 +00:00
SEL selector = RCTConvertSelectorForType(typeName);
2015-08-11 15:33:28 +00:00
if ([RCTConvert respondsToSelector:selector]) {
switch (objcType[0]) {
2017-07-24 13:46:03 +00:00
// 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 *)
2015-10-22 11:02:51 +00:00
case _C_ID: {
isNullableType = YES;
2017-08-14 12:16:45 +00:00
id (*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
2017-08-14 12:16:43 +00:00
RCT_RETAINED_ARG_BLOCK(
2015-10-22 11:02:51 +00:00
id value = convert([RCTConvert class], selector, json);
2017-07-24 13:46:03 +00:00
);
2015-10-22 11:02:51 +00:00
break;
}
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2015-08-11 15:33:28 +00:00
case _C_STRUCT_B: {
NSMethodSignature *typeSignature = [RCTConvert methodSignatureForSelector:selector];
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
2015-08-24 10:14:33 +00:00
typeInvocation.selector = selector;
typeInvocation.target = [RCTConvert class];
2015-08-06 22:44:15 +00:00
2015-08-18 13:55:25 +00:00
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) {
2015-08-11 15:33:28 +00:00
void *returnValue = malloc(typeSignature.methodReturnLength);
[typeInvocation setArgument:&json atIndex:2];
[typeInvocation invoke];
[typeInvocation getReturnValue:returnValue];
[invocation setArgument:returnValue atIndex:index + 2];
free(returnValue);
2015-08-18 13:55:25 +00:00
return YES;
2015-08-11 15:33:28 +00:00
}];
break;
}
2015-07-07 15:47:23 +00:00
2015-08-11 15:33:28 +00:00
default: {
2017-08-14 12:16:45 +00:00
static const char *blockType = @encode(__typeof__(^{}));
2015-08-11 15:33:28 +00:00
if (!strcmp(objcType, blockType)) {
2017-07-24 13:46:03 +00:00
BLOCK_CASE((NSArray *args), {
[bridge enqueueCallback:json args:args];
});
2015-08-11 15:33:28 +00:00
} else {
RCTLogError(@"Unsupported argument type '%@' in method %@.",
typeName, [self methodName]);
2015-07-31 13:55:47 +00:00
}
2015-07-29 12:54:59 +00:00
}
2015-08-11 15:33:28 +00:00
}
} else if ([typeName isEqualToString:@"RCTResponseSenderBlock"]) {
2017-07-24 13:46:03 +00:00
BLOCK_CASE((NSArray *args), {
[bridge enqueueCallback:json args:args];
});
2015-08-11 15:33:28 +00:00
} else if ([typeName isEqualToString:@"RCTResponseErrorBlock"]) {
2017-07-24 13:46:03 +00:00
BLOCK_CASE((NSError *error), {
[bridge enqueueCallback:json args:@[RCTJSErrorFromNSError(error)]];
});
2015-08-11 15:33:28 +00:00
} else if ([typeName isEqualToString:@"RCTPromiseResolveBlock"]) {
RCTAssert(i == numberOfArguments - 2,
2017-07-24 13:46:01 +00:00
@"The RCTPromiseResolveBlock must be the second to last parameter in %@",
[self methodName]);
2017-07-24 13:46:03 +00:00
BLOCK_CASE((id result), {
[bridge enqueueCallback:json args:result ? @[result] : @[]];
});
2015-08-11 15:33:28 +00:00
} else if ([typeName isEqualToString:@"RCTPromiseRejectBlock"]) {
RCTAssert(i == numberOfArguments - 1,
2017-07-24 13:46:01 +00:00
@"The RCTPromiseRejectBlock must be the last parameter in %@",
[self methodName]);
2017-07-24 13:46:03 +00:00
BLOCK_CASE((NSString *code, NSString *message, NSError *error), {
NSDictionary *errorJSON = RCTJSErrorFromCodeMessageAndNSError(code, message, error);
[bridge enqueueCallback:json args:@[errorJSON]];
});
2017-08-14 12:16:45 +00:00
} else if ([typeName hasPrefix:@"JS::"]) {
NSString *selectorNameForCxxType =
[[typeName stringByReplacingOccurrencesOfString:@"::" withString:@"_"]
stringByAppendingString:@":"];
selector = NSSelectorFromString(selectorNameForCxxType);
[argumentBlocks addObject:^(__unused RCTBridge *bridge, NSUInteger index, id json) {
RCTManagedPointer *(*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
RCTManagedPointer *box = convert([RCTCxxConvert class], selector, json);
void *pointer = box.voidPointer;
[invocation setArgument:&pointer atIndex:index + 2];
[retainedObjects addObject:box];
return YES;
}];
2015-08-11 15:33:28 +00:00
} else {
// Unknown argument type
2017-07-24 13:46:03 +00:00
RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert to support this type.",
typeName, [self methodName]);
2015-08-11 15:33:28 +00:00
}
2015-07-31 13:55:47 +00:00
2017-07-24 13:46:03 +00:00
#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.");
2015-08-11 15:33:28 +00:00
}
2017-07-24 13:46:03 +00:00
nullability = RCTNonnullable;
}
2015-07-31 13:55:47 +00:00
2017-07-24 13:46:03 +00:00
/**
* 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);
2015-07-29 12:54:59 +00:00
}
2017-07-24 13:46:03 +00:00
nullability = RCTNonnullable;
}
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2017-07-24 13:46:03 +00:00
if (nullability == RCTNonnullable) {
RCTArgumentBlock oldBlock = argumentBlocks[i - 2];
argumentBlocks[i - 2] = ^(RCTBridge *bridge, NSUInteger index, id json) {
if (json != nil) {
if (!oldBlock(bridge, index, json)) {
return NO;
}
if (isNullableType) {
// Check converted value wasn't null either, as method probably
// won't gracefully handle a nil vallue for a nonull argument
void *value;
[invocation getArgument:&value atIndex:index + 2];
if (value == NULL) {
2015-10-29 12:13:58 +00:00
return NO;
}
2015-08-11 15:33:28 +00:00
}
2017-07-24 13:46:03 +00:00
return YES;
}
RCTLogArgumentError(weakSelf, index, typeName, "must not be null");
return NO;
};
2015-08-11 15:33:28 +00:00
}
2017-07-24 13:46:03 +00:00
#endif
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
}
2017-07-24 13:46:03 +00:00
#if RCT_DEBUG
const char *objcType = _invocation.methodSignature.methodReturnType;
if (_methodInfo->isSync && objcType[0] != _C_ID) {
RCTLogError(@"Return type of %@.%s should be (id) as the method is \"sync\"",
RCTBridgeModuleNameForClass(_moduleClass), self.JSMethodName);
2017-04-27 18:49:49 +00:00
}
2017-07-24 13:46:03 +00:00
#endif
2017-04-27 18:49:49 +00:00
2017-07-24 13:46:03 +00:00
_argumentBlocks = argumentBlocks;
2015-08-11 15:33:28 +00:00
}
- (SEL)selector
{
if (_selector == NULL) {
2016-09-05 18:11:37 +00:00
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"", (@{ @"module": NSStringFromClass(_moduleClass),
2017-07-24 13:46:01 +00:00
@"method": @(_methodInfo->objcName) }));
2015-08-11 15:33:28 +00:00
[self processMethodSignature];
2016-09-05 18:11:37 +00:00
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
2015-08-11 15:33:28 +00:00
}
return _selector;
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
}
2017-07-24 13:46:01 +00:00
- (const char *)JSMethodName
2015-09-18 22:01:21 +00:00
{
2016-09-05 14:32:19 +00:00
NSString *methodName = _JSMethodName;
2017-07-24 13:46:01 +00:00
if (!methodName) {
const char *jsName = _methodInfo->jsName;
if (jsName && strlen(jsName) > 0) {
methodName = @(jsName);
} else {
methodName = @(_methodInfo->objcName);
NSRange colonRange = [methodName rangeOfString:@":"];
if (colonRange.location != NSNotFound) {
methodName = [methodName substringToIndex:colonRange.location];
}
methodName = [methodName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
RCTAssert(methodName.length, @"%s is not a valid JS function name, please"
" supply an alternative using RCT_REMAP_METHOD()", _methodInfo->objcName);
2016-09-05 14:32:19 +00:00
}
2017-07-24 13:46:01 +00:00
_JSMethodName = methodName;
2016-09-05 14:32:19 +00:00
}
2017-07-24 13:46:01 +00:00
return methodName.UTF8String;
2016-09-05 14:32:19 +00:00
}
- (RCTFunctionType)functionType
{
2017-07-24 13:46:01 +00:00
if (strstr(_methodInfo->objcName, "RCTPromise") != NULL) {
RCTAssert(!_methodInfo->isSync, @"Promises cannot be used in sync functions");
2016-09-05 14:32:19 +00:00
return RCTFunctionTypePromise;
2017-07-24 13:46:01 +00:00
} else if (_methodInfo->isSync) {
2017-04-27 18:49:49 +00:00
return RCTFunctionTypeSync;
2016-09-05 14:32:19 +00:00
} else {
return RCTFunctionTypeNormal;
2015-09-18 22:01:21 +00:00
}
}
2016-09-05 14:32:20 +00:00
- (id)invokeWithBridge:(RCTBridge *)bridge
module:(id)module
arguments:(NSArray *)arguments
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
{
2015-08-11 15:33:28 +00:00
if (_argumentBlocks == nil) {
[self processMethodSignature];
}
2017-07-24 13:46:03 +00:00
#if RCT_DEBUG
// Sanity check
RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \
%@ on a module of class %@", [self methodName], [module class]);
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2017-07-24 13:46:03 +00:00
// Safety check
if (arguments.count != _argumentBlocks.count) {
NSInteger actualCount = arguments.count;
NSInteger expectedCount = _argumentBlocks.count;
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
2017-07-24 13:46:03 +00:00
// Subtract the implicit Promise resolver and rejecter functions for implementations of async functions
if (self.functionType == RCTFunctionTypePromise) {
actualCount -= 2;
expectedCount -= 2;
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
}
2017-07-24 13:46:03 +00:00
RCTLogError(@"%@.%s was called with %zd arguments but expects %zd arguments. "
@"If you haven\'t changed this method yourself, this usually means that "
@"your versions of the native code and JavaScript code are out of sync. "
@"Updating both should make this error go away.",
RCTBridgeModuleNameForClass(_moduleClass), self.JSMethodName,
actualCount, expectedCount);
return nil;
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
}
2017-07-24 13:46:03 +00:00
#endif
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
// Set arguments
NSUInteger index = 0;
for (id json in arguments) {
2015-07-29 12:54:59 +00:00
RCTArgumentBlock block = _argumentBlocks[index];
2015-08-11 20:18:08 +00:00
if (!block(bridge, index, RCTNilIfNull(json))) {
// Invalid argument, abort
2017-07-24 13:46:03 +00:00
RCTLogArgumentError(self, index, json, "could not be processed. Aborting method call.");
2016-09-05 14:32:20 +00:00
return nil;
2015-08-11 20:18:08 +00:00
}
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
index++;
}
// Invoke method
2017-09-06 23:32:49 +00:00
#ifdef RCT_MAIN_THREAD_WATCH_DOG_THRESHOLD
if (RCTIsMainQueue()) {
CFTimeInterval start = CACurrentMediaTime();
[_invocation invokeWithTarget:module];
CFTimeInterval duration = CACurrentMediaTime() - start;
if (duration > RCT_MAIN_THREAD_WATCH_DOG_THRESHOLD) {
RCTLogWarn(
@"Main Thread Watchdog: Invocation of %@ blocked the main thread for %dms. "
"Consider using background-threaded modules and asynchronous calls "
"to spend less time on the main thread and keep the app's UI responsive.",
[self methodName],
(int)(duration * 1000)
);
}
} else {
[_invocation invokeWithTarget:module];
}
#else
2015-08-06 22:44:15 +00:00
[_invocation invokeWithTarget:module];
2017-09-06 23:32:49 +00:00
#endif
2015-10-22 11:02:51 +00:00
index = 2;
2017-08-14 12:16:43 +00:00
[_retainedObjects removeAllObjects];
2016-09-05 14:32:20 +00:00
2017-07-24 13:46:01 +00:00
if (_methodInfo->isSync) {
2017-07-24 13:46:03 +00:00
void *returnValue;
[_invocation getReturnValue:&returnValue];
return (__bridge id)returnValue;
2017-04-27 18:49:49 +00:00
}
2017-07-24 13:46:03 +00:00
return nil;
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
}
- (NSString *)methodName
{
2017-07-24 13:46:03 +00:00
if (!_selector) {
2015-08-11 15:33:28 +00:00
[self processMethodSignature];
}
2017-07-24 13:46:01 +00:00
return [NSString stringWithFormat:@"-[%@ %s]", _moduleClass, sel_getName(_selector)];
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
}
- (NSString *)description
{
2017-07-24 13:46:01 +00:00
return [NSString stringWithFormat:@"<%@: %p; exports %@ as %s(); type: %s>",
2017-04-28 12:32:13 +00:00
[self class], self, [self methodName], self.JSMethodName, RCTFunctionDescriptorFromType(self.functionType)];
[ReactNative] Move module info from bridge to RCTModuleData
Summary:
@public
The info about bridge modules (such as id, name, queue, methods...) was spread
across arrays & dictionaries on the bridge, move it into a specific class.
It also removes a lot of information that was statically cached, and now have
the same lifecycle of the bridge.
Also moved RCTModuleMethod, RCTFrameUpdate and RCTBatchedBridge into it's own
files, for organization sake.
NOTE: This diff seems huge, but most of it was just moving code :)
Test Plan:
Tested UIExplorer & UIExplorer tests, Catalyst, MAdMan and Groups. Everything
looks fine.
2015-06-24 23:34:56 +00:00
}
@end