[ReactNative] Remove module info from the data section + allow external modules
Summary: @public The information we required about the exported methods were previously stored on the binary's DATA section, which didn't allow to access methods on different static libraries, or in any dynamic library at all. Instead of fetching information from all the DATA segments, this diff changes the macro in order to create a new method, that returns the required information about the original method. The module itself is registered at load time, and on the bridge initialization all the auto-generated methods are called to gather the methods' information. Test Plan: UIExplorer previously had a dependency on `RCTTest`, because it had a `TestModule` that had to be on the same library. `RCTTest` is now a dependency of `UIExplorerIntegrationTests`. So the tests themselves running should test it.
This commit is contained in:
parent
4690983c10
commit
e9095b2f42
|
@ -30,6 +30,7 @@
|
||||||
1497CFB21B21F5E400C1F8F2 /* RCTSparseArrayTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFAA1B21F5E400C1F8F2 /* RCTSparseArrayTests.m */; };
|
1497CFB21B21F5E400C1F8F2 /* RCTSparseArrayTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFAA1B21F5E400C1F8F2 /* RCTSparseArrayTests.m */; };
|
||||||
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */; };
|
1497CFB31B21F5E400C1F8F2 /* RCTUIManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */; };
|
||||||
14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; };
|
14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; };
|
||||||
|
14B6DA821B276C5900BF4DD1 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58005BEE1ABA80530062E044 /* libRCTTest.a */; };
|
||||||
14D6D7111B220EB3001FB087 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D6D7101B220EB3001FB087 /* libOCMock.a */; };
|
14D6D7111B220EB3001FB087 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14D6D7101B220EB3001FB087 /* libOCMock.a */; };
|
||||||
14D6D71E1B2222EF001FB087 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; };
|
14D6D71E1B2222EF001FB087 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; };
|
||||||
14D6D71F1B2222EF001FB087 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; };
|
14D6D71F1B2222EF001FB087 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; };
|
||||||
|
@ -44,7 +45,6 @@
|
||||||
14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; };
|
14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; };
|
||||||
14D6D7291B2222EF001FB087 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; };
|
14D6D7291B2222EF001FB087 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; };
|
||||||
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; };
|
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; };
|
||||||
58005BF21ABA80A60062E044 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58005BEE1ABA80530062E044 /* libRCTTest.a */; };
|
|
||||||
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; };
|
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; };
|
||||||
D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; };
|
D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
@ -229,7 +229,6 @@
|
||||||
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */,
|
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */,
|
||||||
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */,
|
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */,
|
||||||
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */,
|
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */,
|
||||||
58005BF21ABA80A60062E044 /* libRCTTest.a in Frameworks */,
|
|
||||||
134180011AA9153C003F314A /* libRCTText.a in Frameworks */,
|
134180011AA9153C003F314A /* libRCTText.a in Frameworks */,
|
||||||
D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */,
|
D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */,
|
||||||
139FDEDB1B0651FB00C62182 /* libRCTWebSocket.a in Frameworks */,
|
139FDEDB1B0651FB00C62182 /* libRCTWebSocket.a in Frameworks */,
|
||||||
|
@ -240,6 +239,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
14B6DA821B276C5900BF4DD1 /* libRCTTest.a in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -865,7 +865,7 @@
|
||||||
"$(SRCROOT)/../../React/**",
|
"$(SRCROOT)/../../React/**",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = "$(SRCROOT)/UIExplorer/Info.plist";
|
INFOPLIST_FILE = "$(SRCROOT)/UIExplorer/Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited)";
|
||||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
PRODUCT_NAME = UIExplorer;
|
PRODUCT_NAME = UIExplorer;
|
||||||
|
@ -883,7 +883,7 @@
|
||||||
"$(SRCROOT)/../../React/**",
|
"$(SRCROOT)/../../React/**",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = "$(SRCROOT)/UIExplorer/Info.plist";
|
INFOPLIST_FILE = "$(SRCROOT)/UIExplorer/Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited)";
|
||||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
PRODUCT_NAME = UIExplorer;
|
PRODUCT_NAME = UIExplorer;
|
||||||
|
@ -907,6 +907,12 @@
|
||||||
INFOPLIST_FILE = UIExplorerIntegrationTests/Info.plist;
|
INFOPLIST_FILE = UIExplorerIntegrationTests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-framework",
|
||||||
|
XCTest,
|
||||||
|
"-ObjC",
|
||||||
|
);
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UIExplorer.app/UIExplorer";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UIExplorer.app/UIExplorer";
|
||||||
};
|
};
|
||||||
|
@ -926,6 +932,12 @@
|
||||||
INFOPLIST_FILE = UIExplorerIntegrationTests/Info.plist;
|
INFOPLIST_FILE = UIExplorerIntegrationTests/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-framework",
|
||||||
|
XCTest,
|
||||||
|
"-ObjC",
|
||||||
|
);
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UIExplorer.app/UIExplorer";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UIExplorer.app/UIExplorer";
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,17 +21,17 @@
|
||||||
/**
|
/**
|
||||||
* This notification triggers a reload of all bridges currently running.
|
* This notification triggers a reload of all bridges currently running.
|
||||||
*/
|
*/
|
||||||
extern NSString *const RCTReloadNotification;
|
RCT_EXTERN NSString *const RCTReloadNotification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This notification fires when the bridge has finished loading.
|
* This notification fires when the bridge has finished loading.
|
||||||
*/
|
*/
|
||||||
extern NSString *const RCTJavaScriptDidLoadNotification;
|
RCT_EXTERN NSString *const RCTJavaScriptDidLoadNotification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This notification fires when the bridge failed to load.
|
* This notification fires when the bridge failed to load.
|
||||||
*/
|
*/
|
||||||
extern NSString *const RCTJavaScriptDidFailToLoadNotification;
|
RCT_EXTERN NSString *const RCTJavaScriptDidFailToLoadNotification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This block can be used to instantiate modules that require additional
|
* This block can be used to instantiate modules that require additional
|
||||||
|
@ -43,6 +43,13 @@ extern NSString *const RCTJavaScriptDidFailToLoadNotification;
|
||||||
*/
|
*/
|
||||||
typedef NSArray *(^RCTBridgeModuleProviderBlock)(void);
|
typedef NSArray *(^RCTBridgeModuleProviderBlock)(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given class as a bridge module. All modules must be registered
|
||||||
|
* prior to the first bridge initialization.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
RCT_EXTERN void RCTRegisterModule(Class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function returns the module name for a given class.
|
* This function returns the module name for a given class.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -70,6 +70,30 @@ typedef struct section RCTHeaderSection;
|
||||||
NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification";
|
NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification";
|
||||||
NSString *const RCTDequeueNotification = @"RCTDequeueNotification";
|
NSString *const RCTDequeueNotification = @"RCTDequeueNotification";
|
||||||
|
|
||||||
|
static NSDictionary *RCTModuleIDsByName;
|
||||||
|
static NSArray *RCTModuleNamesByID;
|
||||||
|
static NSArray *RCTModuleClassesByID;
|
||||||
|
void RCTRegisterModule(Class moduleClass)
|
||||||
|
{
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
RCTModuleIDsByName = [[NSMutableDictionary alloc] init];
|
||||||
|
RCTModuleNamesByID = [[NSMutableArray alloc] init];
|
||||||
|
RCTModuleClassesByID = [[NSMutableArray alloc] init];
|
||||||
|
});
|
||||||
|
|
||||||
|
RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
||||||
|
@"%@ does not conform to the RCTBridgeModule protocol",
|
||||||
|
NSStringFromClass(moduleClass));
|
||||||
|
|
||||||
|
// Register module
|
||||||
|
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
|
||||||
|
((NSMutableDictionary *)RCTModuleIDsByName)[moduleName] = @(RCTModuleNamesByID.count);
|
||||||
|
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
||||||
|
[(NSMutableArray *)RCTModuleClassesByID addObject:moduleClass];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function returns the module name for a given class.
|
* This function returns the module name for a given class.
|
||||||
*/
|
*/
|
||||||
|
@ -122,93 +146,6 @@ static NSArray *RCTJSMethods(void)
|
||||||
return JSMethods;
|
return JSMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This function scans all exported modules available at runtime and returns an
|
|
||||||
* array. As a backup, it also scans all classes that implement the
|
|
||||||
* RTCBridgeModule protocol to ensure they've been exported. This scanning
|
|
||||||
* functionality is disabled in release mode to improve startup performance.
|
|
||||||
*/
|
|
||||||
static NSDictionary *RCTModuleIDsByName;
|
|
||||||
static NSArray *RCTModuleNamesByID;
|
|
||||||
static NSArray *RCTModuleClassesByID;
|
|
||||||
static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|
||||||
{
|
|
||||||
static dispatch_once_t onceToken;
|
|
||||||
dispatch_once(&onceToken, ^{
|
|
||||||
|
|
||||||
RCTModuleIDsByName = [[NSMutableDictionary alloc] init];
|
|
||||||
RCTModuleNamesByID = [[NSMutableArray alloc] init];
|
|
||||||
RCTModuleClassesByID = [[NSMutableArray alloc] init];
|
|
||||||
|
|
||||||
Dl_info info;
|
|
||||||
dladdr(&RCTBridgeModuleClassesByModuleID, &info);
|
|
||||||
|
|
||||||
const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
|
|
||||||
unsigned long size;
|
|
||||||
const uint8_t *sectionData = getsectiondata(mach_header, "__DATA", "RCTExportModule", &size);
|
|
||||||
if (sectionData) {
|
|
||||||
for (const uint8_t *addr = sectionData;
|
|
||||||
addr < sectionData + size;
|
|
||||||
addr += sizeof(const char **)) {
|
|
||||||
|
|
||||||
// Get data entry
|
|
||||||
NSString *entry = @(*(const char **)addr);
|
|
||||||
NSArray *parts = [[entry substringWithRange:(NSRange){2, entry.length - 3}]
|
|
||||||
componentsSeparatedByString:@" "];
|
|
||||||
|
|
||||||
// Parse class name
|
|
||||||
NSString *moduleClassName = parts[0];
|
|
||||||
NSRange categoryRange = [moduleClassName rangeOfString:@"("];
|
|
||||||
if (categoryRange.length) {
|
|
||||||
moduleClassName = [moduleClassName substringToIndex:categoryRange.location];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get class
|
|
||||||
Class cls = NSClassFromString(moduleClassName);
|
|
||||||
RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
||||||
@"%@ does not conform to the RCTBridgeModule protocol",
|
|
||||||
NSStringFromClass(cls));
|
|
||||||
|
|
||||||
// Register module
|
|
||||||
NSString *moduleName = RCTBridgeModuleNameForClass(cls);
|
|
||||||
((NSMutableDictionary *)RCTModuleIDsByName)[moduleName] = @(RCTModuleNamesByID.count);
|
|
||||||
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
|
||||||
[(NSMutableArray *)RCTModuleClassesByID addObject:cls];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RCT_DEBUG) {
|
|
||||||
|
|
||||||
// We may be able to get rid of this check in future, once people
|
|
||||||
// get used to the new registration system. That would potentially
|
|
||||||
// allow you to create modules that are not automatically registered
|
|
||||||
|
|
||||||
static unsigned int classCount;
|
|
||||||
Class *classes = objc_copyClassList(&classCount);
|
|
||||||
for (unsigned int i = 0; i < classCount; i++)
|
|
||||||
{
|
|
||||||
Class cls = classes[i];
|
|
||||||
Class superclass = cls;
|
|
||||||
while (superclass)
|
|
||||||
{
|
|
||||||
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
|
|
||||||
{
|
|
||||||
if (![RCTModuleClassesByID containsObject:cls]) {
|
|
||||||
RCTLogError(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", NSStringFromClass(cls));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
superclass = class_getSuperclass(superclass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return RCTModuleClassesByID;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Can we just replace RCTMakeError with this function instead?
|
// TODO: Can we just replace RCTMakeError with this function instead?
|
||||||
static NSDictionary *RCTJSErrorFromNSError(NSError *error)
|
static NSDictionary *RCTJSErrorFromNSError(NSError *error)
|
||||||
{
|
{
|
||||||
|
@ -278,25 +215,42 @@ static NSDictionary *RCTJSErrorFromNSError(NSError *error)
|
||||||
dispatch_block_t _methodQueue;
|
dispatch_block_t _methodQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithReactMethodName:(NSString *)reactMethodName
|
- (instancetype)initWithObjCMethodName:(NSString *)objCMethodName
|
||||||
objCMethodName:(NSString *)objCMethodName
|
JSMethodName:(NSString *)JSMethodName
|
||||||
JSMethodName:(NSString *)JSMethodName
|
moduleClass:(Class)moduleClass
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
static NSRegularExpression *typeRegex;
|
||||||
|
static NSRegularExpression *selectorRegex;
|
||||||
|
if (!typeRegex) {
|
||||||
|
NSString *unusedPattern = @"(?:(?:__unused|__attribute__\\(\\(unused\\)\\)))";
|
||||||
|
NSString *constPattern = @"(?:const)";
|
||||||
|
NSString *constUnusedPattern = [NSString stringWithFormat:@"(?:(?:%@|%@)\\s*)", unusedPattern, constPattern];
|
||||||
|
NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", constUnusedPattern];
|
||||||
|
typeRegex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL];
|
||||||
|
|
||||||
NSArray *parts = [[reactMethodName substringWithRange:(NSRange){2, reactMethodName.length - 3}] componentsSeparatedByString:@" "];
|
selectorRegex = [[NSRegularExpression alloc] initWithPattern:@"(?<=:).*?(?=[a-zA-Z_]+:|$)" options:0 error:NULL];
|
||||||
|
|
||||||
// Parse class and method
|
|
||||||
_moduleClassName = parts[0];
|
|
||||||
NSRange categoryRange = [_moduleClassName rangeOfString:@"("];
|
|
||||||
if (categoryRange.length) {
|
|
||||||
_moduleClassName = [_moduleClassName substringToIndex:categoryRange.location];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *selectorString = [parts[1] substringFromIndex:14];
|
NSMutableArray *argumentNames = [NSMutableArray array];
|
||||||
_selector = NSSelectorFromString(selectorString);
|
[typeRegex enumerateMatchesInString:objCMethodName options:0 range:NSMakeRange(0, objCMethodName.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||||
_JSMethodName = JSMethodName ?: ({
|
NSString *argumentName = [objCMethodName substringWithRange:[result rangeAtIndex:1]];
|
||||||
NSString *methodName = selectorString;
|
[argumentNames addObject:argumentName];
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Remove the parameters' type and name
|
||||||
|
objCMethodName = [selectorRegex stringByReplacingMatchesInString:objCMethodName
|
||||||
|
options:0
|
||||||
|
range:NSMakeRange(0, objCMethodName.length)
|
||||||
|
withTemplate:@""];
|
||||||
|
// Remove any spaces since `selector : (Type)name` is a valid syntax
|
||||||
|
objCMethodName = [objCMethodName stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||||
|
|
||||||
|
_moduleClass = moduleClass;
|
||||||
|
_moduleClassName = NSStringFromClass(_moduleClass);
|
||||||
|
_selector = NSSelectorFromString(objCMethodName);
|
||||||
|
_JSMethodName = JSMethodName.length > 0 ? JSMethodName : ({
|
||||||
|
NSString *methodName = NSStringFromSelector(_selector);
|
||||||
NSRange colonRange = [methodName rangeOfString:@":"];
|
NSRange colonRange = [methodName rangeOfString:@":"];
|
||||||
if (colonRange.length) {
|
if (colonRange.length) {
|
||||||
methodName = [methodName substringToIndex:colonRange.location];
|
methodName = [methodName substringToIndex:colonRange.location];
|
||||||
|
@ -304,31 +258,6 @@ static NSDictionary *RCTJSErrorFromNSError(NSError *error)
|
||||||
methodName;
|
methodName;
|
||||||
});
|
});
|
||||||
|
|
||||||
static NSRegularExpression *regExp;
|
|
||||||
if (!regExp) {
|
|
||||||
NSString *unusedPattern = @"(?:(?:__unused|__attribute__\\(\\(unused\\)\\)))";
|
|
||||||
NSString *constPattern = @"(?:const)";
|
|
||||||
NSString *constUnusedPattern = [NSString stringWithFormat:@"(?:(?:%@|%@)\\s*)", unusedPattern, constPattern];
|
|
||||||
NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", constUnusedPattern];
|
|
||||||
regExp = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableArray *argumentNames = [NSMutableArray array];
|
|
||||||
[regExp enumerateMatchesInString:objCMethodName options:0 range:NSMakeRange(0, objCMethodName.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
|
||||||
NSString *argumentName = [objCMethodName substringWithRange:[result rangeAtIndex:1]];
|
|
||||||
[argumentNames addObject:argumentName];
|
|
||||||
}];
|
|
||||||
|
|
||||||
// Extract class and method details
|
|
||||||
_moduleClass = NSClassFromString(_moduleClassName);
|
|
||||||
|
|
||||||
if (RCT_DEBUG) {
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
RCTAssert([_moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
||||||
@"You are attempting to export the method %@, but %@ does not \
|
|
||||||
conform to the RCTBridgeModule Protocol", objCMethodName, _moduleClassName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get method signature
|
// Get method signature
|
||||||
_methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
_methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
|
||||||
|
@ -554,44 +483,33 @@ static RCTSparseArray *RCTExportedMethodsByModuleID(void)
|
||||||
static RCTSparseArray *methodsByModuleID;
|
static RCTSparseArray *methodsByModuleID;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
|
methodsByModuleID = [[RCTSparseArray alloc] initWithCapacity:[RCTModuleClassesByID count]];
|
||||||
|
|
||||||
Dl_info info;
|
[RCTModuleClassesByID enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
||||||
dladdr(&RCTExportedMethodsByModuleID, &info);
|
|
||||||
|
|
||||||
const RCTHeaderValue mach_header = (RCTHeaderValue)info.dli_fbase;
|
methodsByModuleID[moduleID] = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
unsigned long size;
|
unsigned int methodCount;
|
||||||
const uint8_t *sectionData = getsectiondata(mach_header, "__DATA", "RCTExport", &size);
|
Method *methods = class_copyMethodList(object_getClass(moduleClass), &methodCount);
|
||||||
|
|
||||||
if (sectionData == NULL) {
|
for (unsigned int i = 0; i < methodCount; i++) {
|
||||||
return;
|
Method method = methods[i];
|
||||||
}
|
SEL selector = method_getName(method);
|
||||||
|
if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
|
||||||
|
NSArray *entries = ((NSArray *(*)(id, SEL))objc_msgSend)(moduleClass, selector);
|
||||||
|
RCTModuleMethod *moduleMethod =
|
||||||
|
[[RCTModuleMethod alloc] initWithObjCMethodName:entries[1]
|
||||||
|
JSMethodName:entries[0]
|
||||||
|
moduleClass:moduleClass];
|
||||||
|
|
||||||
NSArray *classes = RCTBridgeModuleClassesByModuleID();
|
[methodsByModuleID[moduleID] addObject:moduleMethod];
|
||||||
NSMutableDictionary *methodsByModuleClassName = [NSMutableDictionary dictionaryWithCapacity:[classes count]];
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const uint8_t *addr = sectionData;
|
free(methods);
|
||||||
addr < sectionData + size;
|
|
||||||
addr += sizeof(const char **) * 3) {
|
|
||||||
|
|
||||||
// Get data entry
|
|
||||||
const char **entries = (const char **) addr;
|
|
||||||
|
|
||||||
// Create method
|
|
||||||
RCTModuleMethod *moduleMethod =
|
|
||||||
[[RCTModuleMethod alloc] initWithReactMethodName:@(entries[0])
|
|
||||||
objCMethodName:@(entries[1])
|
|
||||||
JSMethodName:strlen(entries[2]) ? @(entries[2]) : nil];
|
|
||||||
// Cache method
|
|
||||||
NSArray *methods = methodsByModuleClassName[moduleMethod.moduleClassName];
|
|
||||||
methodsByModuleClassName[moduleMethod.moduleClassName] =
|
|
||||||
methods ? [methods arrayByAddingObject:moduleMethod] : @[moduleMethod];
|
|
||||||
}
|
|
||||||
|
|
||||||
methodsByModuleID = [[RCTSparseArray alloc] initWithCapacity:[classes count]];
|
|
||||||
[classes enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
|
||||||
methodsByModuleID[moduleID] = methodsByModuleClassName[NSStringFromClass(moduleClass)];
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return methodsByModuleID;
|
return methodsByModuleID;
|
||||||
|
@ -630,7 +548,7 @@ static NSDictionary *RCTRemoteModulesConfig(NSDictionary *modulesByName)
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
|
|
||||||
remoteModuleConfigByClassName = [[NSMutableDictionary alloc] init];
|
remoteModuleConfigByClassName = [[NSMutableDictionary alloc] init];
|
||||||
[RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
[RCTModuleClassesByID enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
||||||
|
|
||||||
NSArray *methods = RCTExportedMethodsByModuleID()[moduleID];
|
NSArray *methods = RCTExportedMethodsByModuleID()[moduleID];
|
||||||
NSMutableDictionary *methodsByName = [NSMutableDictionary dictionaryWithCapacity:methods.count];
|
NSMutableDictionary *methodsByName = [NSMutableDictionary dictionaryWithCapacity:methods.count];
|
||||||
|
@ -768,6 +686,38 @@ static NSDictionary *RCTLocalModulesConfig()
|
||||||
|
|
||||||
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
|
|
||||||
|
#if RCT_DEBUG
|
||||||
|
+ (void)initialize
|
||||||
|
{
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
|
||||||
|
static unsigned int classCount;
|
||||||
|
Class *classes = objc_copyClassList(&classCount);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < classCount; i++)
|
||||||
|
{
|
||||||
|
Class cls = classes[i];
|
||||||
|
Class superclass = cls;
|
||||||
|
while (superclass)
|
||||||
|
{
|
||||||
|
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
|
||||||
|
{
|
||||||
|
if (![RCTModuleClassesByID containsObject:cls]) {
|
||||||
|
RCTLogError(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", NSStringFromClass(cls));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
superclass = class_getSuperclass(superclass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(classes);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
||||||
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
||||||
launchOptions:(NSDictionary *)launchOptions
|
launchOptions:(NSDictionary *)launchOptions
|
||||||
|
@ -1006,7 +956,7 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||||
// Instantiate modules
|
// Instantiate modules
|
||||||
_modulesByID = [[RCTSparseArray alloc] init];
|
_modulesByID = [[RCTSparseArray alloc] init];
|
||||||
NSMutableDictionary *modulesByName = [preregisteredModules mutableCopy];
|
NSMutableDictionary *modulesByName = [preregisteredModules mutableCopy];
|
||||||
[RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
[RCTModuleClassesByID enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
|
||||||
NSString *moduleName = RCTModuleNamesByID[moduleID];
|
NSString *moduleName = RCTModuleNamesByID[moduleID];
|
||||||
// Check if module instance has already been registered for this name
|
// Check if module instance has already been registered for this name
|
||||||
id<RCTBridgeModule> module = modulesByName[moduleName];
|
id<RCTBridgeModule> module = modulesByName[moduleName];
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "RCTDefines.h"
|
||||||
|
|
||||||
@class RCTBridge;
|
@class RCTBridge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,8 +64,9 @@ extern const dispatch_queue_t RCTJSThread;
|
||||||
* match the Objective-C class name.
|
* match the Objective-C class name.
|
||||||
*/
|
*/
|
||||||
#define RCT_EXPORT_MODULE(js_name) \
|
#define RCT_EXPORT_MODULE(js_name) \
|
||||||
+ (NSString *)moduleName { __attribute__((used, section("__DATA,RCTExportModule" \
|
RCT_EXTERN void RCTRegisterModule(Class); \
|
||||||
))) static const char *__rct_export_entry__ = { __func__ }; return @#js_name; }
|
+ (NSString *)moduleName { return @#js_name; } \
|
||||||
|
+ (void)load { RCTRegisterModule([self class]); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap the parameter line of your method implementation with this macro to
|
* Wrap the parameter line of your method implementation with this macro to
|
||||||
|
@ -173,11 +176,10 @@ extern const dispatch_queue_t RCTJSThread;
|
||||||
* Like RCT_EXTERN_REMAP_METHOD, but allows setting a custom JavaScript name.
|
* Like RCT_EXTERN_REMAP_METHOD, but allows setting a custom JavaScript name.
|
||||||
*/
|
*/
|
||||||
#define RCT_EXTERN_REMAP_METHOD(js_name, method) \
|
#define RCT_EXTERN_REMAP_METHOD(js_name, method) \
|
||||||
- (void)__rct_export__##method { \
|
+ (NSArray *)RCT_CONCAT(__rct_export__, __COUNTER__) { \
|
||||||
__attribute__((used, section("__DATA,RCTExport"))) \
|
return @[@#js_name, @#method]; \
|
||||||
__attribute__((__aligned__(1))) \
|
} \
|
||||||
static const char *__rct_export_entry__[] = { __func__, #method, #js_name }; \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The queue that will be used to call all exported methods. If omitted, this
|
* The queue that will be used to call all exported methods. If omitted, this
|
||||||
|
|
|
@ -74,3 +74,11 @@
|
||||||
#define RCT_NSASSERT 0
|
#define RCT_NSASSERT 0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concat two literals. Supports macro expansions
|
||||||
|
*
|
||||||
|
* i.e. RCT_CONCAT(foo, __FILE__)
|
||||||
|
*/
|
||||||
|
#define RCT_CONCAT2(A, B) A ## B
|
||||||
|
#define RCT_CONCAT(A, B) RCT_CONCAT2(A, B)
|
||||||
|
|
Loading…
Reference in New Issue