[ReactNative] Remove RCT_IMPORT_METHOD macro and generate lookup table dynamically

Summary:
@public

This removes the last piece of data that was still stored on the DATA section,
`RCT_IMPORT_METHOD`. JS calls now dynamically populate a lookup table simultaneously
on JS and Native, instead of creating  a mapping at load time.

Test Plan: Everything still runs, tests are green.
This commit is contained in:
Tadeu Zagallo 2015-06-15 13:01:39 -07:00
parent 86dc92d5ab
commit d3065fc2e7
8 changed files with 12 additions and 177 deletions

View File

@ -305,10 +305,12 @@ var MessageQueueMixin = {
return guardReturn(this._callFunction, [moduleID, methodID, params], null, this);
},
_callFunction: function(moduleID, methodID, params) {
var moduleName = this._localModuleIDToModuleName[moduleID];
_callFunction: function(moduleName, methodName, params) {
if (isFinite(moduleName)) {
moduleName = this._localModuleIDToModuleName[moduleName];
methodName = this._localModuleNameToMethodIDToName[moduleName][methodName];
}
var methodName = this._localModuleNameToMethodIDToName[moduleName][methodID];
if (DEBUG_SPY_MODE) {
console.log(
'N->JS: ' + moduleName + '.' + methodName +

View File

@ -75,22 +75,15 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
/**
* This method is used to call functions in the JavaScript application context.
* It is primarily intended for use by modules that require two-way communication
* with the JavaScript code. Method should be registered using the
* RCT_IMPORT_METHOD macro below. Attempting to call a method that has not been
* registered will result in an error. Safe to call from any thread.
* with the JavaScript code. Safe to call from any thread.
*/
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
/**
* This macro is used to register a JS method to be called via the enqueueJSCall
* bridge method. You should place this macro inside any file that uses the
* imported method. If a method has already been registered by another class, it
* is not necessary to register it again, but it is good practice. Registering
* the same method more than once will not result in an error.
* DEPRECATED: Do not use.
*/
#define RCT_IMPORT_METHOD(module, method) \
__attribute__((used, section("__DATA,RCTImport"))) \
static const char *__rct_import_##module##_##method##__ = #module"."#method;
_Pragma("message(\"This macro is no longer required\")")
/**
* URL of the script that was loaded into the bridge.

View File

@ -13,10 +13,6 @@
#import <objc/message.h>
#import <objc/runtime.h>
#import <mach-o/dyld.h>
#import <mach-o/getsect.h>
#import "RCTAssert.h"
#import "RCTContextExecutor.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
@ -54,16 +50,6 @@ typedef NS_ENUM(NSUInteger, RCTJavaScriptFunctionKind) {
RCTJavaScriptFunctionKindAsync,
};
#ifdef __LP64__
typedef struct mach_header_64 *RCTHeaderValue;
typedef struct section_64 RCTHeaderSection;
#define RCTGetSectByNameFromHeader getsectbynamefromheader_64
#else
typedef struct mach_header *RCTHeaderValue;
typedef struct section RCTHeaderSection;
#define RCTGetSectByNameFromHeader getsectbynamefromheader
#endif
#define RCTAssertJSThread() \
RCTAssert(![NSStringFromClass([_javaScriptExecutor class]) isEqualToString:@"RCTContextExecutor"] || \
[[[NSThread currentThread] name] isEqualToString:@"com.facebook.React.JavaScript"], \
@ -114,40 +100,6 @@ NSString *RCTBridgeModuleNameForClass(Class cls)
return name;
}
/**
* This function scans all classes available at runtime and returns an array
* of all JSMethods registered.
*/
static NSArray *RCTJSMethods(void)
{
static NSArray *JSMethods;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableSet *uniqueMethods = [NSMutableSet set];
Dl_info info;
dladdr(&RCTJSMethods, &info);
const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
unsigned long size = 0;
const uint8_t *sectionData = getsectiondata(mach_header, "__DATA", "RCTImport", &size);
if (sectionData) {
for (const uint8_t *addr = sectionData;
addr < sectionData + size;
addr += sizeof(const char **)) {
// Get data entry
NSString *entry = @(*(const char **)addr);
[uniqueMethods addObject:entry];
}
}
JSMethods = [uniqueMethods allObjects];
});
return JSMethods;
}
// TODO: Can we just replace RCTMakeError with this function instead?
static NSDictionary *RCTJSErrorFromNSError(NSError *error)
{
@ -596,80 +548,6 @@ static NSDictionary *RCTRemoteModulesConfig(NSDictionary *modulesByName)
return moduleConfig;
}
/**
* As above, but for local modules/methods, which represent JS classes
* and methods that will be called by the native code via the bridge.
* Structure is essentially the same as for remote modules:
*
* "ModuleName1": {
* "moduleID": 0,
* "methods": {
* "methodName1": {
* "methodID": 0,
* "type": "local"
* },
* "methodName2": {
* "methodID": 1,
* "type": "local"
* },
* etc...
* }
* },
* etc...
*/
static NSMutableDictionary *RCTLocalModuleIDs;
static NSMutableDictionary *RCTLocalMethodIDs;
static NSMutableArray *RCTLocalModuleNames;
static NSMutableArray *RCTLocalMethodNames;
static NSDictionary *RCTLocalModulesConfig()
{
static NSMutableDictionary *localModules;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
RCTLocalModuleIDs = [[NSMutableDictionary alloc] init];
RCTLocalMethodIDs = [[NSMutableDictionary alloc] init];
RCTLocalModuleNames = [[NSMutableArray alloc] init];
RCTLocalMethodNames = [[NSMutableArray alloc] init];
localModules = [[NSMutableDictionary alloc] init];
for (NSString *moduleDotMethod in RCTJSMethods()) {
NSArray *parts = [moduleDotMethod componentsSeparatedByString:@"."];
RCTAssert(parts.count == 2, @"'%@' is not a valid JS method definition - expected 'Module.method' format.", moduleDotMethod);
// Add module if it doesn't already exist
NSString *moduleName = parts[0];
NSDictionary *module = localModules[moduleName];
if (!module) {
module = @{
@"moduleID": @(localModules.count),
@"methods": [[NSMutableDictionary alloc] init]
};
localModules[moduleName] = module;
[RCTLocalModuleNames addObject:moduleName];
}
// Add method if it doesn't already exist
NSString *methodName = parts[1];
NSMutableDictionary *methods = module[@"methods"];
if (!methods[methodName]) {
methods[methodName] = @{
@"methodID": @(methods.count),
@"type": @"local"
};
[RCTLocalMethodNames addObject:methodName];
}
// Add module and method lookup
RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"];
RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"];
}
});
return localModules;
}
@interface RCTFrameUpdate (Private)
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink;
@ -734,12 +612,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
RCTAssertMainThread();
if ((self = [super init])) {
/**
* Pre register modules
*/
RCTLocalModulesConfig();
_bundleURL = bundleURL;
_moduleProvider = block;
_launchOptions = [launchOptions copy];
@ -1052,7 +924,6 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(__unused NSString *)module
// Inject module data into JS context
NSString *configJSON = RCTJSONStringify(@{
@"remoteModuleConfig": RCTRemoteModulesConfig(_modulesByName),
@"localModulesConfig": RCTLocalModulesConfig()
}, NULL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[_javaScriptExecutor injectJSONText:configJSON
@ -1211,13 +1082,6 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(__unused NSString *)module
}];
}
/**
* - TODO (#5906496): When we build a `MessageQueue.m`, handling all the requests could
* cause both a queue of "responses". We would flush them here. However, we
* currently just expect each objc block to handle its own response sending
* using a `RCTResponseSenderBlock`.
*/
#pragma mark - RCTBridge methods
/**
@ -1225,16 +1089,11 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(__unused NSString *)module
*/
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
{
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
NSArray *ids = [moduleDotMethod componentsSeparatedByString:@"."];
[self _invokeAndProcessModule:@"BatchedBridge"
method:@"callFunctionReturnFlushedQueue"
arguments:@[moduleID ?: @0, methodID ?: @0, args ?: @[]]
arguments:@[ids[0], ids[1], args ?: @[]]
context:RCTGetExecutorID(_javaScriptExecutor)];
}
@ -1245,18 +1104,10 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(__unused NSString *)module
{
RCTAssertJSThread();
NSString *moduleDotMethod = @"JSTimersExecution.callTimers";
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
dispatch_block_t block = ^{
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
method:@"callFunctionReturnFlushedQueue"
arguments:@[moduleID, methodID, @[@[timer]]]
arguments:@[@"JSTimersExecution", @"callTimers", @[@[timer]]]
context:RCTGetExecutorID(_javaScriptExecutor)];
};

