mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
564b944e56
Summary: ## Context When Venice is enabled app-wide, we won't be creating/initializing the bridge. This means that RCTBridge +(void)initialize; won't execute: https://www.internalfb.com/code/fbsource/[d8b25a1907ee55baa21e02a69ecab0f7a9442b8e]/xplat/js/react-native-github/React/Base/RCTBridge.m?lines=167%2C171-180 ## Problem When RCTBridge initialize isn't executed, we won't initialize RCTJSThread to kCFNull. RCTJSThread will be nil. NativeModules like RCTEventDispatcher use RCTJSThread to indicate that their methods must be executed on the JavaScript thread: https://www.internalfb.com/code/fbsource/[44976912ae618619a394f063c4c942ef020b86e8]/xplat/js/react-native-github/React/CoreModules/RCTEventDispatcher.mm?lines=198-201 If RCTJSThread is nil, these NativeModules will fail to initialize w/ the TurboModule system: https://www.internalfb.com/code/fbsource/[f6a04f529ac1354b2973bd3553d12aef28ff24f2][blame]/xplat/js/react-native-github/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm?lines=584-589%2C595%2C599%2C610-618 How: 1. The TurboModuleManager will see that each of these NativeModules has a methodQueue getter (line 587 above). 2. It'll invoke that getter (line 588), and get a nil methodQueue out, when **that methodQueue should be kCFNull**. 3. Because the TurboModuleManager gets a nil method queue from the getter, !methodQueue will pass (line 595). 4. So, TurboModuleManager try to create and assign a method queue to these modules (line 611), which'll raise an error (line 613), because none of these modules synthesize the methodQueue (b/c they expose a getter to methodQueue instead). ## Changes We need to initialize RCTJSThread to kCFNull in all cases, to prevent this breakage. So, I moved RCTJSThread into its own header: RCTJSThread.h. RCTJSThread.h exports a function that initializes the RCTJSThread constant: _RCTInitializeJSThreadConstantInternal. This function gets invoked inside RCTHost initialize, and RCTBridge initialize. Created from CodeHub with https://fburl.com/edit-in-codehub Changelog: [Internal] Reviewed By: p-sun, mdvacca Differential Revision: D30910515 fbshipit-source-id: 2dd9b0cfcda92c497bb497f12f9fb847da563f47
453 lines
12 KiB
Objective-C
453 lines
12 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"
|
|
#if RCT_ENABLE_INSPECTOR
|
|
#import "RCTInspectorDevServerHelper.h"
|
|
#endif
|
|
#import "RCTDevLoadingViewProtocol.h"
|
|
#import "RCTJSThread.h"
|
|
#import "RCTLog.h"
|
|
#import "RCTModuleData.h"
|
|
#import "RCTPerformanceLogger.h"
|
|
#import "RCTProfile.h"
|
|
#import "RCTReloadCommand.h"
|
|
#import "RCTUtils.h"
|
|
|
|
NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification";
|
|
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
|
NSString *const RCTJavaScriptWillStartExecutingNotification = @"RCTJavaScriptWillStartExecutingNotification";
|
|
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
|
|
NSString *const RCTDidInitializeModuleNotification = @"RCTDidInitializeModuleNotification";
|
|
NSString *const RCTDidSetupModuleNotification = @"RCTDidSetupModuleNotification";
|
|
NSString *const RCTDidSetupModuleNotificationModuleNameKey = @"moduleName";
|
|
NSString *const RCTDidSetupModuleNotificationSetupTimeKey = @"setupTime";
|
|
NSString *const RCTBridgeWillReloadNotification = @"RCTBridgeWillReloadNotification";
|
|
NSString *const RCTBridgeFastRefreshNotification = @"RCTBridgeFastRefreshNotification";
|
|
NSString *const RCTBridgeWillDownloadScriptNotification = @"RCTBridgeWillDownloadScriptNotification";
|
|
NSString *const RCTBridgeDidDownloadScriptNotification = @"RCTBridgeDidDownloadScriptNotification";
|
|
NSString *const RCTBridgeWillInvalidateModulesNotification = @"RCTBridgeWillInvalidateModulesNotification";
|
|
NSString *const RCTBridgeDidInvalidateModulesNotification = @"RCTBridgeDidInvalidateModulesNotification";
|
|
NSString *const RCTBridgeWillBeInvalidatedNotification = @"RCTBridgeWillBeInvalidatedNotification";
|
|
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)
|
|
{
|
|
RCTWarnNotAllowedForNewArchitecture(
|
|
@"RCTRegisterModule()", [NSString stringWithFormat:@"'%@' was registered unexpectedly", 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)
|
|
{
|
|
#if RCT_DEBUG
|
|
// TODO(T53341772): Allow TurboModule for test environment. Right now this breaks RNTester tests if enabled.
|
|
if (RCTRunningInTestEnvironment()) {
|
|
return NO;
|
|
}
|
|
#endif
|
|
return turboModuleEnabled;
|
|
}
|
|
|
|
void RCTEnableTurboModule(BOOL enabled)
|
|
{
|
|
turboModuleEnabled = enabled;
|
|
}
|
|
|
|
static BOOL turboModuleEagerInitEnabled = NO;
|
|
BOOL RCTTurboModuleEagerInitEnabled(void)
|
|
{
|
|
return turboModuleEagerInitEnabled;
|
|
}
|
|
|
|
void RCTEnableTurboModuleEagerInit(BOOL enabled)
|
|
{
|
|
turboModuleEagerInitEnabled = enabled;
|
|
}
|
|
|
|
static BOOL turboModuleSharedMutexInitEnabled = NO;
|
|
BOOL RCTTurboModuleSharedMutexInitEnabled(void)
|
|
{
|
|
return turboModuleSharedMutexInitEnabled;
|
|
}
|
|
|
|
void RCTEnableTurboModuleSharedMutexInit(BOOL enabled)
|
|
{
|
|
turboModuleSharedMutexInitEnabled = enabled;
|
|
}
|
|
|
|
static RCTTurboModuleCleanupMode turboModuleCleanupMode = kRCTGlobalScope;
|
|
RCTTurboModuleCleanupMode RCTGetTurboModuleCleanupMode(void)
|
|
{
|
|
return turboModuleCleanupMode;
|
|
}
|
|
|
|
void RCTSetTurboModuleCleanupMode(RCTTurboModuleCleanupMode mode)
|
|
{
|
|
turboModuleCleanupMode = mode;
|
|
}
|
|
|
|
// Turn off TurboModule delegate locking
|
|
static BOOL turboModuleManagerDelegateLockingDisabled = YES;
|
|
BOOL RCTTurboModuleManagerDelegateLockingDisabled(void)
|
|
{
|
|
return turboModuleManagerDelegateLockingDisabled;
|
|
}
|
|
|
|
void RCTDisableTurboModuleManagerDelegateLocking(BOOL disabled)
|
|
{
|
|
turboModuleManagerDelegateLockingDisabled = disabled;
|
|
}
|
|
|
|
@interface RCTBridge () <RCTReloadListener>
|
|
@end
|
|
|
|
@implementation RCTBridge {
|
|
NSURL *_delegateBundleURL;
|
|
}
|
|
|
|
+ (void)initialize
|
|
{
|
|
_RCTInitializeJSThreadConstantInternal();
|
|
}
|
|
|
|
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]) {
|
|
RCTEnforceNotAllowedForNewArchitecture(self, nil);
|
|
_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)setRCTTurboModuleRegistry:(id<RCTTurboModuleRegistry>)turboModuleRegistry
|
|
{
|
|
[self.batchedBridge setRCTTurboModuleRegistry:turboModuleRegistry];
|
|
}
|
|
|
|
- (void)attachBridgeAPIsToTurboModule:(id<RCTTurboModule>)module
|
|
{
|
|
[self.batchedBridge attachBridgeAPIsToTurboModule:module];
|
|
}
|
|
|
|
- (void)didReceiveReloadCommand
|
|
{
|
|
#if RCT_ENABLE_INSPECTOR
|
|
// Disable debugger to resume the JsVM & avoid thread locks while reloading
|
|
[RCTInspectorDevServerHelper disableDebugger];
|
|
#endif
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTBridgeWillReloadNotification object:self userInfo:nil];
|
|
|
|
/**
|
|
* Any thread
|
|
*/
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
// WARNING: Invalidation is async, so it may not finish before re-setting up the bridge,
|
|
// causing some issues. TODO: revisit this post-Fabric/TurboModule.
|
|
[self invalidate];
|
|
// Reload is a special case, do not preserve launchOptions and treat reload as a fresh start
|
|
self->_launchOptions = nil;
|
|
[self setUp];
|
|
});
|
|
}
|
|
|
|
- (RCTModuleRegistry *)moduleRegistry
|
|
{
|
|
return self.batchedBridge.moduleRegistry;
|
|
}
|
|
|
|
- (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];
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED - please use RCTReloadCommand.
|
|
*/
|
|
- (void)reload
|
|
{
|
|
RCTTriggerReloadCommandListeners(@"Unknown from bridge");
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED - please use RCTReloadCommand.
|
|
*/
|
|
- (void)reloadWithReason:(NSString *)reason
|
|
{
|
|
RCTTriggerReloadCommandListeners(reason);
|
|
}
|
|
|
|
- (void)onFastRefresh
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTBridgeFastRefreshNotification object:self];
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED - please use RCTReloadCommand.
|
|
*/
|
|
- (void)requestReload
|
|
{
|
|
[self reloadWithReason:@"Requested from bridge"];
|
|
}
|
|
|
|
- (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;
|
|
|
|
// 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];
|
|
|
|
RCTExecuteOnMainQueue(^{
|
|
RCTRegisterReloadCommandListener(self);
|
|
RCTReloadCommandSetBundleURL(self->_bundleURL);
|
|
});
|
|
|
|
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
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTBridgeWillBeInvalidatedNotification object:self];
|
|
|
|
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];
|
|
}
|
|
|
|
- (void)loadAndExecuteSplitBundleURL:(NSURL *)bundleURL
|
|
onError:(RCTLoadAndExecuteErrorBlock)onError
|
|
onComplete:(dispatch_block_t)onComplete
|
|
{
|
|
[self.batchedBridge loadAndExecuteSplitBundleURL:bundleURL onError:onError onComplete:onComplete];
|
|
}
|
|
|
|
@end
|