[ReactNative] Parellelise bridge startup
Summary: Parallelise the bridge startup so we don't keep waiting for the source to load.
This commit is contained in:
parent
3cef3010e6
commit
6cd0709bc0
|
@ -103,10 +103,6 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a
|
|||
(void)bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep on the main thread to allow js thread deallocations then run the runloop
|
||||
* to allow the module to be deallocated on the main thread
|
||||
*/
|
||||
RUN_RUNLOOP_WHILE(module.isValid)
|
||||
XCTAssertFalse(module.isValid, @"AllocationTestModule should have been invalidated by the bridge");
|
||||
}
|
||||
|
|
|
@ -120,10 +120,22 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
[_bridge invalidate];
|
||||
}
|
||||
|
||||
#define RUN_RUNLOOP_WHILE(CONDITION) \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
|
||||
NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \
|
||||
while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \
|
||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \
|
||||
} \
|
||||
_Pragma("clang diagnostic pop")
|
||||
|
||||
- (void)testHookRegistration
|
||||
{
|
||||
TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"];
|
||||
NSString *injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"];
|
||||
|
||||
NSString *injectedStuff;
|
||||
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
|
||||
|
||||
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
|
||||
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
|
||||
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
|
||||
|
@ -142,7 +154,10 @@ RCT_EXPORT_MODULE(TestModule)
|
|||
- (void)testCallNativeMethod
|
||||
{
|
||||
TestExecutor *executor = [_bridge.batchedBridge valueForKey:@"_javaScriptExecutor"];
|
||||
NSString *injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"];
|
||||
|
||||
NSString *injectedStuff;
|
||||
RUN_RUNLOOP_WHILE(!(injectedStuff = executor.injectedStuff[@"__fbBatchedBridgeConfig"]));
|
||||
|
||||
NSDictionary *moduleConfig = RCTJSONParse(injectedStuff, NULL);
|
||||
NSDictionary *remoteModuleConfig = moduleConfig[@"remoteModuleConfig"];
|
||||
NSDictionary *testModuleConfig = remoteModuleConfig[@"TestModule"];
|
||||
|
|
|
@ -70,6 +70,7 @@ id<RCTJavaScriptExecutor> RCTGetLatestExecutor(void)
|
|||
NSMutableSet *_frameUpdateObservers;
|
||||
NSMutableArray *_scheduledCalls;
|
||||
RCTSparseArray *_scheduledCallbacks;
|
||||
NSURL *_sourceURL;
|
||||
}
|
||||
|
||||
@synthesize valid = _valid;
|
||||
|
@ -101,77 +102,119 @@ id<RCTJavaScriptExecutor> RCTGetLatestExecutor(void)
|
|||
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and register bridge modules *before* adding the display link
|
||||
* so we don't have threading issues
|
||||
*/
|
||||
[self registerModules];
|
||||
|
||||
/**
|
||||
* If currently profiling, hook into the current instance
|
||||
*/
|
||||
if (RCTProfileIsProfiling()) {
|
||||
RCTProfileHookModules(self);
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
|
||||
object:self
|
||||
userInfo:@{ @"bridge": self }];
|
||||
|
||||
/**
|
||||
* Start the application script
|
||||
*/
|
||||
[self initJS];
|
||||
[self start];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
|
||||
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
|
||||
launchOptions:(__unused NSDictionary *)launchOptions)
|
||||
|
||||
- (void)setUp {}
|
||||
- (void)bindKeys {}
|
||||
|
||||
- (void)reload
|
||||
- (void)start
|
||||
{
|
||||
[_parentBridge reload];
|
||||
_sourceURL = self.delegate ? [self.delegate sourceURLForBridge:_parentBridge] : self.bundleURL;
|
||||
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
|
||||
__block NSString *sourceCode;
|
||||
__block NSString *config;
|
||||
|
||||
dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
|
||||
|
||||
dispatch_group_enter(initModulesAndLoadSource);
|
||||
[weakSelf loadSource:^(NSError *error, NSString *source) {
|
||||
if (error) {
|
||||
RCTLogError(@"%@", error);
|
||||
} else {
|
||||
sourceCode = source;
|
||||
}
|
||||
|
||||
dispatch_group_leave(initModulesAndLoadSource);
|
||||
}];
|
||||
|
||||
[self initModules];
|
||||
|
||||
dispatch_group_enter(initModulesAndLoadSource);
|
||||
dispatch_async(bridgeQueue, ^{
|
||||
dispatch_group_t setupJSExecutorAndModuleConfig = dispatch_group_create();
|
||||
dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
|
||||
[weakSelf setupExecutor];
|
||||
});
|
||||
|
||||
dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
|
||||
if (weakSelf.isValid) {
|
||||
config = [weakSelf moduleConfig];
|
||||
|
||||
if (RCTProfileIsProfiling()) {
|
||||
RCTProfileHookModules(weakSelf);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
dispatch_group_notify(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
|
||||
[weakSelf injectJSONConfiguration:config onComplete:^(__unused NSError *error) {}];
|
||||
|
||||
dispatch_group_leave(initModulesAndLoadSource);
|
||||
});
|
||||
});
|
||||
|
||||
dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{
|
||||
if (sourceCode) {
|
||||
[weakSelf executeSourceCode:sourceCode];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (Class)executorClass
|
||||
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad
|
||||
{
|
||||
return _parentBridge.executorClass ?: [RCTContextExecutor class];
|
||||
RCTPerformanceLoggerStart(RCTPLScriptDownload);
|
||||
RCTProfileBeginEvent();
|
||||
|
||||
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) {
|
||||
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
|
||||
RCTProfileEndEvent(@"JavaScript download", @"init,download", @[]);
|
||||
|
||||
if (error) {
|
||||
NSArray *stack = [error userInfo][@"stack"];
|
||||
if (stack) {
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
||||
withStack:stack];
|
||||
} else {
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
||||
withDetails:[error localizedFailureReason]];
|
||||
}
|
||||
|
||||
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
||||
object:_parentBridge
|
||||
userInfo:userInfo];
|
||||
}
|
||||
|
||||
_onSourceLoad(error, source);
|
||||
};
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
|
||||
[self.delegate loadSourceForBridge:_parentBridge withBlock:onSourceLoad];
|
||||
} else if (_sourceURL) {
|
||||
[RCTJavaScriptLoader loadBundleAtURL:_sourceURL
|
||||
onComplete:onSourceLoad];
|
||||
} else {
|
||||
// Allow testing without a script
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_loading = NO;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||
object:_parentBridge
|
||||
userInfo:@{ @"bridge": self }];
|
||||
});
|
||||
onSourceLoad(nil, nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setExecutorClass:(Class)executorClass
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
_parentBridge.executorClass = executorClass;
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
return _parentBridge.bundleURL;
|
||||
}
|
||||
|
||||
- (void)setBundleURL:(NSURL *)bundleURL
|
||||
{
|
||||
_parentBridge.bundleURL = bundleURL;
|
||||
}
|
||||
|
||||
- (id<RCTBridgeDelegate>)delegate
|
||||
{
|
||||
return _parentBridge.delegate;
|
||||
}
|
||||
|
||||
- (BOOL)isLoading
|
||||
{
|
||||
return _loading;
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _valid;
|
||||
}
|
||||
|
||||
- (void)registerModules
|
||||
- (void)initModules
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
|
@ -234,9 +277,6 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
|
|||
_javaScriptExecutor = _modulesByName[RCTBridgeModuleNameForClass(self.executorClass)];
|
||||
RCTLatestExecutor = _javaScriptExecutor;
|
||||
|
||||
[_javaScriptExecutor setUp];
|
||||
|
||||
// Set bridge
|
||||
for (id<RCTBridgeModule> module in _modulesByName.allValues) {
|
||||
if ([module respondsToSelector:@selector(setBridge:)]) {
|
||||
module.bridge = self;
|
||||
|
@ -246,127 +286,144 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
|
|||
uid:@(_moduleDataByID.count)
|
||||
instance:module];
|
||||
[_moduleDataByID addObject:moduleData];
|
||||
|
||||
if ([module conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
|
||||
[_frameUpdateObservers addObject:moduleData];
|
||||
}
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDidCreateNativeModules
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (void)initJS
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
// Inject module data into JS context
|
||||
- (void)setupExecutor
|
||||
{
|
||||
[_javaScriptExecutor setUp];
|
||||
|
||||
}
|
||||
|
||||
- (NSString *)moduleConfig
|
||||
{
|
||||
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
|
||||
for (RCTModuleData *moduleData in _moduleDataByID) {
|
||||
config[moduleData.name] = moduleData.config;
|
||||
|
||||
if ([moduleData.instance conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
|
||||
[_frameUpdateObservers addObject:moduleData];
|
||||
}
|
||||
}
|
||||
NSString *configJSON = RCTJSONStringify(@{
|
||||
|
||||
return RCTJSONStringify(@{
|
||||
@"remoteModuleConfig": config,
|
||||
}, NULL);
|
||||
}
|
||||
|
||||
- (void)injectJSONConfiguration:(NSString *)configJSON
|
||||
onComplete:(void (^)(NSError *))onComplete
|
||||
{
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_javaScriptExecutor injectJSONText:configJSON
|
||||
asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
|
||||
callback:^(NSError *error) {
|
||||
if (error) {
|
||||
[[RCTRedBox sharedInstance] showError:error];
|
||||
}
|
||||
onComplete(error);
|
||||
}];
|
||||
}
|
||||
|
||||
if (_javaScriptExecutor == nil) {
|
||||
- (void)executeSourceCode:(NSString *)sourceCode
|
||||
{
|
||||
_loading = NO;
|
||||
|
||||
if (!self.isValid || !_javaScriptExecutor) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
sourceCodeModule.scriptURL = _sourceURL;
|
||||
sourceCodeModule.scriptText = sourceCode;
|
||||
|
||||
static BOOL shouldDismiss = NO;
|
||||
if (shouldDismiss) {
|
||||
[[RCTRedBox sharedInstance] dismiss];
|
||||
}
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
shouldDismiss = YES;
|
||||
});
|
||||
|
||||
[self enqueueApplicationScript:sourceCode url:_sourceURL onComplete:^(NSError *loadError) {
|
||||
|
||||
if (loadError) {
|
||||
[[RCTRedBox sharedInstance] showError:loadError];
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* HACK (tadeu): If it failed to connect to the debugger, set loading to NO
|
||||
* so we can attempt to reload again.
|
||||
* Register the display link to start sending js calls after everything
|
||||
* is setup
|
||||
*/
|
||||
_loading = NO;
|
||||
NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop];
|
||||
[_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes];
|
||||
|
||||
} else {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
|
||||
object:self
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||
object:_parentBridge
|
||||
userInfo:@{ @"bridge": self }];
|
||||
RCTPerformanceLoggerStart(RCTPLScriptDownload);
|
||||
RCTProfileBeginEvent();
|
||||
NSURL *sourceURL = self.delegate ? [self.delegate sourceURLForBridge:_parentBridge] : self.bundleURL;
|
||||
void (^loadCallback)(NSError *, NSString *) = ^(NSError *error, NSString *script) {
|
||||
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
|
||||
RCTProfileEndEvent(@"JavaScript download", @"init,download", @[]);
|
||||
}];
|
||||
}
|
||||
|
||||
_loading = NO;
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
static BOOL shouldDismiss = NO;
|
||||
if (shouldDismiss) {
|
||||
[[RCTRedBox sharedInstance] dismiss];
|
||||
}
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
shouldDismiss = YES;
|
||||
});
|
||||
RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
|
||||
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
|
||||
launchOptions:(__unused NSDictionary *)launchOptions)
|
||||
|
||||
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
sourceCodeModule.scriptURL = sourceURL;
|
||||
sourceCodeModule.scriptText = script;
|
||||
if (error) {
|
||||
/**
|
||||
* Prevent super from calling setUp (that'd create another batchedBridge)
|
||||
*/
|
||||
- (void)setUp {}
|
||||
- (void)bindKeys {}
|
||||
|
||||
NSArray *stack = [error userInfo][@"stack"];
|
||||
if (stack) {
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
||||
withStack:stack];
|
||||
} else {
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
||||
withDetails:[error localizedFailureReason]];
|
||||
}
|
||||
- (void)reload
|
||||
{
|
||||
[_parentBridge reload];
|
||||
}
|
||||
|
||||
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
||||
object:_parentBridge
|
||||
userInfo:userInfo];
|
||||
- (Class)executorClass
|
||||
{
|
||||
return _parentBridge.executorClass ?: [RCTContextExecutor class];
|
||||
}
|
||||
|
||||
} else {
|
||||
- (void)setExecutorClass:(Class)executorClass
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
[self enqueueApplicationScript:script url:sourceURL onComplete:^(NSError *loadError) {
|
||||
_parentBridge.executorClass = executorClass;
|
||||
}
|
||||
|
||||
if (loadError) {
|
||||
[[RCTRedBox sharedInstance] showError:loadError];
|
||||
return;
|
||||
}
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
return _parentBridge.bundleURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the display link to start sending js calls after everything
|
||||
* is setup
|
||||
*/
|
||||
NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop];
|
||||
[_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes];
|
||||
- (void)setBundleURL:(NSURL *)bundleURL
|
||||
{
|
||||
_parentBridge.bundleURL = bundleURL;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||
object:_parentBridge
|
||||
userInfo:@{ @"bridge": self }];
|
||||
}];
|
||||
}
|
||||
};
|
||||
- (id<RCTBridgeDelegate>)delegate
|
||||
{
|
||||
return _parentBridge.delegate;
|
||||
}
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
|
||||
[self.delegate loadSourceForBridge:_parentBridge withBlock:loadCallback];
|
||||
} else if (sourceURL) {
|
||||
[RCTJavaScriptLoader loadBundleAtURL:sourceURL
|
||||
onComplete:loadCallback];
|
||||
} else {
|
||||
// Allow testing without a script
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_loading = NO;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||
object:_parentBridge
|
||||
userInfo:@{ @"bridge": self }];
|
||||
});
|
||||
}
|
||||
}
|
||||
- (BOOL)isLoading
|
||||
{
|
||||
return _loading;
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _valid;
|
||||
}
|
||||
|
||||
- (NSDictionary *)modules
|
||||
|
@ -751,14 +808,14 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
|
|||
|
||||
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
|
||||
|
||||
[self.perfStats.uiGraph onTick:displayLink.timestamp];
|
||||
_modulesByName == nil ?: [self.perfStats.uiGraph onTick:displayLink.timestamp];
|
||||
}
|
||||
|
||||
- (void)startProfiling
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if (![_parentBridge.bundleURL.scheme isEqualToString:@"http"]) {
|
||||
if (![_sourceURL.scheme isEqualToString:@"http"]) {
|
||||
RCTLogError(@"To run the profiler you must be running from the dev server");
|
||||
return;
|
||||
}
|
||||
|
@ -774,10 +831,8 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL
|
|||
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
NSString *log = RCTProfileEnd(self);
|
||||
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||
NSString *port = bundleURL.port ? [@":" stringByAppendingString:bundleURL.port.stringValue] : @"";
|
||||
NSString *URLString = [NSString stringWithFormat:@"%@://%@%@/profile", bundleURL.scheme, bundleURL.host, port];
|
||||
NSURL *URL = [RCTConvert NSURL:URLString];
|
||||
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", _sourceURL.scheme, _sourceURL.host, _sourceURL.port];
|
||||
NSURL *URL = [NSURL URLWithString:URLString];
|
||||
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
|
||||
URLRequest.HTTPMethod = @"POST";
|
||||
[URLRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
|
|
|
@ -208,7 +208,7 @@ void _RCTLogFormat(
|
|||
#if RCT_DEBUG // Red box is only available in debug mode
|
||||
|
||||
// Log to red box
|
||||
if (level >= RCTLOG_REDBOX_LEVEL) {
|
||||
if ([UIApplication sharedApplication] && level >= RCTLOG_REDBOX_LEVEL) {
|
||||
NSArray *stackSymbols = [NSThread callStackSymbols];
|
||||
NSMutableArray *stack = [NSMutableArray arrayWithCapacity:(stackSymbols.count - 1)];
|
||||
[stackSymbols enumerateObjectsUsingBlock:^(NSString *frameSymbols, NSUInteger idx, __unused BOOL *stop) {
|
||||
|
|
|
@ -14,6 +14,12 @@
|
|||
#import "RCTLog.h"
|
||||
|
||||
@implementation RCTModuleData
|
||||
{
|
||||
id _consts;
|
||||
|
||||
NSDictionary *_config;
|
||||
NSArray *_methods;
|
||||
}
|
||||
|
||||
- (instancetype)initWithExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
uid:(NSNumber *)uid
|
||||
|
@ -26,102 +32,109 @@
|
|||
_moduleClass = [instance class];
|
||||
_name = RCTBridgeModuleNameForClass(_moduleClass);
|
||||
|
||||
[self loadMethods];
|
||||
[self generateConfig];
|
||||
[self setQueue];
|
||||
if ([_instance respondsToSelector:@selector(constantsToExport)]) {
|
||||
_consts = [_instance constantsToExport];
|
||||
}
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(-init);
|
||||
|
||||
- (void)loadMethods
|
||||
- (NSArray *)methods
|
||||
{
|
||||
NSMutableArray *moduleMethods = [[NSMutableArray alloc] init];
|
||||
unsigned int methodCount;
|
||||
Method *methods = class_copyMethodList(object_getClass(_moduleClass), &methodCount);
|
||||
if (!_methods) {
|
||||
NSMutableArray *moduleMethods = [[NSMutableArray alloc] init];
|
||||
unsigned int methodCount;
|
||||
Method *methods = class_copyMethodList(object_getClass(_moduleClass), &methodCount);
|
||||
|
||||
for (unsigned int i = 0; i < methodCount; i++) {
|
||||
Method method = methods[i];
|
||||
SEL selector = method_getName(method);
|
||||
if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
|
||||
IMP imp = method_getImplementation(method);
|
||||
NSArray *entries = ((NSArray *(*)(id, SEL))imp)(_moduleClass, selector);
|
||||
RCTModuleMethod *moduleMethod =
|
||||
[[RCTModuleMethod alloc] initWithObjCMethodName:entries[1]
|
||||
JSMethodName:entries[0]
|
||||
moduleClass:_moduleClass];
|
||||
for (unsigned int i = 0; i < methodCount; i++) {
|
||||
Method method = methods[i];
|
||||
SEL selector = method_getName(method);
|
||||
if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
|
||||
IMP imp = method_getImplementation(method);
|
||||
NSArray *entries = ((NSArray *(*)(id, SEL))imp)(_moduleClass, selector);
|
||||
RCTModuleMethod *moduleMethod =
|
||||
[[RCTModuleMethod alloc] initWithObjCMethodName:entries[1]
|
||||
JSMethodName:entries[0]
|
||||
moduleClass:_moduleClass];
|
||||
|
||||
[moduleMethods addObject:moduleMethod];
|
||||
[moduleMethods addObject:moduleMethod];
|
||||
}
|
||||
}
|
||||
|
||||
free(methods);
|
||||
|
||||
_methods = [moduleMethods copy];
|
||||
}
|
||||
|
||||
free(methods);
|
||||
|
||||
_methods = [moduleMethods copy];
|
||||
return _methods;
|
||||
}
|
||||
|
||||
- (void)generateConfig
|
||||
- (NSDictionary *)config
|
||||
{
|
||||
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
|
||||
config[@"moduleID"] = _uid;
|
||||
config[@"methods"] = [[NSMutableDictionary alloc] init];
|
||||
if (!_config) {
|
||||
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
|
||||
config[@"moduleID"] = _uid;
|
||||
config[@"methods"] = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if ([_instance respondsToSelector:@selector(constantsToExport)]) {
|
||||
id consts = [_instance constantsToExport];
|
||||
if (consts) {
|
||||
config[@"constants"] = consts;
|
||||
if (_consts) {
|
||||
config[@"constants"] = _consts;
|
||||
}
|
||||
|
||||
[self.methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger idx, __unused BOOL *stop) {
|
||||
config[@"methods"][method.JSMethodName] = @{
|
||||
@"methodID": @(idx),
|
||||
@"type": method.functionKind == RCTJavaScriptFunctionKindAsync ? @"remoteAsync" : @"remote",
|
||||
};
|
||||
}];
|
||||
|
||||
_config = [config copy];
|
||||
}
|
||||
|
||||
[_methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger idx, __unused BOOL *stop) {
|
||||
config[@"methods"][method.JSMethodName] = @{
|
||||
@"methodID": @(idx),
|
||||
@"type": method.functionKind == RCTJavaScriptFunctionKindAsync ? @"remoteAsync" : @"remote",
|
||||
};
|
||||
}];
|
||||
|
||||
_config = [config copy];
|
||||
return _config;
|
||||
}
|
||||
|
||||
- (void)setQueue
|
||||
- (dispatch_queue_t)queue
|
||||
{
|
||||
dispatch_queue_t queue = nil;
|
||||
BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
|
||||
if (implementsMethodQueue) {
|
||||
queue = [_instance methodQueue];
|
||||
}
|
||||
if (!queue) {
|
||||
|
||||
// Need to cache queueNames because they aren't retained by dispatch_queue
|
||||
static NSMutableDictionary *queueNames;
|
||||
if (!queueNames) {
|
||||
queueNames = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
NSString *queueName = queueNames[_name];
|
||||
if (!queueName) {
|
||||
queueName = [NSString stringWithFormat:@"com.facebook.React.%@Queue", _name];
|
||||
queueNames[_name] = queueName;
|
||||
}
|
||||
|
||||
// Create new queue
|
||||
queue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// assign it to the module
|
||||
if (!_queue) {
|
||||
BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
|
||||
if (implementsMethodQueue) {
|
||||
@try {
|
||||
[(id)_instance setValue:queue forKey:@"methodQueue"];
|
||||
_queue = [_instance methodQueue];
|
||||
}
|
||||
if (!_queue) {
|
||||
|
||||
// Need to cache queueNames because they aren't retained by dispatch_queue
|
||||
static NSMutableDictionary *queueNames;
|
||||
if (!queueNames) {
|
||||
queueNames = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogError(@"%@ is returning nil for it's methodQueue, which is not "
|
||||
"permitted. You must either return a pre-initialized "
|
||||
"queue, or @synthesize the methodQueue to let the bridge "
|
||||
"create a queue for you.", _name);
|
||||
NSString *queueName = queueNames[_name];
|
||||
if (!queueName) {
|
||||
queueName = [NSString stringWithFormat:@"com.facebook.React.%@Queue", _name];
|
||||
queueNames[_name] = queueName;
|
||||
}
|
||||
|
||||
// Create new queue
|
||||
_queue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// assign it to the module
|
||||
if (implementsMethodQueue) {
|
||||
@try {
|
||||
[(id)_instance setValue:_queue forKey:@"methodQueue"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogError(@"%@ is returning nil for it's methodQueue, which is not "
|
||||
"permitted. You must either return a pre-initialized "
|
||||
"queue, or @synthesize the methodQueue to let the bridge "
|
||||
"create a queue for you.", _name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_queue = queue;
|
||||
return _queue;
|
||||
}
|
||||
|
||||
- (void)dispatchBlock:(dispatch_block_t)block
|
||||
|
@ -132,13 +145,13 @@ RCT_NOT_IMPLEMENTED(-init);
|
|||
- (void)dispatchBlock:(dispatch_block_t)block
|
||||
dispatchGroup:(dispatch_group_t)group
|
||||
{
|
||||
if (_queue == RCTJSThread) {
|
||||
if (self.queue == RCTJSThread) {
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||
} else if (_queue) {
|
||||
} else if (self.queue) {
|
||||
if (group != NULL) {
|
||||
dispatch_group_async(group, _queue, block);
|
||||
dispatch_group_async(group, self.queue, block);
|
||||
} else {
|
||||
dispatch_async(_queue, block);
|
||||
dispatch_async(self.queue, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ static IMP RCTProfileMsgForward(NSObject *self, SEL selector)
|
|||
|
||||
void RCTProfileHookModules(RCTBridge *bridge)
|
||||
{
|
||||
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
|
||||
for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) {
|
||||
[moduleData dispatchBlock:^{
|
||||
Class moduleClass = moduleData.moduleClass;
|
||||
Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);
|
||||
|
@ -190,7 +190,7 @@ void RCTProfileHookModules(RCTBridge *bridge)
|
|||
|
||||
void RCTProfileUnhookModules(RCTBridge *bridge)
|
||||
{
|
||||
for (RCTModuleData *moduleData in [bridge valueForKey:@"_modules"]) {
|
||||
for (RCTModuleData *moduleData in [bridge valueForKey:@"moduleDataByID"]) {
|
||||
Class proxyClass = object_getClass(moduleData.instance);
|
||||
if (moduleData.moduleClass != proxyClass) {
|
||||
object_setClass(moduleData.instance, moduleData.moduleClass);
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
_redColor = [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1];
|
||||
self.windowLevel = CGFLOAT_MAX;
|
||||
self.windowLevel = UIWindowLevelAlert + 1000;
|
||||
self.backgroundColor = _redColor;
|
||||
self.hidden = YES;
|
||||
|
||||
|
|
|
@ -468,7 +468,8 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||
sourceURL:(NSURL *)sourceURL
|
||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
RCTAssert(sourceURL != nil, @"url should not be nil");
|
||||
RCTAssertParam(script);
|
||||
RCTAssertParam(sourceURL);
|
||||
|
||||
__weak RCTContextExecutor *weakSelf = self;
|
||||
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
|
||||
|
|
Loading…
Reference in New Issue