@@ -138,8 +138,8 @@ @implementation RCTCxxBridge
138138
139139 // Native modules
140140 NSMutableDictionary <NSString *, RCTModuleData *> *_moduleDataByName;
141- NSArray <RCTModuleData *> *_moduleDataByID;
142- NSArray <Class > *_moduleClassesByID;
141+ NSMutableArray <RCTModuleData *> *_moduleDataByID;
142+ NSMutableArray <Class > *_moduleClassesByID;
143143 NSUInteger _modulesInitializedOnMainQueue;
144144 RCTDisplayLink *_displayLink;
145145
@@ -195,6 +195,10 @@ - (instancetype)initWithParentBridge:(RCTBridge *)bridge
195195 _pendingCalls = [NSMutableArray new ];
196196 _displayLink = [RCTDisplayLink new ];
197197
198+ _moduleDataByName = [NSMutableDictionary new ];
199+ _moduleClassesByID = [NSMutableArray new ];
200+ _moduleDataByID = [NSMutableArray new ];
201+
198202 [RCTBridge setCurrentBridge: self ];
199203 }
200204 return self;
@@ -275,8 +279,13 @@ - (void)start
275279
276280 dispatch_group_t prepareBridge = dispatch_group_create ();
277281
282+ [_performanceLogger markStartForTag: RCTPLNativeModuleInit];
283+
284+ [self registerExtraModules ];
278285 // Initialize all native modules that cannot be loaded lazily
279- [self _initModulesWithDispatchGroup: prepareBridge];
286+ [self _initModules: RCTGetModuleClasses () withDispatchGroup: prepareBridge lazilyDiscovered: NO ];
287+
288+ [_performanceLogger markStopForTag: RCTPLNativeModuleInit];
280289
281290 // This doesn't really do anything. The real work happens in initializeBridge.
282291 _reactInstance.reset (new Instance);
@@ -442,7 +451,16 @@ - (BOOL)moduleIsInitialized:(Class)moduleClass
442451 [_performanceLogger markStartForTag: RCTPLNativeModulePrepareConfig];
443452 RCT_PROFILE_BEGIN_EVENT (RCTProfileTagAlways, @" -[RCTCxxBridge buildModuleRegistry]" , nil );
444453
445- auto registry = std::make_shared<ModuleRegistry>(createNativeModules (_moduleDataByID, self, _reactInstance));
454+ __weak __typeof (self) weakSelf = self;
455+ ModuleRegistry::ModuleNotFoundCallback moduleNotFoundCallback = ^bool (const std::string &name) {
456+ __strong __typeof (weakSelf) strongSelf = weakSelf;
457+ return [strongSelf.delegate respondsToSelector: @selector (bridge:didNotFindModule: )] &&
458+ [strongSelf.delegate bridge: strongSelf didNotFindModule: @(name.c_str ())];
459+ };
460+
461+ auto registry = std::make_shared<ModuleRegistry>(
462+ createNativeModules (_moduleDataByID, self, _reactInstance),
463+ moduleNotFoundCallback);
446464
447465 [_performanceLogger markStopForTag: RCTPLNativeModulePrepareConfig];
448466 RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
@@ -501,20 +519,66 @@ - (NSArray *)configForModuleName:(NSString *)moduleName
501519 return moduleData.config ;
502520}
503521
504- - (void ) _initModulesWithDispatchGroup : ( dispatch_group_t ) dispatchGroup
522+ - (NSArray <RCTModuleData *> *) registerModulesForClasses : ( NSArray <Class> *) moduleClasses
505523{
506- [_performanceLogger markStartForTag: RCTPLNativeModuleInit];
524+ RCT_PROFILE_BEGIN_EVENT (RCTProfileTagAlways,
525+ @" -[RCTCxxBridge initModulesWithDispatchGroup:] autoexported moduleData" , nil );
526+
527+ NSMutableArray <RCTModuleData *> *moduleDataByID = [NSMutableArray arrayWithCapacity: moduleClasses.count];
528+ for (Class moduleClass in moduleClasses) {
529+ NSString *moduleName = RCTBridgeModuleNameForClass (moduleClass);
530+
531+ // Don't initialize the old executor in the new bridge.
532+ // TODO mhorowitz #10487027: after D3175632 lands, we won't need
533+ // this, because it won't be eagerly initialized.
534+ if ([moduleName isEqualToString: @" RCTJSCExecutor" ]) {
535+ continue ;
536+ }
537+
538+ // Check for module name collisions
539+ RCTModuleData *moduleData = _moduleDataByName[moduleName];
540+ if (moduleData) {
541+ if (moduleData.hasInstance ) {
542+ // Existing module was preregistered, so it takes precedence
543+ continue ;
544+ } else if ([moduleClass new ] == nil ) {
545+ // The new module returned nil from init, so use the old module
546+ continue ;
547+ } else if ([moduleData.moduleClass new ] != nil ) {
548+ // Both modules were non-nil, so it's unclear which should take precedence
549+ RCTLogError (@" Attempted to register RCTBridgeModule class %@ for the "
550+ " name '%@ ', but name was already registered by class %@ " ,
551+ moduleClass, moduleName, moduleData.moduleClass );
552+ }
553+ }
554+
555+ // Instantiate moduleData
556+ // TODO #13258411: can we defer this until config generation?
557+ moduleData = [[RCTModuleData alloc ] initWithModuleClass: moduleClass bridge: self ];
507558
559+ _moduleDataByName[moduleName] = moduleData;
560+ [_moduleClassesByID addObject: moduleClass];
561+ [moduleDataByID addObject: moduleData];
562+ }
563+ [_moduleDataByID addObjectsFromArray: moduleDataByID];
564+
565+ RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
566+
567+ return moduleDataByID;
568+ }
569+
570+ - (void )registerExtraModules
571+ {
508572 RCT_PROFILE_BEGIN_EVENT (RCTProfileTagAlways,
509573 @" -[RCTCxxBridge initModulesWithDispatchGroup:] extraModules" , nil );
574+
510575 NSArray <id <RCTBridgeModule>> *extraModules = nil ;
511- if (self.delegate ) {
512- if ([self .delegate respondsToSelector: @selector (extraModulesForBridge: )]) {
513- extraModules = [self .delegate extraModulesForBridge: _parentBridge];
514- }
576+ if ([self .delegate respondsToSelector: @selector (extraModulesForBridge: )]) {
577+ extraModules = [self .delegate extraModulesForBridge: _parentBridge];
515578 } else if (self.moduleProvider ) {
516579 extraModules = self.moduleProvider ();
517580 }
581+
518582 RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
519583
520584#if RCT_DEBUG
@@ -524,10 +588,6 @@ - (void)_initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
524588 });
525589#endif
526590
527- NSMutableArray <Class > *moduleClassesByID = [NSMutableArray new ];
528- NSMutableArray <RCTModuleData *> *moduleDataByID = [NSMutableArray new ];
529- NSMutableDictionary <NSString *, RCTModuleData *> *moduleDataByName = [NSMutableDictionary new ];
530-
531591 RCT_PROFILE_BEGIN_EVENT (RCTProfileTagAlways,
532592 @" -[RCTCxxBridge initModulesWithDispatchGroup:] preinitialized moduleData" , nil );
533593 // Set up moduleData for pre-initialized module instances
@@ -537,7 +597,7 @@ - (void)_initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
537597
538598 if (RCT_DEBUG) {
539599 // Check for name collisions between preregistered modules
540- RCTModuleData *moduleData = moduleDataByName [moduleName];
600+ RCTModuleData *moduleData = _moduleDataByName [moduleName];
541601 if (moduleData) {
542602 RCTLogError (@" Attempted to register RCTBridgeModule class %@ for the "
543603 " name '%@ ', but name was already registered by class %@ " ,
@@ -547,84 +607,60 @@ - (void)_initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
547607 }
548608
549609 // Instantiate moduleData container
550- RCTModuleData *moduleData = [[RCTModuleData alloc ] initWithModuleInstance: module
551- bridge: self ];
552- moduleDataByName[moduleName] = moduleData;
553- [moduleClassesByID addObject: moduleClass];
554- [moduleDataByID addObject: moduleData];
610+ RCTModuleData *moduleData = [[RCTModuleData alloc ] initWithModuleInstance: module bridge: self ];
611+ _moduleDataByName[moduleName] = moduleData;
612+ [_moduleClassesByID addObject: moduleClass];
613+ [_moduleDataByID addObject: moduleData];
555614 }
556615 RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
616+ }
557617
558- RCT_PROFILE_BEGIN_EVENT (RCTProfileTagAlways,
559- @" -[RCTCxxBridge initModulesWithDispatchGroup:] autoexported moduleData " , nil );
560- // Set up moduleData for automatically-exported modules
561- for ( Class moduleClass in RCTGetModuleClasses ()) {
562- NSString *moduleName = RCTBridgeModuleNameForClass (moduleClass );
618+ - ( void ) _initModules : ( NSArray <id<RCTBridgeModule>> *) modules
619+ withDispatchGroup : ( dispatch_group_t ) dispatchGroup
620+ lazilyDiscovered : ( BOOL ) lazilyDiscovered
621+ {
622+ RCTAssert (!( RCTIsMainQueue () && lazilyDiscovered), @" Lazy discovery can only happen off the Main Queue " );
563623
564- // Don't initialize the old executor in the new bridge.
565- // TODO mhorowitz #10487027: after D3175632 lands, we won't need
566- // this, because it won't be eagerly initialized.
567- if ([moduleName isEqual: @" RCTJSCExecutor" ]) {
568- continue ;
624+ // Set up moduleData for automatically-exported modules
625+ NSArray <RCTModuleData *> *moduleDataById = [self registerModulesForClasses: modules];
626+
627+ #ifdef RCT_DEBUG
628+ if (lazilyDiscovered) {
629+ // Lazily discovered modules do not require instantiation here,
630+ // as they are not allowed to have pre-instantiated instance
631+ // and must not require the main queue.
632+ for (RCTModuleData *moduleData in moduleDataById) {
633+ RCTAssert (!(moduleData.requiresMainQueueSetup || moduleData.hasInstance ),
634+ @" Module \' %@ \' requires initialization on the Main Queue or has pre-instantiated, which is not supported for the lazily discovered modules." , moduleData.name );
569635 }
570-
571- // Check for module name collisions
572- RCTModuleData *moduleData = moduleDataByName[moduleName];
573- if (moduleData) {
574- if (moduleData.hasInstance ) {
575- // Existing module was preregistered, so it takes precedence
576- continue ;
577- } else if ([moduleClass new ] == nil ) {
578- // The new module returned nil from init, so use the old module
579- continue ;
580- } else if ([moduleData.moduleClass new ] != nil ) {
581- // Both modules were non-nil, so it's unclear which should take precedence
582- RCTLogError (@" Attempted to register RCTBridgeModule class %@ for the "
583- " name '%@ ', but name was already registered by class %@ " ,
584- moduleClass, moduleName, moduleData.moduleClass );
636+ }
637+ else
638+ #endif
639+ {
640+ RCT_PROFILE_BEGIN_EVENT (RCTProfileTagAlways,
641+ @" -[RCTCxxBridge initModulesWithDispatchGroup:] moduleData.hasInstance" , nil );
642+ // Dispatch module init onto main thread for those modules that require it
643+ // For non-lazily discovered modules we run through the entire set of modules
644+ // that we have, otherwise some modules coming from the delegate
645+ // or module provider block, will not be properly instantiated.
646+ for (RCTModuleData *moduleData in _moduleDataByID) {
647+ if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue ())) {
648+ // Modules that were pre-initialized should ideally be set up before
649+ // bridge init has finished, otherwise the caller may try to access the
650+ // module directly rather than via `[bridge moduleForClass:]`, which won't
651+ // trigger the lazy initialization process. If the module cannot safely be
652+ // set up on the current thread, it will instead be async dispatched
653+ // to the main thread to be set up in _prepareModulesWithDispatchGroup:.
654+ (void )[moduleData instance ];
585655 }
586656 }
657+ RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
587658
588- // Instantiate moduleData
589- // TODO #13258411: can we defer this until config generation?
590- moduleData = [[RCTModuleData alloc ] initWithModuleClass: moduleClass
591- bridge: self ];
592- moduleDataByName[moduleName] = moduleData;
593- [moduleClassesByID addObject: moduleClass];
594- [moduleDataByID addObject: moduleData];
595- }
596- RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
597-
598- // Store modules
599- _moduleDataByID = [moduleDataByID copy ];
600- _moduleDataByName = [moduleDataByName copy ];
601- _moduleClassesByID = [moduleClassesByID copy ];
602-
603- RCT_PROFILE_BEGIN_EVENT (RCTProfileTagAlways,
604- @" -[RCTCxxBridge initModulesWithDispatchGroup:] moduleData.hasInstance" , nil );
605- // Dispatch module init onto main thead for those modules that require it
606- for (RCTModuleData *moduleData in _moduleDataByID) {
607- if (moduleData.hasInstance &&
608- (!moduleData.requiresMainQueueSetup || RCTIsMainQueue ())) {
609- // Modules that were pre-initialized should ideally be set up before
610- // bridge init has finished, otherwise the caller may try to access the
611- // module directly rather than via `[bridge moduleForClass:]`, which won't
612- // trigger the lazy initialization process. If the module cannot safely be
613- // set up on the current thread, it will instead be async dispatched
614- // to the main thread to be set up in the loop below.
615- (void )[moduleData instance ];
616- }
659+ // From this point on, RCTDidInitializeModuleNotification notifications will
660+ // be sent the first time a module is accessed.
661+ _moduleSetupComplete = YES ;
662+ [self _prepareModulesWithDispatchGroup: dispatchGroup];
617663 }
618- RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
619-
620- // From this point on, RCTDidInitializeModuleNotification notifications will
621- // be sent the first time a module is accessed.
622- _moduleSetupComplete = YES ;
623-
624- [self _prepareModulesWithDispatchGroup: dispatchGroup];
625-
626- [_performanceLogger markStopForTag: RCTPLNativeModuleInit];
627- RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
628664
629665#if RCT_PROFILE
630666 if (RCTProfileIsProfiling ()) {
@@ -634,6 +670,11 @@ - (void)_initModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
634670#endif
635671}
636672
673+ - (void )registerAdditionalModuleClasses : (NSArray <Class> *)modules
674+ {
675+ [self _initModules: modules withDispatchGroup: NULL lazilyDiscovered: YES ];
676+ }
677+
637678- (void )_prepareModulesWithDispatchGroup : (dispatch_group_t )dispatchGroup
638679{
639680 RCT_PROFILE_BEGIN_EVENT (0 , @" -[RCTBatchedBridge prepareModulesWithDispatch]" , nil );
@@ -655,6 +696,7 @@ - (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
655696
656697 // Set up modules that require main thread init or constants export
657698 [_performanceLogger setValue: 0 forTag: RCTPLNativeModuleMainThread];
699+
658700 for (RCTModuleData *moduleData in _moduleDataByID) {
659701 if (whitelistedModules && ![whitelistedModules containsObject: [moduleData moduleClass ]]) {
660702 continue ;
@@ -687,7 +729,6 @@ - (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
687729 _modulesInitializedOnMainQueue++;
688730 }
689731 }
690-
691732 [_performanceLogger setValue: _modulesInitializedOnMainQueue forTag: RCTPLNativeModuleMainThreadUsesCount];
692733 RCT_PROFILE_END_EVENT (RCTProfileTagAlways, @" " );
693734}
0 commit comments