12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460 |
- /*
- * 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.
- */
- #include <atomic>
- #include <future>
- #import <React/RCTAssert.h>
- #import <React/RCTBridge+Private.h>
- #import <React/RCTBridge.h>
- #import <React/RCTBridgeMethod.h>
- #import <React/RCTConvert.h>
- #import <React/RCTCxxBridgeDelegate.h>
- #import <React/RCTCxxModule.h>
- #import <React/RCTCxxUtils.h>
- #import <React/RCTDevSettings.h>
- #import <React/RCTDisplayLink.h>
- #import <React/RCTFollyConvert.h>
- #import <React/RCTJavaScriptLoader.h>
- #import <React/RCTLog.h>
- #import <React/RCTModuleData.h>
- #import <React/RCTPerformanceLogger.h>
- #import <React/RCTProfile.h>
- #import <React/RCTRedBox.h>
- #import <React/RCTReloadCommand.h>
- #import <React/RCTUtils.h>
- #import <cxxreact/CxxNativeModule.h>
- #import <cxxreact/Instance.h>
- #import <cxxreact/JSBundleType.h>
- #import <cxxreact/JSIndexedRAMBundle.h>
- #import <cxxreact/ModuleRegistry.h>
- #import <cxxreact/RAMBundleRegistry.h>
- #import <cxxreact/ReactMarker.h>
- #import <jsireact/JSIExecutor.h>
- #import "JSCExecutorFactory.h"
- #import "NSDataBigString.h"
- #import "RCTMessageThread.h"
- #import "RCTObjcExecutor.h"
- #ifdef WITH_FBSYSTRACE
- #import <React/RCTFBSystrace.h>
- #endif
- #if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include(<React/RCTDevLoadingViewProtocol.h>)
- #import <React/RCTDevLoadingViewProtocol.h>
- #endif
- static NSString *const RCTJSThreadName = @"com.facebook.react.JavaScript";
- typedef void (^RCTPendingCall)();
- using namespace facebook::jsi;
- using namespace facebook::react;
- /**
- * Must be kept in sync with `MessageQueue.js`.
- */
- typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
- RCTBridgeFieldRequestModuleIDs = 0,
- RCTBridgeFieldMethodIDs,
- RCTBridgeFieldParams,
- RCTBridgeFieldCallID,
- };
- namespace {
- class GetDescAdapter : public JSExecutorFactory {
- public:
- GetDescAdapter(RCTCxxBridge *bridge, std::shared_ptr<JSExecutorFactory> factory) : bridge_(bridge), factory_(factory)
- {
- }
- std::unique_ptr<JSExecutor> createJSExecutor(
- std::shared_ptr<ExecutorDelegate> delegate,
- std::shared_ptr<MessageQueueThread> jsQueue) override
- {
- auto ret = factory_->createJSExecutor(delegate, jsQueue);
- bridge_.bridgeDescription = @(ret->getDescription().c_str());
- return ret;
- }
- private:
- RCTCxxBridge *bridge_;
- std::shared_ptr<JSExecutorFactory> factory_;
- };
- }
- static bool isRAMBundle(NSData *script)
- {
- BundleHeader header;
- [script getBytes:&header length:sizeof(header)];
- return parseTypeFromHeader(header) == ScriptTag::RAMBundle;
- }
- static void notifyAboutModuleSetup(RCTPerformanceLogger *performanceLogger, const char *tag)
- {
- NSString *moduleName = [[NSString alloc] initWithUTF8String:tag];
- if (moduleName) {
- int64_t setupTime = [performanceLogger durationForTag:RCTPLNativeModuleSetup];
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTDidSetupModuleNotification
- object:nil
- userInfo:@{
- RCTDidSetupModuleNotificationModuleNameKey : moduleName,
- RCTDidSetupModuleNotificationSetupTimeKey : @(setupTime)
- }];
- }
- }
- static void registerPerformanceLoggerHooks(RCTPerformanceLogger *performanceLogger)
- {
- __weak RCTPerformanceLogger *weakPerformanceLogger = performanceLogger;
- ReactMarker::logTaggedMarker = [weakPerformanceLogger](
- const ReactMarker::ReactMarkerId markerId, const char *__unused tag) {
- switch (markerId) {
- case ReactMarker::RUN_JS_BUNDLE_START:
- [weakPerformanceLogger markStartForTag:RCTPLScriptExecution];
- break;
- case ReactMarker::RUN_JS_BUNDLE_STOP:
- [weakPerformanceLogger markStopForTag:RCTPLScriptExecution];
- break;
- case ReactMarker::NATIVE_REQUIRE_START:
- [weakPerformanceLogger appendStartForTag:RCTPLRAMNativeRequires];
- break;
- case ReactMarker::NATIVE_REQUIRE_STOP:
- [weakPerformanceLogger appendStopForTag:RCTPLRAMNativeRequires];
- [weakPerformanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount];
- break;
- case ReactMarker::NATIVE_MODULE_SETUP_START:
- [weakPerformanceLogger markStartForTag:RCTPLNativeModuleSetup];
- break;
- case ReactMarker::NATIVE_MODULE_SETUP_STOP:
- [weakPerformanceLogger markStopForTag:RCTPLNativeModuleSetup];
- notifyAboutModuleSetup(weakPerformanceLogger, tag);
- break;
- case ReactMarker::CREATE_REACT_CONTEXT_STOP:
- case ReactMarker::JS_BUNDLE_STRING_CONVERT_START:
- case ReactMarker::JS_BUNDLE_STRING_CONVERT_STOP:
- case ReactMarker::REGISTER_JS_SEGMENT_START:
- case ReactMarker::REGISTER_JS_SEGMENT_STOP:
- // These are not used on iOS.
- break;
- }
- };
- }
- @interface RCTCxxBridge ()
- @property (nonatomic, weak, readonly) RCTBridge *parentBridge;
- @property (nonatomic, assign, readonly) BOOL moduleSetupComplete;
- - (instancetype)initWithParentBridge:(RCTBridge *)bridge;
- - (void)partialBatchDidFlush;
- - (void)batchDidComplete;
- @end
- struct RCTInstanceCallback : public InstanceCallback {
- __weak RCTCxxBridge *bridge_;
- RCTInstanceCallback(RCTCxxBridge *bridge) : bridge_(bridge){};
- void onBatchComplete() override
- {
- // There's no interface to call this per partial batch
- [bridge_ partialBatchDidFlush];
- [bridge_ batchDidComplete];
- }
- };
- @implementation RCTCxxBridge {
- BOOL _didInvalidate;
- BOOL _moduleRegistryCreated;
- NSMutableArray<RCTPendingCall> *_pendingCalls;
- std::atomic<NSInteger> _pendingCount;
- // Native modules
- NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName;
- NSMutableArray<RCTModuleData *> *_moduleDataByID;
- NSMutableArray<Class> *_moduleClassesByID;
- NSUInteger _modulesInitializedOnMainQueue;
- RCTDisplayLink *_displayLink;
- // JS thread management
- NSThread *_jsThread;
- std::shared_ptr<RCTMessageThread> _jsMessageThread;
- std::mutex _moduleRegistryLock;
- // This is uniquely owned, but weak_ptr is used.
- std::shared_ptr<Instance> _reactInstance;
- // Necessary for searching in TurboModuleRegistry
- id<RCTTurboModuleLookupDelegate> _turboModuleLookupDelegate;
- }
- @synthesize bridgeDescription = _bridgeDescription;
- @synthesize loading = _loading;
- @synthesize performanceLogger = _performanceLogger;
- @synthesize valid = _valid;
- - (void)setRCTTurboModuleLookupDelegate:(id<RCTTurboModuleLookupDelegate>)turboModuleLookupDelegate
- {
- _turboModuleLookupDelegate = turboModuleLookupDelegate;
- }
- - (std::shared_ptr<MessageQueueThread>)jsMessageThread
- {
- return _jsMessageThread;
- }
- - (BOOL)isInspectable
- {
- return _reactInstance ? _reactInstance->isInspectable() : NO;
- }
- - (instancetype)initWithParentBridge:(RCTBridge *)bridge
- {
- RCTAssertParam(bridge);
- if ((self = [super initWithDelegate:bridge.delegate
- bundleURL:bridge.bundleURL
- moduleProvider:bridge.moduleProvider
- launchOptions:bridge.launchOptions])) {
- _parentBridge = bridge;
- _performanceLogger = [bridge performanceLogger];
- registerPerformanceLoggerHooks(_performanceLogger);
- /**
- * Set Initial State
- */
- _valid = YES;
- _loading = YES;
- _moduleRegistryCreated = NO;
- _pendingCalls = [NSMutableArray new];
- _displayLink = [RCTDisplayLink new];
- _moduleDataByName = [NSMutableDictionary new];
- _moduleClassesByID = [NSMutableArray new];
- _moduleDataByID = [NSMutableArray new];
- [RCTBridge setCurrentBridge:self];
- }
- return self;
- }
- + (void)runRunLoop
- {
- @autoreleasepool {
- RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge runJSRunLoop] setup", nil);
- // copy thread name to pthread name
- pthread_setname_np([NSThread currentThread].name.UTF8String);
- // Set up a dummy runloop source to avoid spinning
- CFRunLoopSourceContext noSpinCtx = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
- CFRunLoopSourceRef noSpinSource = CFRunLoopSourceCreate(NULL, 0, &noSpinCtx);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), noSpinSource, kCFRunLoopDefaultMode);
- CFRelease(noSpinSource);
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- // run the run loop
- while (kCFRunLoopRunStopped !=
- CFRunLoopRunInMode(
- kCFRunLoopDefaultMode, ((NSDate *)[NSDate distantFuture]).timeIntervalSinceReferenceDate, NO)) {
- RCTAssert(NO, @"not reached assertion"); // runloop spun. that's bad.
- }
- }
- }
- - (void)_tryAndHandleError:(dispatch_block_t)block
- {
- NSError *error = tryAndReturnError(block);
- if (error) {
- [self handleError:error];
- }
- }
- /**
- * Ensure block is run on the JS thread. If we're already on the JS thread, the block will execute synchronously.
- * If we're not on the JS thread, the block is dispatched to that thread. Any errors encountered while executing
- * the block will go through handleError:
- */
- - (void)ensureOnJavaScriptThread:(dispatch_block_t)block
- {
- RCTAssert(_jsThread, @"This method must not be called before the JS thread is created");
- // This does not use _jsMessageThread because it may be called early before the runloop reference is captured
- // and _jsMessageThread is valid. _jsMessageThread also doesn't allow us to shortcut the dispatch if we're
- // already on the correct thread.
- if ([NSThread currentThread] == _jsThread) {
- [self _tryAndHandleError:block];
- } else {
- [self performSelector:@selector(_tryAndHandleError:) onThread:_jsThread withObject:block waitUntilDone:NO];
- }
- }
- - (void)start
- {
- RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge start]", nil);
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
- object:_parentBridge
- userInfo:@{@"bridge" : self}];
- // Set up the JS thread early
- _jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
- _jsThread.name = RCTJSThreadName;
- _jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
- #if RCT_DEBUG
- _jsThread.stackSize *= 2;
- #endif
- [_jsThread start];
- dispatch_group_t prepareBridge = dispatch_group_create();
- [_performanceLogger markStartForTag:RCTPLNativeModuleInit];
- [self registerExtraModules];
- // Initialize all native modules that cannot be loaded lazily
- (void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
- [self registerExtraLazyModules];
- [_performanceLogger markStopForTag:RCTPLNativeModuleInit];
- // This doesn't really do anything. The real work happens in initializeBridge.
- _reactInstance.reset(new Instance);
- __weak RCTCxxBridge *weakSelf = self;
- // Prepare executor factory (shared_ptr for copy into block)
- std::shared_ptr<JSExecutorFactory> executorFactory;
- if (!self.executorClass) {
- if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
- id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>)self.delegate;
- executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
- }
- if (!executorFactory) {
- executorFactory = std::make_shared<JSCExecutorFactory>(nullptr);
- }
- } else {
- id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
- executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
- if (error) {
- [weakSelf handleError:error];
- }
- }));
- }
- // Dispatch the instance initialization as soon as the initial module metadata has
- // been collected (see initModules)
- dispatch_group_enter(prepareBridge);
- [self ensureOnJavaScriptThread:^{
- [weakSelf _initializeBridge:executorFactory];
- dispatch_group_leave(prepareBridge);
- }];
- // Load the source asynchronously, then store it for later execution.
- dispatch_group_enter(prepareBridge);
- __block NSData *sourceCode;
- [self
- loadSource:^(NSError *error, RCTSource *source) {
- if (error) {
- [weakSelf handleError:error];
- }
- sourceCode = source.data;
- dispatch_group_leave(prepareBridge);
- }
- onProgress:^(RCTLoadingProgress *progressData) {
- #if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include(<React/RCTDevLoadingViewProtocol.h>)
- id<RCTDevLoadingViewProtocol> loadingView = [weakSelf moduleForName:@"DevLoadingView"
- lazilyLoadIfNecessary:YES];
- [loadingView updateProgress:progressData];
- #endif
- }];
- // Wait for both the modules and source code to have finished loading
- dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
- RCTCxxBridge *strongSelf = weakSelf;
- if (sourceCode && strongSelf.loading) {
- [strongSelf executeSourceCode:sourceCode sync:NO];
- }
- });
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- }
- - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
- {
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- [center postNotificationName:RCTBridgeWillDownloadScriptNotification object:_parentBridge];
- [_performanceLogger markStartForTag:RCTPLScriptDownload];
- NSUInteger cookie = RCTProfileBeginAsyncEvent(0, @"JavaScript download", nil);
- // Suppress a warning if RCTProfileBeginAsyncEvent gets compiled out
- (void)cookie;
- RCTPerformanceLogger *performanceLogger = _performanceLogger;
- RCTSourceLoadBlock onSourceLoad = ^(NSError *error, RCTSource *source) {
- RCTProfileEndAsyncEvent(0, @"native", cookie, @"JavaScript download", @"JS async");
- [performanceLogger markStopForTag:RCTPLScriptDownload];
- [performanceLogger setValue:source.length forTag:RCTPLBundleSize];
- NSDictionary *userInfo = @{
- RCTBridgeDidDownloadScriptNotificationSourceKey : source ?: [NSNull null],
- RCTBridgeDidDownloadScriptNotificationBridgeDescriptionKey : self->_bridgeDescription ?: [NSNull null],
- };
- [center postNotificationName:RCTBridgeDidDownloadScriptNotification object:self->_parentBridge userInfo:userInfo];
- _onSourceLoad(error, source);
- };
- if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:onProgress:onComplete:)]) {
- [self.delegate loadSourceForBridge:_parentBridge onProgress:onProgress onComplete:onSourceLoad];
- } else if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
- [self.delegate loadSourceForBridge:_parentBridge withBlock:onSourceLoad];
- } else if (!self.bundleURL) {
- NSError *error = RCTErrorWithMessage(
- @"No bundle URL present.\n\nMake sure you're running a packager "
- "server or have included a .jsbundle file in your application bundle.");
- onSourceLoad(error, nil);
- } else {
- __weak RCTCxxBridge *weakSelf = self;
- [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL
- onProgress:onProgress
- onComplete:^(NSError *error, RCTSource *source) {
- if (error) {
- [weakSelf handleError:error];
- return;
- }
- onSourceLoad(error, source);
- }];
- }
- }
- - (NSArray<Class> *)moduleClasses
- {
- if (RCT_DEBUG && _valid && _moduleClassesByID == nil) {
- RCTLogError(
- @"Bridge modules have not yet been initialized. You may be "
- "trying to access a module too early in the startup procedure.");
- }
- return _moduleClassesByID;
- }
- /**
- * Used by RCTUIManager
- */
- - (RCTModuleData *)moduleDataForName:(NSString *)moduleName
- {
- return _moduleDataByName[moduleName];
- }
- - (id)moduleForName:(NSString *)moduleName
- {
- return [self moduleForName:moduleName lazilyLoadIfNecessary:NO];
- }
- - (id)moduleForName:(NSString *)moduleName lazilyLoadIfNecessary:(BOOL)lazilyLoad
- {
- if (RCTTurboModuleEnabled() && _turboModuleLookupDelegate) {
- const char *moduleNameCStr = [moduleName UTF8String];
- if (lazilyLoad || [_turboModuleLookupDelegate moduleIsInitialized:moduleNameCStr]) {
- id<RCTTurboModule> module = [_turboModuleLookupDelegate moduleForName:moduleNameCStr warnOnLookupFailure:NO];
- if (module != nil) {
- return module;
- }
- }
- }
- if (!lazilyLoad) {
- return _moduleDataByName[moduleName].instance;
- }
- RCTModuleData *moduleData = _moduleDataByName[moduleName];
- if (moduleData) {
- if (![moduleData isKindOfClass:[RCTModuleData class]]) {
- // There is rare race condition where the data stored in the dictionary
- // may have been deallocated, which means the module instance is no longer
- // usable.
- return nil;
- }
- return moduleData.instance;
- }
- // Module may not be loaded yet, so attempt to force load it here.
- const BOOL result = [self.delegate respondsToSelector:@selector(bridge:didNotFindModule:)] &&
- [self.delegate bridge:self didNotFindModule:moduleName];
- if (result) {
- // Try again.
- moduleData = _moduleDataByName[moduleName];
- #if RCT_DEV
- // If the `_moduleDataByName` is nil, it must have been cleared by the reload.
- } else if (_moduleDataByName != nil) {
- RCTLogError(@"Unable to find module for %@", moduleName);
- }
- #else
- } else {
- RCTLogError(@"Unable to find module for %@", moduleName);
- }
- #endif
- return moduleData.instance;
- }
- - (BOOL)moduleIsInitialized:(Class)moduleClass
- {
- NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
- if (_moduleDataByName[moduleName].hasInstance) {
- return YES;
- }
- if (_turboModuleLookupDelegate) {
- return [_turboModuleLookupDelegate moduleIsInitialized:[moduleName UTF8String]];
- }
- return NO;
- }
- - (id)moduleForClass:(Class)moduleClass
- {
- return [self moduleForName:RCTBridgeModuleNameForClass(moduleClass) lazilyLoadIfNecessary:YES];
- }
- - (std::shared_ptr<ModuleRegistry>)_buildModuleRegistryUnlocked
- {
- if (!self.valid) {
- return {};
- }
- [_performanceLogger markStartForTag:RCTPLNativeModulePrepareConfig];
- RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge buildModuleRegistry]", nil);
- __weak __typeof(self) weakSelf = self;
- ModuleRegistry::ModuleNotFoundCallback moduleNotFoundCallback = ^bool(const std::string &name) {
- __strong __typeof(weakSelf) strongSelf = weakSelf;
- return [strongSelf.delegate respondsToSelector:@selector(bridge:didNotFindModule:)] &&
- [strongSelf.delegate bridge:strongSelf didNotFindModule:@(name.c_str())];
- };
- auto registry = std::make_shared<ModuleRegistry>(
- createNativeModules(_moduleDataByID, self, _reactInstance), moduleNotFoundCallback);
- [_performanceLogger markStopForTag:RCTPLNativeModulePrepareConfig];
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- return registry;
- }
- - (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory
- {
- if (!self.valid) {
- return;
- }
- __weak RCTCxxBridge *weakSelf = self;
- _jsMessageThread = std::make_shared<RCTMessageThread>([NSRunLoop currentRunLoop], ^(NSError *error) {
- if (error) {
- [weakSelf handleError:error];
- }
- });
- RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge initializeBridge:]", nil);
- // This can only be false if the bridge was invalidated before startup completed
- if (_reactInstance) {
- #if RCT_DEV
- executorFactory = std::make_shared<GetDescAdapter>(self, executorFactory);
- #endif
- [self _initializeBridgeLocked:executorFactory];
- #if RCT_PROFILE
- if (RCTProfileIsProfiling()) {
- _reactInstance->setGlobalVariable("__RCTProfileIsProfiling", std::make_unique<JSBigStdString>("true"));
- }
- #endif
- }
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- }
- - (void)_initializeBridgeLocked:(std::shared_ptr<JSExecutorFactory>)executorFactory
- {
- std::lock_guard<std::mutex> guard(_moduleRegistryLock);
- // This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
- _reactInstance->initializeBridge(
- std::make_unique<RCTInstanceCallback>(self),
- executorFactory,
- _jsMessageThread,
- [self _buildModuleRegistryUnlocked]);
- _moduleRegistryCreated = YES;
- }
- - (void)updateModuleWithInstance:(id<RCTBridgeModule>)instance
- {
- NSString *const moduleName = RCTBridgeModuleNameForClass([instance class]);
- if (moduleName) {
- RCTModuleData *const moduleData = _moduleDataByName[moduleName];
- if (moduleData) {
- moduleData.instance = instance;
- }
- }
- }
- - (NSArray<RCTModuleData *> *)registerModulesForClasses:(NSArray<Class> *)moduleClasses
- {
- return [self _registerModulesForClasses:moduleClasses lazilyDiscovered:NO];
- }
- - (NSArray<RCTModuleData *> *)_registerModulesForClasses:(NSArray<Class> *)moduleClasses
- lazilyDiscovered:(BOOL)lazilyDiscovered
- {
- RCT_PROFILE_BEGIN_EVENT(
- RCTProfileTagAlways, @"-[RCTCxxBridge initModulesWithDispatchGroup:] autoexported moduleData", nil);
- NSArray *moduleClassesCopy = [moduleClasses copy];
- NSMutableArray<RCTModuleData *> *moduleDataByID = [NSMutableArray arrayWithCapacity:moduleClassesCopy.count];
- for (Class moduleClass in moduleClassesCopy) {
- if (RCTTurboModuleEnabled() && [moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
- continue;
- }
- NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
- // Check for module name collisions
- RCTModuleData *moduleData = _moduleDataByName[moduleName];
- if (moduleData) {
- if (moduleData.hasInstance || lazilyDiscovered) {
- // Existing module was preregistered, so it takes precedence
- continue;
- } else if ([moduleClass new] == nil) {
- // The new module returned nil from init, so use the old module
- continue;
- } else if ([moduleData.moduleClass new] != nil) {
- // Both modules were non-nil, so it's unclear which should take precedence
- RCTLogWarn(
- @"Attempted to register RCTBridgeModule class %@ for the "
- "name '%@', but name was already registered by class %@",
- moduleClass,
- moduleName,
- moduleData.moduleClass);
- }
- }
- // Instantiate moduleData
- // TODO #13258411: can we defer this until config generation?
- moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];
- _moduleDataByName[moduleName] = moduleData;
- [_moduleClassesByID addObject:moduleClass];
- [moduleDataByID addObject:moduleData];
- }
- [_moduleDataByID addObjectsFromArray:moduleDataByID];
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- return moduleDataByID;
- }
- - (void)registerExtraModules
- {
- RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge initModulesWithDispatchGroup:] extraModules", nil);
- NSArray<id<RCTBridgeModule>> *extraModules = nil;
- if ([self.delegate respondsToSelector:@selector(extraModulesForBridge:)]) {
- extraModules = [self.delegate extraModulesForBridge:_parentBridge];
- } else if (self.moduleProvider) {
- extraModules = self.moduleProvider();
- }
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- RCT_PROFILE_BEGIN_EVENT(
- RCTProfileTagAlways, @"-[RCTCxxBridge initModulesWithDispatchGroup:] preinitialized moduleData", nil);
- // Set up moduleData for pre-initialized module instances
- for (id<RCTBridgeModule> module in extraModules) {
- Class moduleClass = [module class];
- NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
- if (RCT_DEBUG) {
- // Check for name collisions between preregistered modules
- RCTModuleData *moduleData = _moduleDataByName[moduleName];
- if (moduleData) {
- RCTLogError(
- @"Attempted to register RCTBridgeModule class %@ for the "
- "name '%@', but name was already registered by class %@",
- moduleClass,
- moduleName,
- moduleData.moduleClass);
- continue;
- }
- }
- if (RCTTurboModuleEnabled() && [module conformsToProtocol:@protocol(RCTTurboModule)]) {
- #if RCT_DEBUG
- // TODO: don't ask for extra module for when TurboModule is enabled.
- RCTLogError(
- @"NativeModule '%@' was marked as TurboModule, but provided as an extra NativeModule "
- "by the class '%@', ignoring.",
- moduleName,
- moduleClass);
- #endif
- continue;
- }
- // Instantiate moduleData container
- RCTModuleData *moduleData = [[RCTModuleData alloc] initWithModuleInstance:module bridge:self];
- _moduleDataByName[moduleName] = moduleData;
- [_moduleClassesByID addObject:moduleClass];
- [_moduleDataByID addObject:moduleData];
- }
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- }
- - (void)registerExtraLazyModules
- {
- #if RCT_DEBUG
- // This is debug-only and only when Chrome is attached, since it expects all modules to be already
- // available on start up. Otherwise, we can let the lazy module discovery to load them on demand.
- Class executorClass = [_parentBridge executorClass];
- if (executorClass && [NSStringFromClass(executorClass) isEqualToString:@"RCTWebSocketExecutor"]) {
- NSDictionary<NSString *, Class> *moduleClasses = nil;
- if ([self.delegate respondsToSelector:@selector(extraLazyModuleClassesForBridge:)]) {
- moduleClasses = [self.delegate extraLazyModuleClassesForBridge:_parentBridge];
- }
- if (!moduleClasses) {
- return;
- }
- // This logic is mostly copied from `registerModulesForClasses:`, but with one difference:
- // we must use the names provided by the delegate method here.
- for (NSString *moduleName in moduleClasses) {
- Class moduleClass = moduleClasses[moduleName];
- if (RCTTurboModuleEnabled() && [moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
- continue;
- }
- // Check for module name collisions
- RCTModuleData *moduleData = _moduleDataByName[moduleName];
- if (moduleData) {
- if (moduleData.hasInstance) {
- // Existing module was preregistered, so it takes precedence
- continue;
- } else if ([moduleClass new] == nil) {
- // The new module returned nil from init, so use the old module
- continue;
- } else if ([moduleData.moduleClass new] != nil) {
- // Use existing module since it was already loaded but not yet instantiated.
- continue;
- }
- }
- moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];
- _moduleDataByName[moduleName] = moduleData;
- [_moduleClassesByID addObject:moduleClass];
- [_moduleDataByID addObject:moduleData];
- }
- }
- #endif
- }
- - (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules
- withDispatchGroup:(dispatch_group_t)dispatchGroup
- lazilyDiscovered:(BOOL)lazilyDiscovered
- {
- // Set up moduleData for automatically-exported modules
- NSArray<RCTModuleData *> *moduleDataById = [self _registerModulesForClasses:modules
- lazilyDiscovered:lazilyDiscovered];
- if (lazilyDiscovered) {
- #if RCT_DEBUG
- // Lazily discovered modules do not require instantiation here,
- // as they are not allowed to have pre-instantiated instance
- // and must not require the main queue.
- for (RCTModuleData *moduleData in moduleDataById) {
- RCTAssert(
- !(moduleData.requiresMainQueueSetup || moduleData.hasInstance),
- @"Module \'%@\' requires initialization on the Main Queue or has pre-instantiated, which is not supported for the lazily discovered modules.",
- moduleData.name);
- }
- #endif
- } else {
- RCT_PROFILE_BEGIN_EVENT(
- RCTProfileTagAlways, @"-[RCTCxxBridge initModulesWithDispatchGroup:] moduleData.hasInstance", nil);
- // Dispatch module init onto main thread for those modules that require it
- // For non-lazily discovered modules we run through the entire set of modules
- // that we have, otherwise some modules coming from the delegate
- // or module provider block, will not be properly instantiated.
- for (RCTModuleData *moduleData in _moduleDataByID) {
- if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
- // Modules that were pre-initialized should ideally be set up before
- // bridge init has finished, otherwise the caller may try to access the
- // module directly rather than via `[bridge moduleForClass:]`, which won't
- // trigger the lazy initialization process. If the module cannot safely be
- // set up on the current thread, it will instead be async dispatched
- // to the main thread to be set up in _prepareModulesWithDispatchGroup:.
- (void)[moduleData instance];
- }
- }
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- // From this point on, RCTDidInitializeModuleNotification notifications will
- // be sent the first time a module is accessed.
- _moduleSetupComplete = YES;
- [self _prepareModulesWithDispatchGroup:dispatchGroup];
- }
- #if RCT_PROFILE
- if (RCTProfileIsProfiling()) {
- // Depends on moduleDataByID being loaded
- RCTProfileHookModules(self);
- }
- #endif
- return moduleDataById;
- }
- - (void)registerAdditionalModuleClasses:(NSArray<Class> *)modules
- {
- std::lock_guard<std::mutex> guard(_moduleRegistryLock);
- if (_moduleRegistryCreated) {
- NSArray<RCTModuleData *> *newModules = [self _initializeModules:modules
- withDispatchGroup:NULL
- lazilyDiscovered:YES];
- assert(_reactInstance); // at this point you must have reactInstance as you already called
- // reactInstance->initialzeBridge
- _reactInstance->getModuleRegistry().registerModules(createNativeModules(newModules, self, _reactInstance));
- } else {
- [self registerModulesForClasses:modules];
- }
- }
- - (void)_prepareModulesWithDispatchGroup:(dispatch_group_t)dispatchGroup
- {
- RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTCxxBridge _prepareModulesWithDispatchGroup]", nil);
- BOOL initializeImmediately = NO;
- if (dispatchGroup == NULL) {
- // If no dispatchGroup is passed in, we must prepare everything immediately.
- // We better be on the right thread too.
- RCTAssertMainQueue();
- initializeImmediately = YES;
- }
- // Set up modules that require main thread init or constants export
- [_performanceLogger setValue:0 forTag:RCTPLNativeModuleMainThread];
- for (RCTModuleData *moduleData in _moduleDataByID) {
- if (moduleData.requiresMainQueueSetup) {
- // Modules that need to be set up on the main thread cannot be initialized
- // lazily when required without doing a dispatch_sync to the main thread,
- // which can result in deadlock. To avoid this, we initialize all of these
- // modules on the main thread in parallel with loading the JS code, so
- // they will already be available before they are ever required.
- dispatch_block_t block = ^{
- if (self.valid && ![moduleData.moduleClass isSubclassOfClass:[RCTCxxModule class]]) {
- [self->_performanceLogger appendStartForTag:RCTPLNativeModuleMainThread];
- (void)[moduleData instance];
- [moduleData gatherConstants];
- [self->_performanceLogger appendStopForTag:RCTPLNativeModuleMainThread];
- }
- };
- if (initializeImmediately && RCTIsMainQueue()) {
- block();
- } else {
- // We've already checked that dispatchGroup is non-null, but this satisfies the
- // Xcode analyzer
- if (dispatchGroup) {
- dispatch_group_async(dispatchGroup, dispatch_get_main_queue(), block);
- }
- }
- _modulesInitializedOnMainQueue++;
- }
- }
- [_performanceLogger setValue:_modulesInitializedOnMainQueue forTag:RCTPLNativeModuleMainThreadUsesCount];
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- }
- - (void)registerModuleForFrameUpdates:(id<RCTBridgeModule>)module withModuleData:(RCTModuleData *)moduleData
- {
- [_displayLink registerModuleForFrameUpdates:module withModuleData:moduleData];
- }
- - (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
- {
- // This will get called from whatever thread was actually executing JS.
- dispatch_block_t completion = ^{
- // Log start up metrics early before processing any other js calls
- [self logStartupFinish];
- // Flush pending calls immediately so we preserve ordering
- [self _flushPendingCalls];
- // Perform the state update and notification on the main thread, so we can't run into
- // timing issues with RCTRootView
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
- object:self->_parentBridge
- userInfo:@{@"bridge" : self}];
- // Starting the display link is not critical to startup, so do it last
- [self ensureOnJavaScriptThread:^{
- // Register the display link to start sending js calls after everything is setup
- [self->_displayLink addToRunLoop:[NSRunLoop currentRunLoop]];
- }];
- });
- };
- if (sync) {
- [self executeApplicationScriptSync:sourceCode url:self.bundleURL];
- completion();
- } else {
- [self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
- }
- [self.devSettings setupHotModuleReloadClientIfApplicableForURL:self.bundleURL];
- }
- - (void)handleError:(NSError *)error
- {
- // This is generally called when the infrastructure throws an
- // exception while calling JS. Most product exceptions will not go
- // through this method, but through RCTExceptionManager.
- // There are three possible states:
- // 1. initializing == _valid && _loading
- // 2. initializing/loading finished (success or failure) == _valid && !_loading
- // 3. invalidated == !_valid && !_loading
- // !_valid && _loading can't happen.
- // In state 1: on main queue, move to state 2, reset the bridge, and RCTFatal.
- // In state 2: go directly to RCTFatal. Do not enqueue, do not collect $200.
- // In state 3: do nothing.
- if (self->_valid && !self->_loading) {
- if ([error userInfo][RCTJSRawStackTraceKey]) {
- [self.redBox showErrorMessage:[error localizedDescription] withRawStack:[error userInfo][RCTJSRawStackTraceKey]];
- }
- RCTFatal(error);
- // RN will stop, but let the rest of the app keep going.
- return;
- }
- if (!_valid || !_loading) {
- return;
- }
- // Hack: once the bridge is invalidated below, it won't initialize any new native
- // modules. Initialize the redbox module now so we can still report this error.
- RCTRedBox *redBox = [self redBox];
- _loading = NO;
- _valid = NO;
- _moduleRegistryCreated = NO;
- dispatch_async(dispatch_get_main_queue(), ^{
- if (self->_jsMessageThread) {
- // Make sure initializeBridge completed
- self->_jsMessageThread->runOnQueueSync([] {});
- }
- self->_reactInstance.reset();
- self->_jsMessageThread.reset();
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
- object:self->_parentBridge
- userInfo:@{@"bridge" : self, @"error" : error}];
- if ([error userInfo][RCTJSRawStackTraceKey]) {
- [redBox showErrorMessage:[error localizedDescription] withRawStack:[error userInfo][RCTJSRawStackTraceKey]];
- }
- RCTFatal(error);
- });
- }
- RCT_NOT_IMPLEMENTED(-(instancetype)initWithDelegate
- : (__unused id<RCTBridgeDelegate>)delegate bundleURL
- : (__unused NSURL *)bundleURL moduleProvider
- : (__unused RCTBridgeModuleListProvider)block launchOptions
- : (__unused NSDictionary *)launchOptions)
- RCT_NOT_IMPLEMENTED(-(instancetype)initWithBundleURL
- : (__unused NSURL *)bundleURL moduleProvider
- : (__unused RCTBridgeModuleListProvider)block launchOptions
- : (__unused NSDictionary *)launchOptions)
- /**
- * Prevent super from calling setUp (that'd create another batchedBridge)
- */
- - (void)setUp
- {
- }
- - (void)reload
- {
- if (!_valid) {
- RCTLogWarn(
- @"Attempting to reload bridge before it's valid: %@. Try restarting the development server if connected.",
- self);
- }
- RCTTriggerReloadCommandListeners(@"Unknown from cxx bridge");
- }
- - (void)reloadWithReason:(NSString *)reason
- {
- if (!_valid) {
- RCTLogWarn(
- @"Attempting to reload bridge before it's valid: %@. Try restarting the development server if connected.",
- self);
- }
- RCTTriggerReloadCommandListeners(reason);
- }
- - (Class)executorClass
- {
- return _parentBridge.executorClass;
- }
- - (void)setExecutorClass:(Class)executorClass
- {
- RCTAssertMainQueue();
- _parentBridge.executorClass = executorClass;
- }
- - (NSURL *)bundleURL
- {
- return _parentBridge.bundleURL;
- }
- - (void)setBundleURL:(NSURL *)bundleURL
- {
- _parentBridge.bundleURL = bundleURL;
- }
- - (id<RCTBridgeDelegate>)delegate
- {
- return _parentBridge.delegate;
- }
- - (void)dispatchBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue
- {
- if (queue == RCTJSThread) {
- [self ensureOnJavaScriptThread:block];
- } else if (queue) {
- dispatch_async(queue, block);
- }
- }
- #pragma mark - RCTInvalidating
- - (void)invalidate
- {
- if (_didInvalidate) {
- return;
- }
- RCTAssertMainQueue();
- RCTLogInfo(@"Invalidating %@ (parent: %@, executor: %@)", self, _parentBridge, [self executorClass]);
- _loading = NO;
- _valid = NO;
- _didInvalidate = YES;
- _moduleRegistryCreated = NO;
- if ([RCTBridge currentBridge] == self) {
- [RCTBridge setCurrentBridge:nil];
- }
- // Stop JS instance and message thread
- [self ensureOnJavaScriptThread:^{
- [self->_displayLink invalidate];
- self->_displayLink = nil;
- if (RCTProfileIsProfiling()) {
- RCTProfileUnhookModules(self);
- }
- // Invalidate modules
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTBridgeWillInvalidateModulesNotification
- object:self->_parentBridge
- userInfo:@{@"bridge" : self}];
- // We're on the JS thread (which we'll be suspending soon), so no new calls will be made to native modules after
- // this completes. We must ensure all previous calls were dispatched before deallocating the instance (and module
- // wrappers) or we may have invalid pointers still in flight.
- dispatch_group_t moduleInvalidation = dispatch_group_create();
- for (RCTModuleData *moduleData in self->_moduleDataByID) {
- // Be careful when grabbing an instance here, we don't want to instantiate
- // any modules just to invalidate them.
- if (![moduleData hasInstance]) {
- continue;
- }
- if ([moduleData.instance respondsToSelector:@selector(invalidate)]) {
- dispatch_group_enter(moduleInvalidation);
- [self
- dispatchBlock:^{
- [(id<RCTInvalidating>)moduleData.instance invalidate];
- dispatch_group_leave(moduleInvalidation);
- }
- queue:moduleData.methodQueue];
- }
- [moduleData invalidate];
- }
- if (dispatch_group_wait(moduleInvalidation, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC))) {
- RCTLogError(@"Timed out waiting for modules to be invalidated");
- }
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTBridgeDidInvalidateModulesNotification
- object:self->_parentBridge
- userInfo:@{@"bridge" : self}];
- self->_reactInstance.reset();
- self->_jsMessageThread.reset();
- self->_moduleDataByName = nil;
- self->_moduleDataByID = nil;
- self->_moduleClassesByID = nil;
- self->_pendingCalls = nil;
- [self->_jsThread cancel];
- self->_jsThread = nil;
- CFRunLoopStop(CFRunLoopGetCurrent());
- }];
- }
- - (void)logMessage:(NSString *)message level:(NSString *)level
- {
- if (RCT_DEBUG && _valid) {
- [self enqueueJSCall:@"RCTLog" method:@"logIfNoNativeHook" args:@[ level, message ] completion:NULL];
- }
- }
- #pragma mark - RCTBridge methods
- - (void)_runAfterLoad:(RCTPendingCall)block
- {
- // Ordering here is tricky. Ideally, the C++ bridge would provide
- // functionality to defer calls until after the app is loaded. Until that
- // happens, we do this. _pendingCount keeps a count of blocks which have
- // been deferred. It is incremented using an atomic barrier call before each
- // block is added to the js queue, and decremented using an atomic barrier
- // call after the block is executed. If _pendingCount is zero, there is no
- // work either in the js queue, or in _pendingCalls, so it is safe to add new
- // work to the JS queue directly.
- if (self.loading || _pendingCount > 0) {
- // From the callers' perspective:
- // Phase 1: jsQueueBlocks are added to the queue; _pendingCount is
- // incremented for each. If the first block is created after self.loading is
- // true, phase 1 will be nothing.
- _pendingCount++;
- dispatch_block_t jsQueueBlock = ^{
- // From the perspective of the JS queue:
- if (self.loading) {
- // Phase A: jsQueueBlocks are executed. self.loading is true, so they
- // are added to _pendingCalls.
- [self->_pendingCalls addObject:block];
- } else {
- // Phase C: More jsQueueBlocks are executed. self.loading is false, so
- // each block is executed, adding work to the queue, and _pendingCount is
- // decremented.
- block();
- self->_pendingCount--;
- }
- };
- [self ensureOnJavaScriptThread:jsQueueBlock];
- } else {
- // Phase 2/Phase D: blocks are executed directly, adding work to the JS queue.
- block();
- }
- }
- - (void)logStartupFinish
- {
- // Log metrics about native requires during the bridge startup.
- uint64_t nativeRequiresCount = [_performanceLogger valueForTag:RCTPLRAMNativeRequiresCount];
- [_performanceLogger setValue:nativeRequiresCount forTag:RCTPLRAMStartupNativeRequiresCount];
- uint64_t nativeRequires = [_performanceLogger valueForTag:RCTPLRAMNativeRequires];
- [_performanceLogger setValue:nativeRequires forTag:RCTPLRAMStartupNativeRequires];
- [_performanceLogger markStopForTag:RCTPLBridgeStartup];
- }
- - (void)_flushPendingCalls
- {
- RCT_PROFILE_BEGIN_EVENT(0, @"Processing pendingCalls", @{@"count" : [@(_pendingCalls.count) stringValue]});
- // Phase B: _flushPendingCalls happens. Each block in _pendingCalls is
- // executed, adding work to the queue, and _pendingCount is decremented.
- // loading is set to NO.
- NSArray<RCTPendingCall> *pendingCalls = _pendingCalls;
- _pendingCalls = nil;
- for (RCTPendingCall call in pendingCalls) {
- call();
- _pendingCount--;
- }
- _loading = NO;
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- }
- /**
- * Public. Can be invoked from any thread.
- */
- - (void)enqueueJSCall:(NSString *)module
- method:(NSString *)method
- args:(NSArray *)args
- completion:(dispatch_block_t)completion
- {
- if (!self.valid) {
- return;
- }
- /**
- * AnyThread
- */
- RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge enqueueJSCall:]", nil);
- RCTProfileBeginFlowEvent();
- __weak __typeof(self) weakSelf = self;
- [self _runAfterLoad:^() {
- RCTProfileEndFlowEvent();
- __strong __typeof(weakSelf) strongSelf = weakSelf;
- if (!strongSelf) {
- return;
- }
- if (strongSelf->_reactInstance) {
- strongSelf->_reactInstance->callJSFunction(
- [module UTF8String], [method UTF8String], convertIdToFollyDynamic(args ?: @[]));
- // ensureOnJavaScriptThread may execute immediately, so use jsMessageThread, to make sure
- // the block is invoked after callJSFunction
- if (completion) {
- if (strongSelf->_jsMessageThread) {
- strongSelf->_jsMessageThread->runOnQueue(completion);
- } else {
- RCTLogWarn(@"Can't invoke completion without messageThread");
- }
- }
- }
- }];
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- }
- /**
- * Called by RCTModuleMethod from any thread.
- */
- - (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args
- {
- if (!self.valid) {
- return;
- }
- /**
- * AnyThread
- */
- RCTProfileBeginFlowEvent();
- __weak __typeof(self) weakSelf = self;
- [self _runAfterLoad:^() {
- RCTProfileEndFlowEvent();
- __strong __typeof(weakSelf) strongSelf = weakSelf;
- if (!strongSelf) {
- return;
- }
- if (strongSelf->_reactInstance) {
- strongSelf->_reactInstance->callJSCallback([cbID unsignedLongLongValue], convertIdToFollyDynamic(args ?: @[]));
- }
- }];
- }
- /**
- * Private hack to support `setTimeout(fn, 0)`
- */
- - (void)_immediatelyCallTimer:(NSNumber *)timer
- {
- if (_reactInstance) {
- _reactInstance->callJSFunction(
- "JSTimers", "callTimers", folly::dynamic::array(folly::dynamic::array([timer doubleValue])));
- }
- }
- - (void)enqueueApplicationScript:(NSData *)script url:(NSURL *)url onComplete:(dispatch_block_t)onComplete
- {
- RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge enqueueApplicationScript]", nil);
- [self executeApplicationScript:script url:url async:YES];
- RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
- // Assumes that onComplete can be called when the next block on the JS thread is scheduled
- if (onComplete) {
- RCTAssert(_jsMessageThread != nullptr, @"Cannot invoke completion without jsMessageThread");
- _jsMessageThread->runOnQueue(onComplete);
- }
- }
- - (void)executeApplicationScriptSync:(NSData *)script url:(NSURL *)url
- {
- [self executeApplicationScript:script url:url async:NO];
- }
- - (void)executeApplicationScript:(NSData *)script url:(NSURL *)url async:(BOOL)async
- {
- [self _tryAndHandleError:^{
- NSString *sourceUrlStr = deriveSourceURL(url);
- [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartExecutingNotification
- object:self->_parentBridge
- userInfo:@{@"bridge" : self}];
- // hold a local reference to reactInstance in case a parallel thread
- // resets it between null check and usage
- auto reactInstance = self->_reactInstance;
- if (isRAMBundle(script)) {
- [self->_performanceLogger markStartForTag:RCTPLRAMBundleLoad];
- auto ramBundle = std::make_unique<JSIndexedRAMBundle>(sourceUrlStr.UTF8String);
- std::unique_ptr<const JSBigString> scriptStr = ramBundle->getStartupCode();
- [self->_performanceLogger markStopForTag:RCTPLRAMBundleLoad];
- [self->_performanceLogger setValue:scriptStr->size() forTag:RCTPLRAMStartupCodeSize];
- if (reactInstance) {
- auto registry =
- RAMBundleRegistry::multipleBundlesRegistry(std::move(ramBundle), JSIndexedRAMBundle::buildFactory());
- reactInstance->loadRAMBundle(std::move(registry), std::move(scriptStr), sourceUrlStr.UTF8String, !async);
- }
- } else if (reactInstance) {
- reactInstance->loadScriptFromString(std::make_unique<NSDataBigString>(script), sourceUrlStr.UTF8String, !async);
- } else {
- std::string methodName = async ? "loadBundle" : "loadBundleSync";
- throw std::logic_error("Attempt to call " + methodName + ": on uninitialized bridge");
- }
- }];
- }
- - (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path
- {
- if (_reactInstance) {
- _reactInstance->registerBundle(static_cast<uint32_t>(segmentId), path.UTF8String);
- }
- }
- #pragma mark - Payload Processing
- - (void)partialBatchDidFlush
- {
- for (RCTModuleData *moduleData in _moduleDataByID) {
- if (moduleData.implementsPartialBatchDidFlush) {
- [self
- dispatchBlock:^{
- [moduleData.instance partialBatchDidFlush];
- }
- queue:moduleData.methodQueue];
- }
- }
- }
- - (void)batchDidComplete
- {
- // TODO #12592471: batchDidComplete is only used by RCTUIManager,
- // can we eliminate this special case?
- for (RCTModuleData *moduleData in _moduleDataByID) {
- if (moduleData.implementsBatchDidComplete) {
- [self
- dispatchBlock:^{
- [moduleData.instance batchDidComplete];
- }
- queue:moduleData.methodQueue];
- }
- }
- }
- - (void)startProfiling
- {
- RCTAssertMainQueue();
- [self ensureOnJavaScriptThread:^{
- #if WITH_FBSYSTRACE
- [RCTFBSystrace registerCallbacks];
- #endif
- RCTProfileInit(self);
- [self enqueueJSCall:@"Systrace" method:@"setEnabled" args:@[ @YES ] completion:NULL];
- }];
- }
- - (void)stopProfiling:(void (^)(NSData *))callback
- {
- RCTAssertMainQueue();
- [self ensureOnJavaScriptThread:^{
- [self enqueueJSCall:@"Systrace" method:@"setEnabled" args:@[ @NO ] completion:NULL];
- RCTProfileEnd(self, ^(NSString *log) {
- NSData *logData = [log dataUsingEncoding:NSUTF8StringEncoding];
- callback(logData);
- #if WITH_FBSYSTRACE
- if (![RCTFBSystrace verifyTraceSize:logData.length]) {
- RCTLogWarn(
- @"Your FBSystrace trace might be truncated, try to bump up the buffer size"
- " in RCTFBSystrace.m or capture a shorter trace");
- }
- [RCTFBSystrace unregisterCallbacks];
- #endif
- });
- }];
- }
- - (BOOL)isBatchActive
- {
- return _reactInstance ? _reactInstance->isBatchActive() : NO;
- }
- - (void *)runtime
- {
- if (!_reactInstance) {
- return nullptr;
- }
- return _reactInstance->getJavaScriptContext();
- }
- - (void)invokeAsync:(std::function<void()> &&)func
- {
- __block auto retainedFunc = std::move(func);
- __weak __typeof(self) weakSelf = self;
- [self _runAfterLoad:^{
- __strong __typeof(self) strongSelf = weakSelf;
- if (std::shared_ptr<CallInvoker> jsInvoker = strongSelf.jsCallInvoker) {
- jsInvoker->invokeAsync(std::move(retainedFunc));
- }
- }];
- }
- #pragma mark - RCTBridge (RCTTurboModule)
- - (std::shared_ptr<CallInvoker>)jsCallInvoker
- {
- return _reactInstance ? _reactInstance->getJSCallInvoker() : nullptr;
- }
- - (std::shared_ptr<CallInvoker>)decorateNativeCallInvoker:(std::shared_ptr<CallInvoker>)nativeInvoker
- {
- return _reactInstance ? _reactInstance->getDecoratedNativeCallInvoker(nativeInvoker) : nullptr;
- }
- @end
|