From cb120801799b60c8c241781099caff8f2b76ebd3 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 24 Jul 2017 06:46:01 -0700 Subject: [PATCH] Replace exported method registration with statically allocated struct Reviewed By: fromcelticpark Differential Revision: D5389383 fbshipit-source-id: 9eb29b254b616574966b43ad24aa880d44589652 --- Libraries/RCTTest/RCTTestRunner.h | 21 ++-- .../RNTesterUnitTests/RCTAllocationTests.m | 22 +++-- .../RCTMethodArgumentTests.m | 30 +++--- ...eMethodTests.m => RCTModuleMethodTests.mm} | 50 +++++----- React/Base/RCTBatchedBridge.mm | 2 +- React/Base/RCTBridgeMethod.h | 2 +- React/Base/RCTBridgeModule.h | 18 +++- React/Base/RCTDefines.h | 4 + React/Base/RCTModuleData.mm | 13 +-- React/Base/RCTModuleMethod.h | 5 +- React/Base/RCTModuleMethod.m | 95 +++++++++---------- React/CxxModule/RCTCxxMethod.mm | 29 +++--- React/CxxModule/RCTNativeModule.mm | 4 +- ReactCommon/cxxreact/CxxModule.h | 5 + ReactCommon/cxxreact/CxxNativeModule.cpp | 4 +- 15 files changed, 161 insertions(+), 143 deletions(-) rename RNTester/RNTesterUnitTests/{RCTModuleMethodTests.m => RCTModuleMethodTests.mm} (75%) diff --git a/Libraries/RCTTest/RCTTestRunner.h b/Libraries/RCTTest/RCTTestRunner.h index d8e909a49..e2def90b2 100644 --- a/Libraries/RCTTest/RCTTestRunner.h +++ b/Libraries/RCTTest/RCTTestRunner.h @@ -15,16 +15,17 @@ #define FB_REFERENCE_IMAGE_DIR "" #endif -#define RCT_RUN_RUNLOOP_WHILE(CONDITION) \ -{ \ - NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5]; \ - while ((CONDITION)) { \ - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; \ - if ([timeout timeIntervalSinceNow] <= 0) { \ - XCTFail(@"Runloop timed out before condition was met"); \ - break; \ - } \ - } \ +#define RCT_RUN_RUNLOOP_WHILE(CONDITION) \ +{ \ + NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:5]; \ + NSRunLoop *runloop = [NSRunLoop mainRunLoop]; \ + while ((CONDITION)) { \ + [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; \ + if ([timeout timeIntervalSinceNow] <= 0) { \ + XCTFail(@"Runloop timed out before condition was met"); \ + break; \ + } \ + } \ } /** diff --git a/RNTester/RNTesterUnitTests/RCTAllocationTests.m b/RNTester/RNTesterUnitTests/RCTAllocationTests.m index 917ca00c9..7cf8a7cc1 100644 --- a/RNTester/RNTesterUnitTests/RCTAllocationTests.m +++ b/RNTester/RNTesterUnitTests/RCTAllocationTests.m @@ -112,9 +112,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a AllocationTestModule *module = [AllocationTestModule new]; @autoreleasepool { RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL - moduleProvider:^{ - return @[module]; - } + moduleProvider:^{ return @[module]; } launchOptions:nil]; XCTAssertTrue(module.isValid, @"AllocationTestModule should be valid"); (void)bridge; @@ -130,12 +128,10 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a @autoreleasepool { AllocationTestModule *module = [AllocationTestModule new]; RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL - moduleProvider:^{ - return @[module]; - } + moduleProvider:^{ return @[module]; } launchOptions:nil]; + XCTAssertNotNil(module, @"AllocationTestModule should have been created"); weakModule = module; - XCTAssertNotNil(weakModule, @"AllocationTestModule should have been created"); (void)bridge; } @@ -145,11 +141,18 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a - (void)testModuleMethodsAreDeallocated { + static RCTMethodInfo methodInfo = { + .objcName = "test:(NSString *)a :(nonnull NSNumber *)b :(RCTResponseSenderBlock)c :(RCTResponseErrorBlock)d", + .jsName = "", + .isSync = false + }; + __weak RCTModuleMethod *weakMethod; @autoreleasepool { - __autoreleasing RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithMethodSignature:@"test:(NSString *)a :(nonnull NSNumber *)b :(RCTResponseSenderBlock)c :(RCTResponseErrorBlock)d" JSMethodName:@"" isSync:NO moduleClass:[AllocationTestModule class]]; - weakMethod = method; + __autoreleasing RCTModuleMethod *method = [[RCTModuleMethod alloc] initWithExportedMethod:&methodInfo + moduleClass:[AllocationTestModule class]]; XCTAssertNotNil(method, @"RCTModuleMethod should have been created"); + weakMethod = method; } RCT_RUN_RUNLOOP_WHILE(weakMethod) @@ -172,7 +175,6 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a #if !TARGET_OS_TV // userInteractionEnabled is true for Apple TV views XCTAssertFalse(rootContentView.userInteractionEnabled, @"RCTContentView should have been invalidated"); #endif - } - (void)testUnderlyingBridgeIsDeallocated diff --git a/RNTester/RNTesterUnitTests/RCTMethodArgumentTests.m b/RNTester/RNTesterUnitTests/RCTMethodArgumentTests.m index ed74c6057..4f6341034 100644 --- a/RNTester/RNTesterUnitTests/RCTMethodArgumentTests.m +++ b/RNTester/RNTesterUnitTests/RCTMethodArgumentTests.m @@ -19,12 +19,12 @@ @implementation RCTMethodArgumentTests -extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes); +extern SEL RCTParseMethodSignature(const char *methodSignature, NSArray **argTypes); - (void)testOneArgument { NSArray *arguments; - NSString *methodSignature = @"foo:(NSInteger)foo"; + const char *methodSignature = "foo:(NSInteger)foo"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); XCTAssertEqual(arguments.count, (NSUInteger)1); @@ -34,7 +34,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testTwoArguments { NSArray *arguments; - NSString *methodSignature = @"foo:(NSInteger)foo bar:(BOOL)bar"; + const char *methodSignature = "foo:(NSInteger)foo bar:(BOOL)bar"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); @@ -45,7 +45,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testSpaces { NSArray *arguments; - NSString *methodSignature = @"foo : (NSInteger)foo bar : (BOOL) bar"; + const char *methodSignature = "foo : (NSInteger)foo bar : (BOOL) bar"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); @@ -56,7 +56,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testNewlines { NSArray *arguments; - NSString *methodSignature = @"foo : (NSInteger)foo\nbar : (BOOL) bar"; + const char *methodSignature = "foo : (NSInteger)foo\nbar : (BOOL) bar"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); @@ -67,7 +67,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testUnnamedArgs { NSArray *arguments; - NSString *methodSignature = @"foo:(NSInteger)foo:(BOOL)bar"; + const char *methodSignature = "foo:(NSInteger)foo:(BOOL)bar"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo::"); XCTAssertEqual(arguments.count, (NSUInteger)2); @@ -78,7 +78,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testUntypedUnnamedArgs { NSArray *arguments; - NSString *methodSignature = @"foo:foo:bar:bar"; + const char *methodSignature = "foo:foo:bar:bar"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:::"); XCTAssertEqual(arguments.count, (NSUInteger)3); @@ -90,7 +90,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testAttributes { NSArray *arguments; - NSString *methodSignature = @"foo:(__attribute__((unused)) NSString *)foo bar:(__unused BOOL)bar"; + const char *methodSignature = "foo:(__attribute__((unused)) NSString *)foo bar:(__unused BOOL)bar"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); @@ -101,7 +101,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testNullability { NSArray *arguments; - NSString *methodSignature = @"foo:(nullable NSString *)foo bar:(nonnull NSNumber *)bar baz:(id)baz"; + const char *methodSignature = "foo:(nullable NSString *)foo bar:(nonnull NSNumber *)bar baz:(id)baz"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:baz:"); XCTAssertEqual(arguments.count, (NSUInteger)3); @@ -116,7 +116,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testSemicolonStripping { NSArray *arguments; - NSString *methodSignature = @"foo:(NSString *)foo bar:(BOOL)bar;"; + const char *methodSignature = "foo:(NSString *)foo bar:(BOOL)bar;"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); @@ -127,7 +127,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testUnused { NSArray *arguments; - NSString *methodSignature = @"foo:(__unused NSString *)foo bar:(NSNumber *)bar"; + const char *methodSignature = "foo:(__unused NSString *)foo bar:(NSNumber *)bar"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:bar:"); XCTAssertEqual(arguments.count, (NSUInteger)2); @@ -140,7 +140,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testGenericArray { NSArray *arguments; - NSString *methodSignature = @"foo:(NSArray *)foo;"; + const char *methodSignature = "foo:(NSArray *)foo;"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); XCTAssertEqual(arguments.count, (NSUInteger)1); @@ -150,7 +150,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testNestedGenericArray { NSArray *arguments; - NSString *methodSignature = @"foo:(NSArray *> *)foo;"; + const char *methodSignature = "foo:(NSArray *> *)foo;"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); XCTAssertEqual(arguments.count, (NSUInteger)1); @@ -160,7 +160,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testGenericSet { NSArray *arguments; - NSString *methodSignature = @"foo:(NSSet *)foo;"; + const char *methodSignature = "foo:(NSSet *)foo;"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); XCTAssertEqual(arguments.count, (NSUInteger)1); @@ -170,7 +170,7 @@ extern SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **argTypes - (void)testGenericDictionary { NSArray *arguments; - NSString *methodSignature = @"foo:(NSDictionary *)foo;"; + const char *methodSignature = "foo:(NSDictionary *)foo;"; SEL selector = RCTParseMethodSignature(methodSignature, &arguments); XCTAssertEqualObjects(NSStringFromSelector(selector), @"foo:"); XCTAssertEqual(arguments.count, (NSUInteger)1); diff --git a/RNTester/RNTesterUnitTests/RCTModuleMethodTests.m b/RNTester/RNTesterUnitTests/RCTModuleMethodTests.mm similarity index 75% rename from RNTester/RNTesterUnitTests/RCTModuleMethodTests.m rename to RNTester/RNTesterUnitTests/RCTModuleMethodTests.mm index b1ac86863..ff0f09afb 100644 --- a/RNTester/RNTesterUnitTests/RCTModuleMethodTests.m +++ b/RNTester/RNTesterUnitTests/RCTModuleMethodTests.mm @@ -37,18 +37,18 @@ static BOOL RCTLogsError(void (^block)(void)) CGRect _s; } -static RCTModuleMethod *buildDefaultMethodWithMethodSignature(NSString *methodSignature) { - return [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature - JSMethodName:nil - isSync:NO - moduleClass:[RCTModuleMethodTests class]]; +static RCTModuleMethod *buildDefaultMethodWithMethodSignature(const char *methodSignature) +{ + // This leaks a RCTMethodInfo, but it's a test, so... + RCTMethodInfo *methodInfo = new RCTMethodInfo {.objcName = methodSignature, .isSync = NO}; + return [[RCTModuleMethod alloc] initWithExportedMethod:methodInfo moduleClass:[RCTModuleMethodTests class]]; } -static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSignature) { - return [[RCTModuleMethod alloc] initWithMethodSignature:methodSignature - JSMethodName:nil - isSync:YES - moduleClass:[RCTModuleMethodTests class]]; +static RCTModuleMethod *buildSyncMethodWithMethodSignature(const char *methodSignature) +{ + // This leaks a RCTMethodInfo, but it's a test, so... + RCTMethodInfo *methodInfo = new RCTMethodInfo {.objcName = methodSignature, .isSync = YES}; + return [[RCTModuleMethod alloc] initWithExportedMethod:methodInfo moduleClass:[RCTModuleMethodTests class]]; } + (NSString *)moduleName { return nil; } @@ -62,7 +62,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna - (void)testNonnull { - NSString *methodSignature = @"doFooWithBar:(nonnull NSString *)bar"; + const char *methodSignature = "doFooWithBar:(nonnull NSString *)bar"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); XCTAssertFalse(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[@"Hello World"]]; @@ -85,7 +85,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna { // Specifying an NSNumber param without nonnull isn't allowed XCTAssertTrue(RCTLogsError(^{ - NSString *methodSignature = @"doFooWithNumber:(NSNumber *)n"; + const char *methodSignature = "doFooWithNumber:(NSNumber *)n"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); // Invoke method to trigger parsing [method invokeWithBridge:nil module:self arguments:@[@1]]; @@ -93,7 +93,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna } { - NSString *methodSignature = @"doFooWithNumber:(nonnull NSNumber *)n"; + const char *methodSignature = "doFooWithNumber:(nonnull NSNumber *)n"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); XCTAssertTrue(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; @@ -101,7 +101,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna } { - NSString *methodSignature = @"doFooWithDouble:(double)n"; + const char *methodSignature = "doFooWithDouble:(double)n"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); XCTAssertTrue(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; @@ -109,7 +109,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna } { - NSString *methodSignature = @"doFooWithInteger:(NSInteger)n"; + const char *methodSignature = "doFooWithInteger:(NSInteger)n"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); XCTAssertTrue(RCTLogsError(^{ [method invokeWithBridge:nil module:self arguments:@[[NSNull null]]]; @@ -119,7 +119,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna - (void)testStructArgument { - NSString *methodSignature = @"doFooWithCGRect:(CGRect)s"; + const char *methodSignature = "doFooWithCGRect:(CGRect)s"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); CGRect r = CGRectMake(10, 20, 30, 40); @@ -129,7 +129,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna - (void)testWhitespaceTolerance { - NSString *methodSignature = @"doFoo : \t (NSString *)foo"; + const char *methodSignature = "doFoo : \t (NSString *)foo"; __block RCTModuleMethod *method; XCTAssertFalse(RCTLogsError(^{ @@ -146,19 +146,19 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna - (void)testFunctionType { { - NSString *methodSignature = @"doFoo"; + const char *methodSignature = "doFoo"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); XCTAssertTrue(method.functionType == RCTFunctionTypeNormal); } { - NSString *methodSignature = @"openURL:(NSURL *)URL resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject"; + const char *methodSignature = "openURL:(NSURL *)URL resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); XCTAssertTrue(method.functionType == RCTFunctionTypePromise); } { - NSString *methodSignature = @"echoString:(NSString *)input"; + const char *methodSignature = "echoString:(NSString *)input"; RCTModuleMethod *method = buildSyncMethodWithMethodSignature(methodSignature); XCTAssertTrue(method.functionType == RCTFunctionTypeSync); } @@ -167,14 +167,14 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna - (void)testReturnsValueForSyncFunction { { - NSString *methodSignature = @"echoString:(NSString *)input"; + const char *methodSignature = "echoString:(NSString *)input"; RCTModuleMethod *method = buildSyncMethodWithMethodSignature(methodSignature); id result = [method invokeWithBridge:nil module:self arguments:@[@"Test String Value"]]; XCTAssertEqualObjects(result, @"Test String Value"); } { - NSString *methodSignature = @"methodThatReturnsNil"; + const char *methodSignature = "methodThatReturnsNil"; RCTModuleMethod *method = buildSyncMethodWithMethodSignature(methodSignature); id result = [method invokeWithBridge:nil module:self arguments:@[]]; XCTAssertNil(result); @@ -183,7 +183,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna - (void)testReturnsNilForDefaultFunction { - NSString *methodSignature = @"doFoo"; + const char *methodSignature = "doFoo"; RCTModuleMethod *method = buildDefaultMethodWithMethodSignature(methodSignature); id result = [method invokeWithBridge:nil module:self arguments:@[]]; XCTAssertNil(result); @@ -192,7 +192,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna - (void)testReturnTypeForSyncFunction { { - NSString *methodSignature = @"methodThatReturnsNil"; + const char *methodSignature = "methodThatReturnsNil"; RCTModuleMethod *method = buildSyncMethodWithMethodSignature(methodSignature); XCTAssertFalse(RCTLogsError(^{ // Invoke method to trigger parsing @@ -201,7 +201,7 @@ static RCTModuleMethod *buildSyncMethodWithMethodSignature(NSString *methodSigna } { - NSString *methodSignature = @"doFoo"; + const char *methodSignature = "doFoo"; RCTModuleMethod *method = buildSyncMethodWithMethodSignature(methodSignature); XCTAssertTrue(RCTLogsError(^{ // Invoke method to trigger parsing diff --git a/React/Base/RCTBatchedBridge.mm b/React/Base/RCTBatchedBridge.mm index 782eda5e3..326546f20 100644 --- a/React/Base/RCTBatchedBridge.mm +++ b/React/Base/RCTBatchedBridge.mm @@ -1067,7 +1067,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR } NSString *message = [NSString stringWithFormat: - @"Exception '%@' was thrown while invoking %@ on target %@ with params %@", + @"Exception '%@' was thrown while invoking %s on target %@ with params %@", exception, method.JSMethodName, moduleData.name, params]; RCTFatal(RCTErrorWithMessage(message)); return nil; diff --git a/React/Base/RCTBridgeMethod.h b/React/Base/RCTBridgeMethod.h index 37ca1bb43..8ffea5396 100644 --- a/React/Base/RCTBridgeMethod.h +++ b/React/Base/RCTBridgeMethod.h @@ -30,7 +30,7 @@ static inline const char *RCTFunctionDescriptorFromType(RCTFunctionType type) { @protocol RCTBridgeMethod -@property (nonatomic, copy, readonly) NSString *JSMethodName; +@property (nonatomic, readonly) const char *JSMethodName; @property (nonatomic, readonly) RCTFunctionType functionType; - (id)invokeWithBridge:(RCTBridge *)bridge diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h index 20febfb1f..3e31621d0 100644 --- a/React/Base/RCTBridgeModule.h +++ b/React/Base/RCTBridgeModule.h @@ -47,7 +47,17 @@ typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError * * NOTE: RCTJSThread is not a real libdispatch queue */ -extern dispatch_queue_t RCTJSThread; +RCT_EXTERN dispatch_queue_t RCTJSThread; + +RCT_EXTERN_C_BEGIN + +typedef struct RCTMethodInfo { + const char *const jsName; + const char *const objcName; + const BOOL isSync; +} RCTMethodInfo; + +RCT_EXTERN_C_END /** * Provides the interface needed to register a bridge module. @@ -248,9 +258,9 @@ RCT_EXTERN void RCTRegisterModule(Class); \ * and also whether this method is synchronous. */ #define _RCT_EXTERN_REMAP_METHOD(js_name, method, is_blocking_synchronous_method) \ - + (NSArray *)RCT_CONCAT(__rct_export__, \ - RCT_CONCAT(js_name, RCT_CONCAT(__LINE__, __COUNTER__))) { \ - return @[@#js_name, @#method, @is_blocking_synchronous_method]; \ + + (const RCTMethodInfo *)RCT_CONCAT(__rct_export__, RCT_CONCAT(js_name, RCT_CONCAT(__LINE__, __COUNTER__))) { \ + static RCTMethodInfo config = {#js_name, #method, is_blocking_synchronous_method}; \ + return &config; \ } /** diff --git a/React/Base/RCTDefines.h b/React/Base/RCTDefines.h index ffb7591f8..b2681e38c 100644 --- a/React/Base/RCTDefines.h +++ b/React/Base/RCTDefines.h @@ -16,8 +16,12 @@ */ #if defined(__cplusplus) #define RCT_EXTERN extern "C" __attribute__((visibility("default"))) +#define RCT_EXTERN_C_BEGIN extern "C" { +#define RCT_EXTERN_C_END } #else #define RCT_EXTERN extern __attribute__((visibility("default"))) +#define RCT_EXTERN_C_BEGIN +#define RCT_EXTERN_C_END #endif /** diff --git a/React/Base/RCTModuleData.mm b/React/Base/RCTModuleData.mm index 3c850148f..ae728a2c5 100644 --- a/React/Base/RCTModuleData.mm +++ b/React/Base/RCTModuleData.mm @@ -270,14 +270,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init); SEL selector = method_getName(method); if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) { IMP imp = method_getImplementation(method); - NSArray *entries = - ((NSArray *(*)(id, SEL))imp)(_moduleClass, selector); - id moduleMethod = - [[RCTModuleMethod alloc] initWithMethodSignature:entries[1] - JSMethodName:entries[0] - isSync:((NSNumber *)entries[2]).boolValue - moduleClass:_moduleClass]; - + auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_moduleClass, selector); + id moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod + moduleClass:_moduleClass]; [moduleMethods addObject:moduleMethod]; } } @@ -345,7 +340,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init); } [syncMethods addObject:@(methods.count)]; } - [methods addObject:method.JSMethodName]; + [methods addObject:@(method.JSMethodName)]; } NSArray *config = @[ diff --git a/React/Base/RCTModuleMethod.h b/React/Base/RCTModuleMethod.h index 3f2800b23..b9ef3f9ff 100644 --- a/React/Base/RCTModuleMethod.h +++ b/React/Base/RCTModuleMethod.h @@ -10,6 +10,7 @@ #import #import +#import #import @class RCTBridge; @@ -27,9 +28,7 @@ @property (nonatomic, readonly) Class moduleClass; @property (nonatomic, readonly) SEL selector; -- (instancetype)initWithMethodSignature:(NSString *)objCMethodName - JSMethodName:(NSString *)JSMethodName - isSync:(BOOL)isSync +- (instancetype)initWithExportedMethod:(const RCTMethodInfo *)exportMethod moduleClass:(Class)moduleClass NS_DESIGNATED_INITIALIZER; @end diff --git a/React/Base/RCTModuleMethod.m b/React/Base/RCTModuleMethod.m index 68f0344b1..df1f02fad 100644 --- a/React/Base/RCTModuleMethod.m +++ b/React/Base/RCTModuleMethod.m @@ -41,21 +41,20 @@ typedef BOOL (^RCTArgumentBlock)(RCTBridge *, NSUInteger, id); @implementation RCTModuleMethod { Class _moduleClass; + const RCTMethodInfo *_methodInfo; + NSString *_JSMethodName; + + SEL _selector; NSInvocation *_invocation; NSArray *_argumentBlocks; - NSString *_methodSignature; - SEL _selector; - BOOL _isSync; } -@synthesize JSMethodName = _JSMethodName; - static void RCTLogArgumentError(RCTModuleMethod *method, NSUInteger index, id valueOrType, const char *issue) { - RCTLogError(@"Argument %tu (%@) of %@.%@ %s", index, valueOrType, + RCTLogError(@"Argument %tu (%@) of %@.%s %s", index, valueOrType, RCTBridgeModuleNameForClass(method->_moduleClass), - method->_JSMethodName, issue); + method.JSMethodName, issue); } RCT_NOT_IMPLEMENTED(- (instancetype)init) @@ -114,10 +113,9 @@ static BOOL RCTCheckCallbackMultipleInvocations(BOOL *didInvoke) { } } -SEL RCTParseMethodSignature(NSString *, NSArray **); -SEL RCTParseMethodSignature(NSString *methodSignature, NSArray **arguments) +SEL RCTParseMethodSignature(const char *, NSArray **); +SEL RCTParseMethodSignature(const char *input, NSArray **arguments) { - const char *input = methodSignature.UTF8String; RCTSkipWhitespace(&input); NSMutableArray *args; @@ -164,30 +162,25 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArray *arguments; - _selector = RCTParseMethodSignature(_methodSignature, &arguments); - RCTAssert(_selector, @"%@ is not a valid selector", _methodSignature); + _selector = RCTParseMethodSignature(_methodInfo->objcName, &arguments); + RCTAssert(_selector, @"%s is not a valid selector", _methodInfo->objcName); // Create method invocation NSMethodSignature *methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector]; - RCTAssert(methodSignature, @"%@ is not a recognized Objective-C method.", _methodSignature); + RCTAssert(methodSignature, @"%s is not a recognized Objective-C method.", sel_getName(_selector)); NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; invocation.selector = _selector; _invocation = invocation; @@ -328,8 +321,8 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArrayisSync && objcType[0] != _C_ID) + RCTLogError(@"Return type of %@.%s should be (id) as the method is \"sync\"", + RCTBridgeModuleNameForClass(_moduleClass), self.JSMethodName); } _argumentBlocks = [argumentBlocks copy]; @@ -434,36 +427,41 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArrayobjcName) })); [self processMethodSignature]; RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); } return _selector; } -- (NSString *)JSMethodName +- (const char *)JSMethodName { NSString *methodName = _JSMethodName; - if (methodName.length == 0) { - methodName = _methodSignature; - NSRange colonRange = [methodName rangeOfString:@":"]; - if (colonRange.location != NSNotFound) { - methodName = [methodName substringToIndex:colonRange.location]; + 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); } - methodName = [methodName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - RCTAssert(methodName.length, @"%@ is not a valid JS function name, please" - " supply an alternative using RCT_REMAP_METHOD()", _methodSignature); + _JSMethodName = methodName; } - return methodName; + return methodName.UTF8String; } - (RCTFunctionType)functionType { - if ([_methodSignature rangeOfString:@"RCTPromise"].length) { - RCTAssert(!_isSync, @"Promises cannot be used in sync functions"); - + if (strstr(_methodInfo->objcName, "RCTPromise") != NULL) { + RCTAssert(!_methodInfo->isSync, @"Promises cannot be used in sync functions"); return RCTFunctionTypePromise; - } else if (_isSync) { + } else if (_methodInfo->isSync) { return RCTFunctionTypeSync; } else { return RCTFunctionTypeNormal; @@ -494,11 +492,11 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArrayisSync) { void *pointer; [_invocation getReturnValue:&pointer]; result = (__bridge id)pointer; @@ -554,13 +552,12 @@ SEL RCTParseMethodSignature(NSString *methodSignature, NSArray", + return [NSString stringWithFormat:@"<%@: %p; exports %@ as %s(); type: %s>", [self class], self, [self methodName], self.JSMethodName, RCTFunctionDescriptorFromType(self.functionType)]; } diff --git a/React/CxxModule/RCTCxxMethod.mm b/React/CxxModule/RCTCxxMethod.mm index 65a5433a4..2c5a9f265 100644 --- a/React/CxxModule/RCTCxxMethod.mm +++ b/React/CxxModule/RCTCxxMethod.mm @@ -26,17 +26,31 @@ using namespace facebook::react; std::unique_ptr _method; } -@synthesize JSMethodName = _JSMethodName; - - (instancetype)initWithCxxMethod:(const CxxModule::Method &)method { if ((self = [super init])) { - _JSMethodName = @(method.name.c_str()); _method = folly::make_unique(method); } return self; } +- (const char *)JSMethodName +{ + return _method->name.c_str(); +} + +- (RCTFunctionType)functionType +{ + std::string type(_method->getType()); + if (type == "sync") { + return RCTFunctionTypeSync; + } else if (type == "async") { + return RCTFunctionTypeNormal; + } else { + return RCTFunctionTypePromise; + } +} + - (id)invokeWithBridge:(RCTBridge *)bridge module:(id)module arguments:(NSArray *)arguments @@ -110,16 +124,9 @@ using namespace facebook::react; } } -- (RCTFunctionType)functionType -{ - // TODO: support promise-style APIs - return _method->syncFunc ? RCTFunctionTypeSync : RCTFunctionTypeNormal; -} - - (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p; name = %@>", - [self class], self, self.JSMethodName]; + return [NSString stringWithFormat:@"<%@: %p; name = %s>", [self class], self, self.JSMethodName]; } @end diff --git a/React/CxxModule/RCTNativeModule.mm b/React/CxxModule/RCTNativeModule.mm index 6d47b1ea6..27b7a50f2 100644 --- a/React/CxxModule/RCTNativeModule.mm +++ b/React/CxxModule/RCTNativeModule.mm @@ -38,7 +38,7 @@ std::vector RCTNativeModule::getMethods() { for (id method in m_moduleData.methods) { descs.emplace_back( - method.JSMethodName.UTF8String, + method.JSMethodName, RCTFunctionDescriptorFromType(method.functionType) ); } @@ -103,7 +103,7 @@ MethodCallResult RCTNativeModule::invokeInner(unsigned int methodId, const folly } NSString *message = [NSString stringWithFormat: - @"Exception '%@' was thrown while invoking %@ on target %@ with params %@", + @"Exception '%@' was thrown while invoking %s on target %@ with params %@", exception, method.JSMethodName, m_moduleData.name, objcParams]; RCTFatal(RCTErrorWithMessage(message)); } diff --git a/ReactCommon/cxxreact/CxxModule.h b/ReactCommon/cxxreact/CxxModule.h index c67f95d82..cf3b95f8b 100644 --- a/ReactCommon/cxxreact/CxxModule.h +++ b/ReactCommon/cxxreact/CxxModule.h @@ -68,6 +68,11 @@ public: std::function syncFunc; + const char *getType() { + assert(func || syncFunc); + return func ? (callbacks == 2 ? "promise" : "async") : "sync"; + } + // std::function/lambda ctors Method(std::string aname, diff --git a/ReactCommon/cxxreact/CxxNativeModule.cpp b/ReactCommon/cxxreact/CxxNativeModule.cpp index c13b53b80..bd4fe385c 100644 --- a/ReactCommon/cxxreact/CxxNativeModule.cpp +++ b/ReactCommon/cxxreact/CxxNativeModule.cpp @@ -57,9 +57,7 @@ std::vector CxxNativeModule::getMethods() { std::vector descs; for (auto& method : methods_) { - assert(method.func || method.syncFunc); - auto methodType = method.func ? (method.callbacks == 2 ? "promise" : "async") : "sync"; - descs.emplace_back(method.name, methodType); + descs.emplace_back(method.name, method.getType()); } return descs; }