View File

@ -89,10 +89,6 @@ RCT_EXPORT_MODULE()
return self;
}
RCT_IMPORT_METHOD(RCTNativeAppEventEmitter, emit);
RCT_IMPORT_METHOD(RCTDeviceEventEmitter, emit);
RCT_IMPORT_METHOD(RCTEventEmitter, receiveEvent);
- (void)sendAppEventWithName:(NSString *)name body:(id)body
{
[_bridge enqueueJSCall:@"RCTNativeAppEventEmitter.emit"

View File

@ -119,9 +119,6 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
return YES;
}
RCT_IMPORT_METHOD(AppRegistry, runApplication)
RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer)
- (void)setLoadingView:(UIView *)loadingView
{
_loadingView = loadingView;

View File

@ -149,8 +149,6 @@ typedef NS_ENUM(NSInteger, RCTTouchEventType) {
reactTouch[@"timestamp"] = @(nativeTouch.timestamp * 1000); // in ms, for JS
}
RCT_IMPORT_METHOD(RCTEventEmitter, receiveTouches);
/**
* Constructs information about touch events to send across the serialized
* boundary. This data should be compliant with W3C `Touch` objects. This data

View File

@ -365,7 +365,7 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
JSValueRef moduleJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)requireJSRef, NULL, 1, (const JSValueRef *)&moduleNameJSRef, &errorJSRef);
JSStringRelease(moduleNameJSStringRef);
if (moduleJSRef != NULL && errorJSRef == NULL) {
if (moduleJSRef != NULL && errorJSRef == NULL && !JSValueIsUndefined(contextJSRef, moduleJSRef)) {
// get method
JSStringRef methodNameJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)method);

View File

@ -74,8 +74,6 @@
RCT_EXPORT_MODULE()
RCT_IMPORT_METHOD(JSTimersExecution, callTimers)
- (instancetype)init
{
if ((self = [super init])) {