/* * Copyright (c) Meta Platforms, Inc. and 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 #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" static NSMutableArray *RCTModuleClasses; static dispatch_queue_t RCTModuleClassesSyncQueue; NSArray *RCTGetModuleClasses(void) { __block NSArray *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. * TODO: (T115656171) Refactor RCTRegisterModule out of Bridge.m since it doesn't use the Bridge. */ 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) { #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; } // Turn off TurboModule delegate locking static BOOL turboModuleManagerDelegateLockingDisabled = YES; BOOL RCTTurboModuleManagerDelegateLockingDisabled(void) { return turboModuleManagerDelegateLockingDisabled; } void RCTDisableTurboModuleManagerDelegateLocking(BOOL disabled) { turboModuleManagerDelegateLockingDisabled = disabled; } @interface RCTBridge () @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)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)delegate bundleURL:(NSURL *)bundleURL moduleProvider:(RCTBridgeModuleListProvider)block launchOptions:(NSDictionary *)launchOptions { if (self = [super init]) { RCTEnforceNewArchitectureValidation(RCTNotAllowedInBridgeless, 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)turboModuleRegistry { [self.batchedBridge setRCTTurboModuleRegistry:turboModuleRegistry]; } - (void)attachBridgeAPIsToTurboModule:(id)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 *)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)instance { [self.batchedBridge updateModuleWithInstance:instance]; } - (void)registerAdditionalModuleClasses:(NSArray *)modules { [self.batchedBridge registerAdditionalModuleClasses:modules]; } - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args { NSArray *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