From 0b4baf760a617fa2a1815c801a6e45ae764dbdd0 Mon Sep 17 00:00:00 2001 From: Lulu Wu Date: Thu, 27 Apr 2023 02:11:07 -0700 Subject: [PATCH] Move Bridgeless files to OSS folders (#37066) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37066 Move Bridgeless ObjC files from > fbobjc/Apps/Wilde/Internal/venice to > xplat/js/react-native-github/packages/react-native/ReactCommon/react/bridgeless/platform/ios Changelog: [iOS][Changed] - Move Bridgeless files to OSS folders Reviewed By: fkgozali Differential Revision: D45120773 fbshipit-source-id: 2aa03238ae5c33dc8a3bf8004c51e9b6e0ca14b6 --- .../platform/ios/Core/ObjCTimerRegistry.h | 33 ++ .../platform/ios/Core/ObjCTimerRegistry.mm | 73 +++ .../bridgeless/platform/ios/Core/RCTHost.h | 83 ++++ .../bridgeless/platform/ios/Core/RCTHost.mm | 302 +++++++++++++ .../platform/ios/Core/RCTInstance.h | 97 ++++ .../platform/ios/Core/RCTInstance.mm | 425 ++++++++++++++++++ .../platform/ios/Core/RCTJSThreadManager.h | 22 + .../platform/ios/Core/RCTJSThreadManager.mm | 125 ++++++ .../ios/Core/RCTPerformanceLoggerUtils.h | 16 + .../ios/Core/RCTPerformanceLoggerUtils.mm | 62 +++ .../platform/ios/Hermes/RCTHermesInstance.h | 38 ++ .../platform/ios/Hermes/RCTHermesInstance.mm | 29 ++ 12 files changed, 1305 insertions(+) create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/ObjCTimerRegistry.h create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/ObjCTimerRegistry.mm create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTHost.h create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTHost.mm create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.h create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTJSThreadManager.h create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTJSThreadManager.mm create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTPerformanceLoggerUtils.h create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTPerformanceLoggerUtils.mm create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Hermes/RCTHermesInstance.h create mode 100644 packages/react-native/ReactCommon/react/bridgeless/platform/ios/Hermes/RCTHermesInstance.mm diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/ObjCTimerRegistry.h b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/ObjCTimerRegistry.h new file mode 100644 index 00000000000000..60219eb7211c98 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/ObjCTimerRegistry.h @@ -0,0 +1,33 @@ +/* + * 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 + +#import +#import +#import +#import + +@interface RCTJSTimerExecutor : NSObject + +- (void)setTimerManager:(std::weak_ptr)timerManager FB_OBJC_DIRECT; + +@end + +class ObjCTimerRegistry : public facebook::react::PlatformTimerRegistry { + public: + ObjCTimerRegistry(); + void createTimer(uint32_t timerID, double delayMS) override; + void deleteTimer(uint32_t timerID) override; + void createRecurringTimer(uint32_t timerID, double delayMS) override; + void setTimerManager(std::weak_ptr timerManager); + RCTTiming *_Null_unspecified timing; + + private: + RCTJSTimerExecutor *_Null_unspecified jsTimerExecutor_; + double toSeconds(double ms); +}; diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/ObjCTimerRegistry.mm b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/ObjCTimerRegistry.mm new file mode 100644 index 00000000000000..194e9b22f14c89 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/ObjCTimerRegistry.mm @@ -0,0 +1,73 @@ +/* + * 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 "ObjCTimerRegistry.h" + +@implementation RCTJSTimerExecutor { + std::weak_ptr _timerManager; +} + +- (void)setTimerManager:(std::weak_ptr)timerManager +{ + _timerManager = timerManager; +} + +- (void)callTimers:(NSArray *)timers +{ + if (auto timerManager = _timerManager.lock()) { + for (NSNumber *timer in timers) { + timerManager->callTimer([timer unsignedIntValue]); + } + } +} + +- (void)immediatelyCallTimer:(nonnull NSNumber *)callbackID +{ + if (auto timerManager = _timerManager.lock()) { + timerManager->callTimer([callbackID unsignedIntValue]); + } +} + +- (void)callIdleCallbacks:(nonnull NSNumber *)absoluteFrameStartMS +{ + // TODO(T53992765)(petetheheat) - Implement this +} + +@end + +ObjCTimerRegistry::ObjCTimerRegistry() +{ + jsTimerExecutor_ = [RCTJSTimerExecutor new]; + timing = [[RCTTiming alloc] initWithDelegate:jsTimerExecutor_]; +} + +void ObjCTimerRegistry::createTimer(uint32_t timerID, double delayMS) +{ + [timing createTimerForNextFrame:@(timerID) duration:toSeconds(delayMS) jsSchedulingTime:nil repeats:NO]; +} + +void ObjCTimerRegistry::deleteTimer(uint32_t timerID) +{ + [timing deleteTimer:(double)timerID]; +} + +void ObjCTimerRegistry::createRecurringTimer(uint32_t timerID, double delayMS) +{ + [timing createTimerForNextFrame:@(timerID) duration:toSeconds(delayMS) jsSchedulingTime:nil repeats:YES]; +} + +void ObjCTimerRegistry::setTimerManager(std::weak_ptr timerManager) +{ + [jsTimerExecutor_ setTimerManager:timerManager]; +} + +// ObjC timing native module expects a NSTimeInterval which is always specified in seconds. JS expresses timer delay +// in ms. Perform a simple conversion here. +double ObjCTimerRegistry::toSeconds(double ms) +{ + return ms / 1000.0; +} diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTHost.h b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTHost.h new file mode 100644 index 00000000000000..338acf48017d07 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTHost.h @@ -0,0 +1,83 @@ +/* + * 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 + +#import +#import +#import +#import +#import + +#import "RCTInstance.h" + +NS_ASSUME_NONNULL_BEGIN + +@class RCTFabricSurface; +@class RCTJSThreadManager; +@class RCTModuleRegistry; +@class RCTPerformanceLogger; +@protocol RCTInstanceDelegate; +FB_RUNTIME_PROTOCOL +@protocol RCTTurboModuleManagerDelegate; + +/** + * This notification fires just before RCTHost releases it's RCTInstance reference. + */ +RCT_EXTERN NSString *const RCTHostWillReloadNotification; + +/** + * This notification fires just after RCTHost releases it's RCTInstance reference. + */ +RCT_EXTERN NSString *const RCTHostDidReloadNotification; + +@protocol RCTHostDelegate + +- (std::shared_ptr)getJSEngine; +- (NSURL *)getBundleURL; + +@end + +/** + * RCTHost is an object which is responsible for managing the lifecycle of a single RCTInstance. + * RCTHost is long lived, while an instance may be deallocated and re-initialized. Some examples of when this happens: + * CMD+R reload in DEV or a JS crash. The host should be the single owner of an RCTInstance. + */ +@interface RCTHost : NSObject + +- (instancetype)initWithHostDelegate:(id)hostDelegate + instanceDelegate:(id)instanceDelegate + turboModuleManagerDelegate:(id)turboModuleManagerDelegate + bindingsInstallFunc:(facebook::react::ReactInstance::BindingsInstallFunc)bindingsInstallFunc + jsErrorHandlingFunc:(facebook::react::JsErrorHandler::JsErrorHandlingFunc)jsErrorHandlingFunc + NS_DESIGNATED_INITIALIZER FB_OBJC_DIRECT; + +/** + * This function initializes an RCTInstance if one does not yet exist. This function is currently only called on the + * main thread, but it should be threadsafe. + * TODO T74233481 - Verify if this function is threadsafe. + */ +- (void)preload; + +- (RCTFabricSurface *)createSurfaceWithModuleName:(NSString *)moduleName + mode:(facebook::react::DisplayMode)displayMode + initialProperties:(NSDictionary *)properties FB_OBJC_DIRECT; + +- (RCTFabricSurface *)createSurfaceWithModuleName:(NSString *)moduleName + initialProperties:(NSDictionary *)properties FB_OBJC_DIRECT; + +- (RCTJSThreadManager *)getJSThreadManager FB_OBJC_DIRECT; + +- (RCTModuleRegistry *)getModuleRegistry FB_OBJC_DIRECT; + +- (RCTPerformanceLogger *)getPerformanceLogger FB_OBJC_DIRECT; + +- (RCTSurfacePresenter *)getSurfacePresenter FB_OBJC_DIRECT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTHost.mm b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTHost.mm new file mode 100644 index 00000000000000..7a251ccc9e9c11 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTHost.mm @@ -0,0 +1,302 @@ +/* + * 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 "RCTHost.h" + +#import +#import +#import +#import +#import +#import +#import +#import + +using namespace facebook::react; + +NSString *const RCTHostWillReloadNotification = @"RCTHostWillReloadNotification"; +NSString *const RCTHostDidReloadNotification = @"RCTHostDidReloadNotification"; + +@interface RCTHost () +@end + +@implementation RCTHost { + RCTInstance *_instance; + __weak id _instanceDelegate; + __weak id _hostDelegate; + __weak id _turboModuleManagerDelegate; + NSURL *_oldDelegateBundleURL; + NSURL *_bundleURL; + RCTBundleManager *_bundleManager; + facebook::react::ReactInstance::BindingsInstallFunc _bindingsInstallFunc; + JsErrorHandler::JsErrorHandlingFunc _jsErrorHandlingFunc; + + // All the surfaces that need to be started after main bundle execution + NSMutableArray *_surfaceStartBuffer; + std::mutex _surfaceStartBufferMutex; + + RCTInstanceInitialBundleLoadCompletionBlock _onInitialBundleLoad; + std::vector<__weak RCTFabricSurface *> _attachedSurfaces; + + RCTModuleRegistry *_moduleRegistry; +} + ++ (void)initialize +{ + _RCTInitializeJSThreadConstantInternal(); +} + +/** + Host initialization should not be resource intensive. A host may be created before any intention of using React Native + has been expressed. + */ +- (instancetype)initWithHostDelegate:(id)hostDelegate + instanceDelegate:(id)instanceDelegate + turboModuleManagerDelegate:(id)turboModuleManagerDelegate + bindingsInstallFunc:(facebook::react::ReactInstance::BindingsInstallFunc)bindingsInstallFunc + jsErrorHandlingFunc:(JsErrorHandler::JsErrorHandlingFunc)jsErrorHandlingFunc; +{ + RCTAssert( + hostDelegate && instanceDelegate && turboModuleManagerDelegate, + @"RCTHost cannot be instantiated with any nil init params."); + + if (self = [super init]) { + _hostDelegate = hostDelegate; + _instanceDelegate = instanceDelegate; + _turboModuleManagerDelegate = turboModuleManagerDelegate; + _surfaceStartBuffer = [NSMutableArray new]; + _bundleManager = [RCTBundleManager new]; + _bindingsInstallFunc = bindingsInstallFunc; + _moduleRegistry = [RCTModuleRegistry new]; + _jsErrorHandlingFunc = jsErrorHandlingFunc; + + __weak RCTHost *weakHost = self; + + auto bundleURLGetter = ^NSURL *() + { + RCTHost *strongHost = weakHost; + if (!strongHost) { + return nil; + } + + return strongHost->_bundleURL; + }; + + auto bundleURLSetter = ^(NSURL *bundleURL) { + RCTHost *strongHost = weakHost; + if (!strongHost) { + return; + } + strongHost->_bundleURL = bundleURL; + }; + + auto defaultBundleURLGetter = ^NSURL *() + { + RCTHost *strongHost = weakHost; + if (!strongHost) { + return nil; + } + + return [strongHost->_hostDelegate getBundleURL]; + }; + + [_bundleManager setBridgelessBundleURLGetter:bundleURLGetter + andSetter:bundleURLSetter + andDefaultGetter:defaultBundleURLGetter]; + + /** + * TODO (T108401473) Remove _onInitialBundleLoad, because it was initially + * introduced to start surfaces after the main JSBundle was fully executed. + * It is no longer needed because Fabric now dispatches all native-to-JS calls + * onto the JS thread, including JS calls to start Fabric surfaces. + * JS calls should be dispatched using the BufferedRuntimeExecutor, which can buffer + * JS calls before the main JSBundle finishes execution, and execute them after. + */ + _onInitialBundleLoad = ^{ + RCTHost *strongHost = weakHost; + if (!strongHost) { + return; + } + + NSArray *unstartedSurfaces = @[]; + + { + std::lock_guard guard{strongHost->_surfaceStartBufferMutex}; + unstartedSurfaces = [NSArray arrayWithArray:strongHost->_surfaceStartBuffer]; + strongHost->_surfaceStartBuffer = nil; + } + + for (RCTFabricSurface *surface in unstartedSurfaces) { + [surface start]; + } + }; + + // Listen to reload commands + dispatch_async(dispatch_get_main_queue(), ^{ + RCTRegisterReloadCommandListener(self); + }); + } + return self; +} + +#pragma mark - Public API + +- (void)preload +{ + if (_instance) { + RCTLogWarn( + @"RCTHost should not be creating a new instance if one already exists. This implies there is a bug with how/when this method is being called."); + [_instance invalidate]; + } + [self _refreshBundleURL]; + RCTReloadCommandSetBundleURL(_bundleURL); + _instance = [[RCTInstance alloc] initWithDelegate:_instanceDelegate + jsEngineInstance:[_hostDelegate getJSEngine] + bundleManager:_bundleManager + turboModuleManagerDelegate:_turboModuleManagerDelegate + onInitialBundleLoad:_onInitialBundleLoad + bindingsInstallFunc:_bindingsInstallFunc + moduleRegistry:_moduleRegistry + jsErrorHandlingFunc:_jsErrorHandlingFunc]; +} + +- (RCTFabricSurface *)createSurfaceWithModuleName:(NSString *)moduleName + mode:(DisplayMode)displayMode + initialProperties:(NSDictionary *)properties +{ + RCTFabricSurface *surface = [[RCTFabricSurface alloc] initWithSurfacePresenter:[self getSurfacePresenter] + moduleName:moduleName + initialProperties:properties]; + surface.surfaceHandler.setDisplayMode(displayMode); + [self _attachSurface:surface]; + + { + std::lock_guard guard{_surfaceStartBufferMutex}; + if (_surfaceStartBuffer) { + [_surfaceStartBuffer addObject:surface]; + return surface; + } + } + + [surface start]; + return surface; +} + +- (RCTFabricSurface *)createSurfaceWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary *)properties +{ + return [self createSurfaceWithModuleName:moduleName mode:DisplayMode::Visible initialProperties:properties]; +} + +- (RCTJSThreadManager *)getJSThreadManager +{ + return [_instance jsThreadManager]; +} + +- (RCTModuleRegistry *)getModuleRegistry +{ + return _moduleRegistry; +} + +- (RCTPerformanceLogger *)getPerformanceLogger +{ + return [_instance performanceLogger]; +} + +- (RCTSurfacePresenter *)getSurfacePresenter +{ + return [_instance surfacePresenter]; +} + +#pragma mark - RCTReloadListener + +- (void)didReceiveReloadCommand +{ + [[NSNotificationCenter defaultCenter] + postNotification:[NSNotification notificationWithName:RCTHostWillReloadNotification object:nil]]; + [_instance invalidate]; + _instance = nil; + [self _refreshBundleURL]; + RCTReloadCommandSetBundleURL(_bundleURL); + + // Ensure all attached surfaces are restarted after reload + { + std::lock_guard guard{_surfaceStartBufferMutex}; + _surfaceStartBuffer = [NSMutableArray arrayWithArray:[self _getAttachedSurfaces]]; + } + + _instance = [[RCTInstance alloc] initWithDelegate:_instanceDelegate + jsEngineInstance:[_hostDelegate getJSEngine] + bundleManager:_bundleManager + turboModuleManagerDelegate:_turboModuleManagerDelegate + onInitialBundleLoad:_onInitialBundleLoad + bindingsInstallFunc:_bindingsInstallFunc + moduleRegistry:_moduleRegistry + jsErrorHandlingFunc:_jsErrorHandlingFunc]; + [[NSNotificationCenter defaultCenter] + postNotification:[NSNotification notificationWithName:RCTHostDidReloadNotification object:nil]]; + + for (RCTFabricSurface *surface in [self _getAttachedSurfaces]) { + [surface resetWithSurfacePresenter:[self getSurfacePresenter]]; + } +} + +// TODO (T74233481) - Should raw instance be accessed in this class like this? These functions shouldn't be called very +// early in startup, but could add some intelligent guards here. +#pragma mark - ReactInstanceForwarding + +- (void)callFunctionOnModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args +{ + [_instance callFunctionOnModule:moduleName method:method args:args]; +} + +- (void)loadScript:(RCTSource *)source +{ + [_instance loadScript:source]; +} + +- (void)registerSegmentWithId:(NSNumber *)segmentId path:(NSString *)path +{ + [_instance registerSegmentWithId:segmentId path:path]; +} + +- (void)dealloc +{ + [_instance invalidate]; +} + +#pragma mark - Private +- (void)_refreshBundleURL FB_OBJC_DIRECT +{ + // Reset the _bundleURL ivar if the RCTHost delegate presents a new bundleURL + NSURL *newDelegateBundleURL = [_hostDelegate getBundleURL]; + if (newDelegateBundleURL && ![newDelegateBundleURL isEqual:_oldDelegateBundleURL]) { + _oldDelegateBundleURL = newDelegateBundleURL; + _bundleURL = newDelegateBundleURL; + } + + // Sanitize the bundle URL + _bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString]; +} + +- (void)_attachSurface:(RCTFabricSurface *)surface FB_OBJC_DIRECT +{ + _attachedSurfaces.push_back(surface); +} + +- (NSArray *)_getAttachedSurfaces FB_OBJC_DIRECT +{ + NSMutableArray *surfaces = [NSMutableArray new]; + for (RCTFabricSurface *surface : _attachedSurfaces) { + if (surface) { + [surfaces addObject:surface]; + } + } + + return surfaces; +} + +@end diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.h b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.h new file mode 100644 index 00000000000000..69f6484aa94a13 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.h @@ -0,0 +1,97 @@ +/* + * 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 + +#import +#import +#import +#import +#import + +/** + * A utility to enable diagnostics mode at runtime. Useful for test runs. + * The flags are comma-separated string tokens, or an empty string when + * nothing is enabled. + */ +RCT_EXTERN NSString *RCTInstanceRuntimeDiagnosticFlags(void); +RCT_EXTERN void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags); + +NS_ASSUME_NONNULL_BEGIN + +@class RCTBundleManager; +@class RCTJSThreadManager; +@class RCTModuleRegistry; +@class RCTPerformanceLogger; +@class RCTSource; +@class RCTSurfacePresenter; + +FB_RUNTIME_PROTOCOL +@protocol RCTTurboModuleManagerDelegate; + +// TODO (T74233481) - Delete this. Communication between Product Code <> RCTInstance should go through RCTHost. +@protocol RCTInstanceDelegate + +@required + +- (std::shared_ptr)createContextContainer; + +@end + +/** + * A set of functions which are forwarded through RCTHost, RCTInstance to ReactInstance. + */ +@protocol ReactInstanceForwarding + +/** + * Calls a method on a JS module that has been registered with `registerCallableModule`. Used to invoke a JS function + * from platform code. + */ +- (void)callFunctionOnModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args; + +/** + * Loads the JS bundle asynchronously. + */ +- (void)loadScript:(RCTSource *)source; + +/** + * Registers a new JS segment. + */ +- (void)registerSegmentWithId:(NSNumber *)segmentId path:(NSString *)path; + +@end + +typedef void (^_Null_unspecified RCTInstanceInitialBundleLoadCompletionBlock)(); + +/** + * RCTInstance owns and manages most of the pieces of infrastructure required to display a screen powered by React + * Native. RCTInstance should never be instantiated in product code, but rather accessed through RCTHost. The host + * ensures that any access to the instance is safe, and manages instance lifecycle. + */ +@interface RCTInstance : NSObject + +- (instancetype)initWithDelegate:(id)delegate + jsEngineInstance:(std::shared_ptr)jsEngineInstance + bundleManager:(RCTBundleManager *)bundleManager + turboModuleManagerDelegate:(id)turboModuleManagerDelegate + onInitialBundleLoad:(RCTInstanceInitialBundleLoadCompletionBlock)onInitialBundleLoad + bindingsInstallFunc:(facebook::react::ReactInstance::BindingsInstallFunc)bindingsInstallFunc + moduleRegistry:(RCTModuleRegistry *)moduleRegistry + jsErrorHandlingFunc:(facebook::react::JsErrorHandler::JsErrorHandlingFunc)jsErrorHandlingFunc + FB_OBJC_DIRECT; + +- (void)invalidate; + +@property (nonatomic, readonly, strong, FB_DIRECT) RCTJSThreadManager *jsThreadManager; + +@property (nonatomic, readonly, strong) RCTPerformanceLogger *performanceLogger; + +@property (nonatomic, readonly, strong) RCTSurfacePresenter *surfacePresenter; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm new file mode 100644 index 00000000000000..b0db0c4094646b --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTInstance.mm @@ -0,0 +1,425 @@ +/* + * 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 "RCTInstance.h" + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "ObjCTimerRegistry.h" +#import "RCTJSThreadManager.h" +#import "RCTPerformanceLoggerUtils.h" + +#if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include() +#import +#import +#endif + +using namespace facebook; +using namespace facebook::react; + +static NSString *sRuntimeDiagnosticFlags = nil; +NSString *RCTInstanceRuntimeDiagnosticFlags(void) +{ + return sRuntimeDiagnosticFlags ? [sRuntimeDiagnosticFlags copy] : [NSString new]; +} + +void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags) +{ + if (!flags) { + return; + } + sRuntimeDiagnosticFlags = [flags copy]; +} + +@interface RCTInstance () +@end + +@implementation RCTInstance { + std::unique_ptr _reactInstance; + std::shared_ptr _jsEngineInstance; + __weak id _appTMMDelegate; + __weak id _delegate; + RCTSurfacePresenter *_surfacePresenter; + RCTDisplayLink *_displayLink; + RCTInstanceInitialBundleLoadCompletionBlock _onInitialBundleLoad; + ReactInstance::BindingsInstallFunc _bindingsInstallFunc; + RCTTurboModuleManager *_turboModuleManager; + JsErrorHandler::JsErrorHandlingFunc _jsErrorHandlingFunc; + std::mutex _invalidationMutex; + std::atomic _valid; + + // APIs supporting interop with native modules and view managers + RCTBridgeModuleDecorator *_bridgeModuleDecorator; +} + +- (instancetype)initWithDelegate:(id)delegate + jsEngineInstance:(std::shared_ptr)jsEngineInstance + bundleManager:(RCTBundleManager *)bundleManager + turboModuleManagerDelegate:(id)tmmDelegate + onInitialBundleLoad:(RCTInstanceInitialBundleLoadCompletionBlock)onInitialBundleLoad + bindingsInstallFunc:(ReactInstance::BindingsInstallFunc)bindingsInstallFunc + moduleRegistry:(RCTModuleRegistry *)moduleRegistry + jsErrorHandlingFunc:(JsErrorHandler::JsErrorHandlingFunc)jsErrorHandlingFunc; +{ + if (self = [super init]) { + _performanceLogger = [RCTPerformanceLogger new]; + registerPerformanceLoggerHooks(_performanceLogger); + [_performanceLogger markStartForTag:RCTPLReactInstanceInit]; + + _delegate = delegate; + _jsEngineInstance = jsEngineInstance; + _appTMMDelegate = tmmDelegate; + _jsThreadManager = [RCTJSThreadManager new]; + _onInitialBundleLoad = onInitialBundleLoad; + _bindingsInstallFunc = bindingsInstallFunc; + _jsErrorHandlingFunc = jsErrorHandlingFunc; + _bridgeModuleDecorator = [[RCTBridgeModuleDecorator alloc] initWithViewRegistry:[RCTViewRegistry new] + moduleRegistry:moduleRegistry + bundleManager:bundleManager + callableJSModules:[RCTCallableJSModules new]]; + { + __weak __typeof(self) weakInstance = self; + [_bridgeModuleDecorator.callableJSModules + setBridgelessJSModuleMethodInvoker:^( + NSString *moduleName, NSString *methodName, NSArray *args, dispatch_block_t onComplete) { + // TODO: Make RCTInstance call onComplete + [weakInstance callFunctionOnModule:moduleName method:methodName args:args]; + }]; + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(notifyEventDispatcherObserversOfEvent_DEPRECATED:) + name:@"RCTNotifyEventDispatcherObserversOfEvent_DEPRECATED" + object:nil]; + + [self start]; + } + return self; +} + +- (void)start +{ + // Set up timers + auto objCTimerRegistry = std::make_unique(); + auto timing = objCTimerRegistry->timing; + auto *objCTimerRegistryRawPtr = objCTimerRegistry.get(); + + auto timerManager = std::make_shared(std::move(objCTimerRegistry)); + objCTimerRegistryRawPtr->setTimerManager(timerManager); + + // Create the React Instance + _reactInstance = std::make_unique( + _jsEngineInstance->createJSRuntime(), _jsThreadManager.jsMessageThread, timerManager, _jsErrorHandlingFunc); + _valid = true; + + RuntimeExecutor bufferedRuntimeExecutor = _reactInstance->getBufferedRuntimeExecutor(); + timerManager->setRuntimeExecutor(bufferedRuntimeExecutor); + + // Set up TurboModules + _turboModuleManager = + [[RCTTurboModuleManager alloc] initWithBridge:nil + delegate:self + jsInvoker:std::make_shared(bufferedRuntimeExecutor)]; + + // Initialize RCTModuleRegistry so that TurboModules can require other TurboModules. + [_bridgeModuleDecorator.moduleRegistry setTurboModuleRegistry:_turboModuleManager]; + + RCTLogSetBridgelessModuleRegistry(_bridgeModuleDecorator.moduleRegistry); + RCTLogSetBridgelessCallableJSModules(_bridgeModuleDecorator.callableJSModules); + + auto contextContainer = [_delegate createContextContainer]; + contextContainer->insert( + "RCTImageLoader", facebook::react::wrapManagedObject([_turboModuleManager moduleForName:"RCTImageLoader"])); + contextContainer->insert( + "RCTEventDispatcher", + facebook::react::wrapManagedObject([_turboModuleManager moduleForName:"RCTEventDispatcher"])); + contextContainer->insert("RCTBridgeModuleDecorator", facebook::react::wrapManagedObject(_bridgeModuleDecorator)); + contextContainer->insert("RuntimeScheduler", std::weak_ptr(_reactInstance->getRuntimeScheduler())); + + _surfacePresenter = [[RCTSurfacePresenter alloc] + initWithContextContainer:contextContainer + runtimeExecutor:bufferedRuntimeExecutor + bridgelessBindingsExecutor:std::optional(_reactInstance->getUnbufferedRuntimeExecutor())]; + + // This enables RCTViewRegistry in modules to return UIViews from its viewForReactTag method + __weak RCTSurfacePresenter *weakSurfacePresenter = _surfacePresenter; + [_bridgeModuleDecorator.viewRegistry_DEPRECATED setBridgelessComponentViewProvider:^UIView *(NSNumber *reactTag) { + RCTSurfacePresenter *strongSurfacePresenter = weakSurfacePresenter; + if (strongSurfacePresenter == nil) { + return nil; + } + + return [strongSurfacePresenter findComponentViewWithTag_DO_NOT_USE_DEPRECATED:reactTag.integerValue]; + }]; + + // DisplayLink is used to call timer callbacks. + _displayLink = [RCTDisplayLink new]; + + __weak __typeof(self) weakSelf = self; + + ReactInstance::JSRuntimeFlags options = { + .isProfiling = false, .runtimeDiagnosticFlags = [RCTInstanceRuntimeDiagnosticFlags() UTF8String]}; + _reactInstance->initializeRuntime(options, [=](jsi::Runtime &runtime) { + __strong __typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + facebook::react::RuntimeExecutor syncRuntimeExecutor = + [&](std::function &&callback) { callback(runtime); }; + [strongSelf->_turboModuleManager installJSBindingWithRuntimeExecutor:syncRuntimeExecutor]; + facebook::react::bindNativeLogger(runtime, [](const std::string &message, unsigned int logLevel) { + _RCTLogJavaScriptInternal(static_cast(logLevel), [NSString stringWithUTF8String:message.c_str()]); + }); + RCTInstallNativeComponentRegistryBinding(runtime); + + if (strongSelf->_bindingsInstallFunc) { + strongSelf->_bindingsInstallFunc(runtime); + } + + // Set up Display Link + RCTModuleData *timingModuleData = [[RCTModuleData alloc] initWithModuleInstance:timing + bridge:nil + moduleRegistry:nil + viewRegistry_DEPRECATED:nil + bundleManager:nil + callableJSModules:nil]; + [strongSelf->_displayLink registerModuleForFrameUpdates:timing withModuleData:timingModuleData]; + [strongSelf->_displayLink addToRunLoop:[NSRunLoop currentRunLoop]]; + + // Attempt to load bundle synchronously, fallback to asynchronously. + [strongSelf->_performanceLogger markStartForTag:RCTPLScriptDownload]; + [strongSelf loadJSBundle:[strongSelf->_bridgeModuleDecorator.bundleManager bundleURL]]; + }); + + [_performanceLogger markStopForTag:RCTPLReactInstanceInit]; +} + +#pragma mark - RCTTurboModuleManagerDelegate +- (Class)getModuleClassFromName:(const char *)name +{ + if ([_appTMMDelegate respondsToSelector:@selector(getModuleClassFromName:)]) { + return [_appTMMDelegate getModuleClassFromName:name]; + } + + return nil; +} + +- (id)getModuleInstanceFromClass:(Class)moduleClass +{ + if ([_appTMMDelegate respondsToSelector:@selector(getModuleInstanceFromClass:)]) { + id module = [_appTMMDelegate getModuleInstanceFromClass:moduleClass]; + [self _attachBridgelessAPIsToModule:module]; + return module; + } + + return nil; +} + +- (std::shared_ptr)getTurboModule:(const std::string &)name + jsInvoker:(std::shared_ptr)jsInvoker +{ + if ([_appTMMDelegate respondsToSelector:@selector(getTurboModule:jsInvoker:)]) { + return [_appTMMDelegate getTurboModule:name jsInvoker:jsInvoker]; + } + + return nullptr; +} + +- (void)_attachBridgelessAPIsToModule:(id)module FB_OBJC_DIRECT +{ + __weak RCTInstance *weakInstance = self; + if ([module respondsToSelector:@selector(setLoadScript:)]) { + ((id)module).loadScript = ^(RCTSource *source) { + [weakInstance loadScript:(source)]; + }; + } + + if ([module respondsToSelector:@selector(setDispatchToJSThread:)]) { + ((id)module).dispatchToJSThread = ^(dispatch_block_t block) { + __strong __typeof(self) strongSelf = weakInstance; + + if (strongSelf && strongSelf->_valid) { + strongSelf->_reactInstance->getBufferedRuntimeExecutor()([=](jsi::Runtime &runtime) { block(); }); + } + }; + } + + if ([module respondsToSelector:@selector(setSurfacePresenter:)]) { + [module performSelector:@selector(setSurfacePresenter:) withObject:self->_surfacePresenter]; + } + + // Replaces bridge.isInspectable + if ([module respondsToSelector:@selector(setIsInspectable:)]) { +#if RCT_DEV_MENU + if (_valid) { + _reactInstance->getBufferedRuntimeExecutor()([module](jsi::Runtime &runtime) { + ((id)module).isInspectable = runtime.isInspectable(); + }); + } +#endif + } + + [_bridgeModuleDecorator attachInteropAPIsToModule:(id)module]; +} + +#pragma mark - ReactInstanceForwarding + +- (void)callFunctionOnModule:(NSString *)moduleName method:(NSString *)method args:(NSArray *)args +{ + if (_valid) { + _reactInstance->callFunctionOnModule( + [moduleName UTF8String], [method UTF8String], convertIdToFollyDynamic(args ?: @[])); + } +} + +- (void)loadScript:(RCTSource *)source +{ + [self loadScriptFromSource:source]; +} + +- (void)registerSegmentWithId:(NSNumber *)segmentId path:(NSString *)path +{ + if (_valid) { + self->_reactInstance->registerSegment(static_cast([segmentId unsignedIntValue]), path.UTF8String); + } +} + +#pragma mark - Private + +- (void)loadJSBundle:(NSURL *)sourceURL FB_OBJC_DIRECT +{ +#if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include() + { + id loadingView = + (id)[_turboModuleManager moduleForName:"DevLoadingView"]; + [loadingView showWithURL:sourceURL]; + } +#endif + + __weak RCTPerformanceLogger *weakPerformanceLogger = _performanceLogger; + __weak __typeof(self) weakSelf = self; + [RCTJavaScriptLoader loadBundleAtURL:sourceURL + onProgress:^(RCTLoadingProgress *progressData) { + __typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + +#if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include() + id loadingView = + (id)[strongSelf->_turboModuleManager moduleForName:"DevLoadingView"]; + [loadingView updateProgress:progressData]; +#endif + } + onComplete:^(NSError *error, RCTSource *source) { + __typeof(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + if (error) { + // TODO(T91461138): Properly address bundle loading errors. + RCTLogError(@"RCTInstance: Error while loading bundle: %@", error); + [strongSelf invalidate]; + return; + } + // DevSettings module is needed by loadScriptFromSource's callback so prior initialization is required + RCTDevSettings *const devSettings = + (RCTDevSettings *)[strongSelf->_turboModuleManager moduleForName:"DevSettings"]; + [strongSelf loadScriptFromSource:source]; + // Set up hot module reloading in Dev only. + [weakPerformanceLogger markStopForTag:RCTPLScriptDownload]; + [devSettings setupHMRClientWithBundleURL:sourceURL]; + }]; +} + +- (void)loadScriptFromSource:(RCTSource *)source FB_OBJC_DIRECT +{ + std::lock_guard lock(self->_invalidationMutex); + if (!_valid) { + return; + } + + auto script = std::make_unique(source.data); + const auto *url = deriveSourceURL(source.url).UTF8String; + self->_reactInstance->loadScript(std::move(script), url); + [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTInstanceDidLoadBundle" object:nil]; + + if (_onInitialBundleLoad) { + _onInitialBundleLoad(); + _onInitialBundleLoad = nil; + } +} + +- (void)notifyEventDispatcherObserversOfEvent_DEPRECATED:(NSNotification *)notification +{ + NSDictionary *userInfo = notification.userInfo; + id event = [userInfo objectForKey:@"event"]; + + RCTModuleRegistry *moduleRegistry = _bridgeModuleDecorator.moduleRegistry; + if (event && moduleRegistry) { + id legacyEventDispatcher = [moduleRegistry moduleForName:"EventDispatcher" + lazilyLoadIfNecessary:YES]; + [legacyEventDispatcher notifyObserversOfEvent:event]; + } +} + +- (void)invalidate +{ + std::lock_guard lock(self->_invalidationMutex); + self->_valid = false; + + [_surfacePresenter suspend]; + [_jsThreadManager dispatchToJSThread:^{ + /** + * Every TurboModule is invalidated on its own method queue. + * TurboModuleManager invalidate blocks the calling thread until all TurboModules are invalidated. + */ + [self->_turboModuleManager invalidate]; + + // Clean up all the Resources + self->_reactInstance = nullptr; + self->_jsEngineInstance = nullptr; + self->_appTMMDelegate = nil; + self->_delegate = nil; + self->_displayLink = nil; + + self->_turboModuleManager = nil; + self->_performanceLogger = nil; + + // Terminate the JavaScript thread, so that no other work executes after this block. + self->_jsThreadManager = nil; + }]; +} + +@end diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTJSThreadManager.h b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTJSThreadManager.h new file mode 100644 index 00000000000000..3fd4edda7bc9f7 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTJSThreadManager.h @@ -0,0 +1,22 @@ +/* + * 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 + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RCTJSThreadManager : NSObject + +- (void)dispatchToJSThread:(dispatch_block_t)block FB_OBJC_DIRECT; +- (std::shared_ptr)jsMessageThread FB_OBJC_DIRECT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTJSThreadManager.mm b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTJSThreadManager.mm new file mode 100644 index 00000000000000..20fcfce9414381 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTJSThreadManager.mm @@ -0,0 +1,125 @@ +/* + * 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 "RCTJSThreadManager.h" + +#import +#import +#import + +static NSString *const RCTJSThreadName = @"com.facebook.react.bridgeless.JavaScript"; + +#define RCTAssertJSThread() \ + RCTAssert(self->_jsThread == [NSThread currentThread], @"This method must be called on JS thread") + +@implementation RCTJSThreadManager { + NSThread *_jsThread; + std::shared_ptr _jsMessageThread; +} + +- (instancetype)init +{ + if (self = [super init]) { + [self startJSThread]; + __weak RCTJSThreadManager *weakSelf = self; + + dispatch_block_t captureJSThreadRunLoop = ^(void) { + __strong RCTJSThreadManager *strongSelf = weakSelf; + strongSelf->_jsMessageThread = + std::make_shared([NSRunLoop currentRunLoop], ^(NSError *error) { + if (error) { + [weakSelf _handleError:error]; + } + }); + }; + + [self performSelector:@selector(_tryAndHandleError:) + onThread:_jsThread + withObject:captureJSThreadRunLoop + waitUntilDone:YES]; + } + return self; +} + +- (std::shared_ptr)jsMessageThread +{ + return _jsMessageThread; +} + +- (void)dealloc +{ + // This avoids a race condition, where work can be executed on JS thread after + // other peices of infra are cleaned up. + _jsMessageThread->quitSynchronous(); +} + +#pragma mark - JSThread Management + +- (void)startJSThread FB_OBJC_DIRECT +{ + _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]; +} + +/** + * 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. + */ +- (void)dispatchToJSThread:(dispatch_block_t)block +{ + RCTAssert(_jsThread, @"This method must not be called before the JS thread is created"); + + if ([NSThread currentThread] == _jsThread) { + [self _tryAndHandleError:block]; + } else { + __weak __typeof(self) weakSelf = self; + _jsMessageThread->runOnQueue([weakSelf, block] { [weakSelf _tryAndHandleError:block]; }); + } +} + ++ (void)runRunLoop +{ + @autoreleasepool { + // 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); + + // run the run loop + while (kCFRunLoopRunStopped != + CFRunLoopRunInMode( + kCFRunLoopDefaultMode, ((NSDate *)[NSDate distantFuture]).timeIntervalSinceReferenceDate, NO)) { + RCTAssert(NO, @"not reached assertion"); // runloop spun. that's bad. + } + } +} + +#pragma mark - Private + +- (void)_handleError:(NSError *)error FB_OBJC_DIRECT +{ + RCTFatal(error); +} + +- (void)_tryAndHandleError:(dispatch_block_t)block +{ + NSError *error = facebook::react::tryAndReturnError(block); + if (error) { + [self _handleError:error]; + } +} + +@end diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTPerformanceLoggerUtils.h b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTPerformanceLoggerUtils.h new file mode 100644 index 00000000000000..85a86fc07f94c0 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTPerformanceLoggerUtils.h @@ -0,0 +1,16 @@ +/* + * 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 + +@class RCTPerformanceLogger; + +NS_ASSUME_NONNULL_BEGIN + +extern void registerPerformanceLoggerHooks(RCTPerformanceLogger *performanceLogger); + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTPerformanceLoggerUtils.mm b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTPerformanceLoggerUtils.mm new file mode 100644 index 00000000000000..a8c20b83f46757 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Core/RCTPerformanceLoggerUtils.mm @@ -0,0 +1,62 @@ +/* + * 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 "RCTPerformanceLoggerUtils.h" + +#import +#import + +using namespace facebook::react; + +static void mapReactMarkerToPerformanceLogger( + const ReactMarker::ReactMarkerId markerId, + RCTPerformanceLogger *performanceLogger) +{ + switch (markerId) { + case ReactMarker::RUN_JS_BUNDLE_START: + [performanceLogger markStartForTag:RCTPLScriptExecution]; + break; + case ReactMarker::RUN_JS_BUNDLE_STOP: + [performanceLogger markStopForTag:RCTPLScriptExecution]; + break; + case ReactMarker::NATIVE_REQUIRE_START: + [performanceLogger appendStartForTag:RCTPLRAMNativeRequires]; + break; + case ReactMarker::NATIVE_REQUIRE_STOP: + [performanceLogger appendStopForTag:RCTPLRAMNativeRequires]; + [performanceLogger addValue:1 forTag:RCTPLRAMNativeRequiresCount]; + break; + case ReactMarker::NATIVE_MODULE_SETUP_START: + [performanceLogger markStartForTag:RCTPLNativeModuleSetup]; + break; + case ReactMarker::NATIVE_MODULE_SETUP_STOP: + [performanceLogger markStopForTag:RCTPLNativeModuleSetup]; + break; + case ReactMarker::REACT_INSTANCE_INIT_START: + [performanceLogger markStartForTag:RCTPLReactInstanceInit]; + break; + case ReactMarker::REACT_INSTANCE_INIT_STOP: + [performanceLogger markStopForTag:RCTPLReactInstanceInit]; + 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; + } +} + +void registerPerformanceLoggerHooks(RCTPerformanceLogger *performanceLogger) +{ + __weak RCTPerformanceLogger *weakPerformanceLogger = performanceLogger; + ReactMarker::logTaggedMarkerBridgelessImpl = [weakPerformanceLogger]( + const ReactMarker::ReactMarkerId markerId, const char *tag) { + mapReactMarkerToPerformanceLogger(markerId, weakPerformanceLogger); + }; +} diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Hermes/RCTHermesInstance.h b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Hermes/RCTHermesInstance.h new file mode 100644 index 00000000000000..5cd910a2a71a43 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Hermes/RCTHermesInstance.h @@ -0,0 +1,38 @@ +/* + * 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 + +#import +#import +#import +#import + +namespace facebook { +namespace react { +using CrashManagerProvider = + std::function()>; + +// ObjC++ wrapper for HermesInstance.cpp +class RCTHermesInstance : public JSEngineInstance { + public: + RCTHermesInstance(); + RCTHermesInstance( + std::shared_ptr reactNativeConfig, + CrashManagerProvider crashManagerProvider); + + std::unique_ptr createJSRuntime() noexcept override; + + ~RCTHermesInstance(){}; + + private: + std::shared_ptr _reactNativeConfig; + CrashManagerProvider _crashManagerProvider; + std::unique_ptr _hermesInstance; +}; +} // namespace react +} // namespace facebook diff --git a/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Hermes/RCTHermesInstance.mm b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Hermes/RCTHermesInstance.mm new file mode 100644 index 00000000000000..681114d9af7fc3 --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/platform/ios/Hermes/RCTHermesInstance.mm @@ -0,0 +1,29 @@ +/* + * 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 "RCTHermesInstance.h" + +namespace facebook { +namespace react { +RCTHermesInstance::RCTHermesInstance() : RCTHermesInstance(nullptr, nullptr) {} + +RCTHermesInstance::RCTHermesInstance( + std::shared_ptr reactNativeConfig, + CrashManagerProvider crashManagerProvider) + : _reactNativeConfig(std::move(reactNativeConfig)), + _crashManagerProvider(std::move(crashManagerProvider)), + _hermesInstance(std::make_unique()) +{ +} + +std::unique_ptr RCTHermesInstance::createJSRuntime() noexcept +{ + return _hermesInstance->createJSRuntime( + _reactNativeConfig, _crashManagerProvider ? _crashManagerProvider() : nullptr); +} +} // namespace react +} // namespace facebook