mirror of
https://github.com/status-im/react-native.git
synced 2025-02-23 06:38:13 +00:00
Summary: This may be controversial. Right now, RelayPrefetcher is initialized [here](https://fburl.com/p01iunr1), after bridge is initialized. I want to create a FBRelayPrefetcherModule instance eagerly (diff 3 in stack), and then pass that into the bridge module registry. This way, native side gets to use RelayPrefetcher before bridge is init, and JS still accesses the same instance of FBRelayPrefetcherModule. The only other option is drastically change bridge init, to allow passing in some eagerly initialized instances. Reviewed By: shergin Differential Revision: D13164277 fbshipit-source-id: b45111cd68d78820e61e4fca7e54a7e8df32a3f0
398 lines
10 KiB
Objective-C
398 lines
10 KiB
Objective-C
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#import "RCTBridge.h"
|
|
#import "RCTBridge+Private.h"
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
#import "RCTConvert.h"
|
|
#import "RCTEventDispatcher.h"
|
|
#if RCT_ENABLE_INSPECTOR
|
|
#import "RCTInspectorDevServerHelper.h"
|
|
#endif
|
|
#import "RCTLog.h"
|
|
#import "RCTModuleData.h"
|
|
#import "RCTPerformanceLogger.h"
|
|
#import "RCTProfile.h"
|
|
#import "RCTReloadCommand.h"
|
|
#import "RCTUtils.h"
|
|
|
|
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
|
|
NSString *const RCTJavaScriptWillStartExecutingNotification = @"RCTJavaScriptWillStartExecutingNotification";
|
|
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
|
NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification";
|
|
NSString *const RCTDidInitializeModuleNotification = @"RCTDidInitializeModuleNotification";
|
|
NSString *const RCTBridgeWillReloadNotification = @"RCTBridgeWillReloadNotification";
|
|
NSString *const RCTBridgeWillDownloadScriptNotification = @"RCTBridgeWillDownloadScriptNotification";
|
|
NSString *const RCTBridgeDidDownloadScriptNotification = @"RCTBridgeDidDownloadScriptNotification";
|
|
NSString *const RCTBridgeDidDownloadScriptNotificationSourceKey = @"source";
|
|
NSString *const RCTBridgeDidDownloadScriptNotificationBridgeDescriptionKey = @"bridgeDescription";
|
|
|
|
static NSMutableArray<Class> *RCTModuleClasses;
|
|
static dispatch_queue_t RCTModuleClassesSyncQueue;
|
|
NSArray<Class> *RCTGetModuleClasses(void)
|
|
{
|
|
__block NSArray<Class> *result;
|
|
dispatch_sync(RCTModuleClassesSyncQueue, ^{
|
|
result = [RCTModuleClasses copy];
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Register the given class as a bridge module. All modules must be registered
|
|
* prior to the first bridge initialization.
|
|
*/
|
|
void RCTRegisterModule(Class);
|
|
void RCTRegisterModule(Class moduleClass)
|
|
{
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
RCTModuleClasses = [NSMutableArray new];
|
|
RCTModuleClassesSyncQueue = dispatch_queue_create("com.facebook.react.ModuleClassesSyncQueue", DISPATCH_QUEUE_CONCURRENT);
|
|
});
|
|
|
|
RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
@"%@ does not conform to the RCTBridgeModule protocol",
|
|
moduleClass);
|
|
|
|
// Register module
|
|
dispatch_barrier_async(RCTModuleClassesSyncQueue, ^{
|
|
[RCTModuleClasses addObject:moduleClass];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* This function returns the module name for a given class.
|
|
*/
|
|
NSString *RCTBridgeModuleNameForClass(Class cls)
|
|
{
|
|
#if RCT_DEBUG
|
|
RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
@"Bridge module `%@` does not conform to RCTBridgeModule", cls);
|
|
#endif
|
|
|
|
NSString *name = [cls moduleName];
|
|
if (name.length == 0) {
|
|
name = NSStringFromClass(cls);
|
|
}
|
|
|
|
return RCTDropReactPrefixes(name);
|
|
}
|
|
|
|
static BOOL turboModuleEnabled = NO;
|
|
BOOL RCTTurboModuleEnabled(void)
|
|
{
|
|
return turboModuleEnabled;
|
|
}
|
|
|
|
void RCTEnableTurboModule(BOOL enabled) {
|
|
turboModuleEnabled = enabled;
|
|
}
|
|
|
|
#if RCT_DEBUG
|
|
void RCTVerifyAllModulesExported(NSArray *extraModules)
|
|
{
|
|
// Check for unexported modules
|
|
unsigned int classCount;
|
|
Class *classes = objc_copyClassList(&classCount);
|
|
|
|
NSMutableSet *moduleClasses = [NSMutableSet new];
|
|
[moduleClasses addObjectsFromArray:RCTGetModuleClasses()];
|
|
[moduleClasses addObjectsFromArray:[extraModules valueForKeyPath:@"class"]];
|
|
|
|
for (unsigned int i = 0; i < classCount; i++) {
|
|
Class cls = classes[i];
|
|
if (strncmp(class_getName(cls), "RCTCxxModule", strlen("RCTCxxModule")) == 0) {
|
|
continue;
|
|
}
|
|
Class superclass = cls;
|
|
while (superclass) {
|
|
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule))) {
|
|
if ([moduleClasses containsObject:cls]) {
|
|
break;
|
|
}
|
|
|
|
// Verify it's not a super-class of one of our moduleClasses
|
|
BOOL isModuleSuperClass = NO;
|
|
for (Class moduleClass in moduleClasses) {
|
|
if ([moduleClass isSubclassOfClass:cls]) {
|
|
isModuleSuperClass = YES;
|
|
break;
|
|
}
|
|
}
|
|
if (isModuleSuperClass) {
|
|
break;
|
|
}
|
|
|
|
// Note: Some modules may be lazily loaded and not exported up front, so this message is no longer a warning.
|
|
RCTLogInfo(@"Class %@ was not exported. Did you forget to use RCT_EXPORT_MODULE()?", cls);
|
|
break;
|
|
}
|
|
superclass = class_getSuperclass(superclass);
|
|
}
|
|
}
|
|
|
|
free(classes);
|
|
}
|
|
#endif
|
|
|
|
@interface RCTBridge () <RCTReloadListener>
|
|
@end
|
|
|
|
@implementation RCTBridge
|
|
{
|
|
NSURL *_delegateBundleURL;
|
|
}
|
|
|
|
dispatch_queue_t RCTJSThread;
|
|
|
|
+ (void)initialize
|
|
{
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
// Set up JS thread
|
|
RCTJSThread = (id)kCFNull;
|
|
});
|
|
}
|
|
|
|
static RCTBridge *RCTCurrentBridgeInstance = nil;
|
|
|
|
/**
|
|
* The last current active bridge instance. This is set automatically whenever
|
|
* the bridge is accessed. It can be useful for static functions or singletons
|
|
* that need to access the bridge for purposes such as logging, but should not
|
|
* be relied upon to return any particular instance, due to race conditions.
|
|
*/
|
|
+ (instancetype)currentBridge
|
|
{
|
|
return RCTCurrentBridgeInstance;
|
|
}
|
|
|
|
+ (void)setCurrentBridge:(RCTBridge *)currentBridge
|
|
{
|
|
RCTCurrentBridgeInstance = currentBridge;
|
|
}
|
|
|
|
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
|
|
launchOptions:(NSDictionary *)launchOptions
|
|
{
|
|
return [self initWithDelegate:delegate
|
|
bundleURL:nil
|
|
moduleProvider:nil
|
|
launchOptions:launchOptions];
|
|
}
|
|
|
|
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
|
moduleProvider:(RCTBridgeModuleListProvider)block
|
|
launchOptions:(NSDictionary *)launchOptions
|
|
{
|
|
return [self initWithDelegate:nil
|
|
bundleURL:bundleURL
|
|
moduleProvider:block
|
|
launchOptions:launchOptions];
|
|
}
|
|
|
|
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
|
|
bundleURL:(NSURL *)bundleURL
|
|
moduleProvider:(RCTBridgeModuleListProvider)block
|
|
launchOptions:(NSDictionary *)launchOptions
|
|
{
|
|
if (self = [super init]) {
|
|
_delegate = delegate;
|
|
_bundleURL = bundleURL;
|
|
_moduleProvider = block;
|
|
_launchOptions = [launchOptions copy];
|
|
|
|
[self setUp];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|
|
|
- (void)dealloc
|
|
{
|
|
/**
|
|
* This runs only on the main thread, but crashes the subclass
|
|
* RCTAssertMainQueue();
|
|
*/
|
|
[self invalidate];
|
|
}
|
|
|
|
- (void)didReceiveReloadCommand
|
|
{
|
|
[self reload];
|
|
}
|
|
|
|
- (NSArray<Class> *)moduleClasses
|
|
{
|
|
return self.batchedBridge.moduleClasses;
|
|
}
|
|
|
|
- (id)moduleForName:(NSString *)moduleName
|
|
{
|
|
return [self.batchedBridge moduleForName:moduleName];
|
|
}
|
|
|
|
- (id)moduleForName:(NSString *)moduleName lazilyLoadIfNecessary:(BOOL)lazilyLoad
|
|
{
|
|
return [self.batchedBridge moduleForName:moduleName lazilyLoadIfNecessary:lazilyLoad];
|
|
}
|
|
|
|
- (id)moduleForClass:(Class)moduleClass
|
|
{
|
|
id module = [self.batchedBridge moduleForClass:moduleClass];
|
|
if (!module) {
|
|
module = [self moduleForName:RCTBridgeModuleNameForClass(moduleClass)];
|
|
}
|
|
return module;
|
|
}
|
|
|
|
- (NSArray *)modulesConformingToProtocol:(Protocol *)protocol
|
|
{
|
|
NSMutableArray *modules = [NSMutableArray new];
|
|
for (Class moduleClass in [self.moduleClasses copy]) {
|
|
if ([moduleClass conformsToProtocol:protocol]) {
|
|
id module = [self moduleForClass:moduleClass];
|
|
if (module) {
|
|
[modules addObject:module];
|
|
}
|
|
}
|
|
}
|
|
return [modules copy];
|
|
}
|
|
|
|
- (BOOL)moduleIsInitialized:(Class)moduleClass
|
|
{
|
|
return [self.batchedBridge moduleIsInitialized:moduleClass];
|
|
}
|
|
|
|
- (void)reload
|
|
{
|
|
#if RCT_ENABLE_INSPECTOR
|
|
// Disable debugger to resume the JsVM & avoid thread locks while reloading
|
|
[RCTInspectorDevServerHelper disableDebugger];
|
|
#endif
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTBridgeWillReloadNotification object:self];
|
|
|
|
/**
|
|
* Any thread
|
|
*/
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self invalidate];
|
|
[self setUp];
|
|
});
|
|
}
|
|
|
|
- (void)requestReload
|
|
{
|
|
[self reload];
|
|
}
|
|
|
|
- (Class)bridgeClass
|
|
{
|
|
return [RCTCxxBridge class];
|
|
}
|
|
|
|
- (void)setUp
|
|
{
|
|
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil);
|
|
|
|
_performanceLogger = [RCTPerformanceLogger new];
|
|
[_performanceLogger markStartForTag:RCTPLBridgeStartup];
|
|
[_performanceLogger markStartForTag:RCTPLTTI];
|
|
|
|
Class bridgeClass = self.bridgeClass;
|
|
|
|
#if RCT_DEV
|
|
RCTExecuteOnMainQueue(^{
|
|
RCTRegisterReloadCommandListener(self);
|
|
});
|
|
#endif
|
|
|
|
// Only update bundleURL from delegate if delegate bundleURL has changed
|
|
NSURL *previousDelegateURL = _delegateBundleURL;
|
|
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
|
|
if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
|
|
_bundleURL = _delegateBundleURL;
|
|
}
|
|
|
|
// Sanitize the bundle URL
|
|
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
|
|
|
|
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
|
|
[self.batchedBridge start];
|
|
|
|
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
|
|
}
|
|
|
|
- (BOOL)isLoading
|
|
{
|
|
return self.batchedBridge.loading;
|
|
}
|
|
|
|
- (BOOL)isValid
|
|
{
|
|
return self.batchedBridge.valid;
|
|
}
|
|
|
|
- (BOOL)isBatchActive
|
|
{
|
|
return [_batchedBridge isBatchActive];
|
|
}
|
|
|
|
- (void)invalidate
|
|
{
|
|
RCTBridge *batchedBridge = self.batchedBridge;
|
|
self.batchedBridge = nil;
|
|
|
|
if (batchedBridge) {
|
|
RCTExecuteOnMainQueue(^{
|
|
[batchedBridge invalidate];
|
|
});
|
|
}
|
|
}
|
|
|
|
- (void)updateModuleWithInstance:(id<RCTBridgeModule>)instance
|
|
{
|
|
[self.batchedBridge updateModuleWithInstance:instance];
|
|
}
|
|
|
|
- (void)registerAdditionalModuleClasses:(NSArray<Class> *)modules
|
|
{
|
|
[self.batchedBridge registerAdditionalModuleClasses:modules];
|
|
}
|
|
|
|
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
|
{
|
|
NSArray<NSString *> *ids = [moduleDotMethod componentsSeparatedByString:@"."];
|
|
NSString *module = ids[0];
|
|
NSString *method = ids[1];
|
|
[self enqueueJSCall:module method:method args:args completion:NULL];
|
|
}
|
|
|
|
- (void)enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion
|
|
{
|
|
[self.batchedBridge enqueueJSCall:module method:method args:args completion:completion];
|
|
}
|
|
|
|
- (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args
|
|
{
|
|
[self.batchedBridge enqueueCallback:cbID args:args];
|
|
}
|
|
|
|
- (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path
|
|
{
|
|
[self.batchedBridge registerSegmentWithId:segmentId path:path];
|
|
}
|
|
|
|
@end
|