From 59c87e66eb35f17e0b9d639ce894e6ccee7702e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20C=C3=A9sar=20Rocha?= Date: Wed, 8 Jun 2022 17:34:31 -0700 Subject: [PATCH 1/3] Do not use Blob/FileReader modules in UWP (#10079) * Do not use Blob/FileReader modules on UWP * Change files * Upgrade packages.lock.json * Use runtime option Blob.EnableModule to condition instantiation --- .../RNTesterIntegrationTests.cpp | 1 + vnext/Shared/OInstance.cpp | 1022 +++++++++-------- 2 files changed, 517 insertions(+), 506 deletions(-) diff --git a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp index ea1183a6a96..0813355bf0a 100644 --- a/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp +++ b/vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp @@ -28,6 +28,7 @@ TEST_MODULE_INITIALIZE(InitModule) { SetRuntimeOptionBool("WebSocket.AcceptSelfSigned", true); SetRuntimeOptionBool("UseBeastWebSocket", false); SetRuntimeOptionBool("Http.UseMonolithicModule", false); + SetRuntimeOptionBool("Blob.EnableModule", true); // WebSocketJSExecutor can't register native log hooks. SetRuntimeOptionBool("RNTester.UseWebDebugger", false); diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 97694c8d330..d6f09896269 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -71,591 +71,601 @@ using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; namespace Microsoft::React { -/*extern*/ std::unique_ptr CreateHttpModule( - winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept { - if (GetRuntimeOptionBool("Http.UseMonolithicModule")) { - return std::make_unique(); - } else { - return std::make_unique(inspectableProperties); + /*extern*/ std::unique_ptr CreateHttpModule( + winrt::Windows::Foundation::IInspectable const& inspectableProperties) noexcept { + if (GetRuntimeOptionBool("Http.UseMonolithicModule")) { + return std::make_unique(); + } + else { + return std::make_unique(inspectableProperties); + } } -} } // namespace Microsoft::React namespace facebook { -namespace react { - -namespace { - -class OJSIExecutorFactory : public JSExecutorFactory { - public: - std::unique_ptr createJSExecutor( - std::shared_ptr delegate, - std::shared_ptr jsQueue) override { - Logger logger; - if (loggingHook_) { - // TODO :: Ensure the logLevels are mapped properly. - logger = [loggingHook = std::move(loggingHook_)](const std::string &message, unsigned int logLevel) { - loggingHook(static_cast(logLevel), message.c_str()); - }; - } else { - logger = [loggingHook = std::move(loggingHook_)](const std::string & /*message*/, unsigned int /*logLevel*/) {}; - } - bindNativeLogger(*runtimeHolder_->getRuntime(), logger); + namespace react { + + namespace { + + class OJSIExecutorFactory : public JSExecutorFactory { + public: + std::unique_ptr createJSExecutor( + std::shared_ptr delegate, + std::shared_ptr jsQueue) override { + Logger logger; + if (loggingHook_) { + // TODO :: Ensure the logLevels are mapped properly. + logger = [loggingHook = std::move(loggingHook_)](const std::string& message, unsigned int logLevel) { + loggingHook(static_cast(logLevel), message.c_str()); + }; + } + else { + logger = [loggingHook = std::move(loggingHook_)](const std::string& /*message*/, unsigned int /*logLevel*/) {}; + } + bindNativeLogger(*runtimeHolder_->getRuntime(), logger); - auto turboModuleManager = std::make_shared(turboModuleRegistry_, jsCallInvoker_); + auto turboModuleManager = std::make_shared(turboModuleRegistry_, jsCallInvoker_); - // TODO: The binding here should also add the proxys that convert cxxmodules into turbomodules - auto binding = [turboModuleManager](const std::string &name) -> std::shared_ptr { - return turboModuleManager->getModule(name); - }; + // TODO: The binding here should also add the proxys that convert cxxmodules into turbomodules + auto binding = [turboModuleManager](const std::string& name) -> std::shared_ptr { + return turboModuleManager->getModule(name); + }; - TurboModuleBinding::install(*runtimeHolder_->getRuntime(), std::function(binding)); + TurboModuleBinding::install(*runtimeHolder_->getRuntime(), std::function(binding)); - // init TurboModule - for (const auto &moduleName : turboModuleManager->getEagerInitModuleNames()) { - turboModuleManager->getModule(moduleName); - } + // init TurboModule + for (const auto& moduleName : turboModuleManager->getEagerInitModuleNames()) { + turboModuleManager->getModule(moduleName); + } - return std::make_unique( - runtimeHolder_->getRuntime(), - std::move(delegate), - JSIExecutor::defaultTimeoutInvoker, - [isProfiling = isProfilingEnabled_]([[maybe_unused]] jsi::Runtime &runtime) { + return std::make_unique( + runtimeHolder_->getRuntime(), + std::move(delegate), + JSIExecutor::defaultTimeoutInvoker, + [isProfiling = isProfilingEnabled_]([[maybe_unused]] jsi::Runtime& runtime) { #ifdef ENABLE_JS_SYSTRACE_TO_ETW - facebook::react::tracing::initializeJSHooks(runtime, isProfiling); + facebook::react::tracing::initializeJSHooks(runtime, isProfiling); #endif - }); - } + }); + } + + OJSIExecutorFactory( + std::shared_ptr runtimeHolder, + NativeLoggingHook loggingHook, + std::shared_ptr turboModuleRegistry, + bool isProfilingEnabled, + std::shared_ptr jsCallInvoker) noexcept + : runtimeHolder_{ std::move(runtimeHolder) }, + loggingHook_{ std::move(loggingHook) }, + turboModuleRegistry_{ std::move(turboModuleRegistry) }, + jsCallInvoker_{ std::move(jsCallInvoker) }, + isProfilingEnabled_{ isProfilingEnabled } {} + + private: + std::shared_ptr runtimeHolder_; + std::shared_ptr turboModuleRegistry_; + std::shared_ptr jsCallInvoker_; + NativeLoggingHook loggingHook_; + bool isProfilingEnabled_; + }; + + } // namespace + + void logMarker(const facebook::react::ReactMarker::ReactMarkerId /*id*/, const char* /*tag*/) {} + + /*static*/ std::shared_ptr InstanceImpl::MakeNoBundle( + std::shared_ptr&& instance, + std::string&& jsBundleBasePath, + std::vector< + std::tuple>> + && cxxModules, + std::shared_ptr turboModuleRegistry, + std::unique_ptr&& callback, + std::shared_ptr jsQueue, + std::shared_ptr nativeQueue, + std::shared_ptr devSettings, + std::shared_ptr devManager) noexcept { + auto inner = std::shared_ptr(new InstanceImpl( + std::move(instance), + std::move(jsBundleBasePath), + std::move(cxxModules), + std::move(turboModuleRegistry), + std::move(callback), + std::move(jsQueue), + std::move(nativeQueue), + std::move(devSettings), + std::move(devManager))); + + inner->RegisterForReloadIfNecessary(); + + return inner; + } - OJSIExecutorFactory( - std::shared_ptr runtimeHolder, - NativeLoggingHook loggingHook, + /*static*/ std::shared_ptr InstanceImpl::MakeAndLoadBundle( + std::shared_ptr&& instance, + std::string&& jsBundleBasePath, + std::string&& jsBundleRelativePath, + std::vector< + std::tuple>> + && cxxModules, std::shared_ptr turboModuleRegistry, - bool isProfilingEnabled, - std::shared_ptr jsCallInvoker) noexcept - : runtimeHolder_{std::move(runtimeHolder)}, - loggingHook_{std::move(loggingHook)}, - turboModuleRegistry_{std::move(turboModuleRegistry)}, - jsCallInvoker_{std::move(jsCallInvoker)}, - isProfilingEnabled_{isProfilingEnabled} {} - - private: - std::shared_ptr runtimeHolder_; - std::shared_ptr turboModuleRegistry_; - std::shared_ptr jsCallInvoker_; - NativeLoggingHook loggingHook_; - bool isProfilingEnabled_; -}; - -} // namespace - -void logMarker(const facebook::react::ReactMarker::ReactMarkerId /*id*/, const char * /*tag*/) {} - -/*static*/ std::shared_ptr InstanceImpl::MakeNoBundle( - std::shared_ptr &&instance, - std::string &&jsBundleBasePath, - std::vector< - std::tuple>> - &&cxxModules, - std::shared_ptr turboModuleRegistry, - std::unique_ptr &&callback, - std::shared_ptr jsQueue, - std::shared_ptr nativeQueue, - std::shared_ptr devSettings, - std::shared_ptr devManager) noexcept { - auto inner = std::shared_ptr(new InstanceImpl( - std::move(instance), - std::move(jsBundleBasePath), - std::move(cxxModules), - std::move(turboModuleRegistry), - std::move(callback), - std::move(jsQueue), - std::move(nativeQueue), - std::move(devSettings), - std::move(devManager))); - - inner->RegisterForReloadIfNecessary(); - - return inner; -} - -/*static*/ std::shared_ptr InstanceImpl::MakeAndLoadBundle( - std::shared_ptr &&instance, - std::string &&jsBundleBasePath, - std::string &&jsBundleRelativePath, - std::vector< - std::tuple>> - &&cxxModules, - std::shared_ptr turboModuleRegistry, - std::unique_ptr &&callback, - std::shared_ptr jsQueue, - std::shared_ptr nativeQueue, - std::shared_ptr devSettings, - std::shared_ptr devManager) noexcept { - auto inner = std::shared_ptr(new InstanceImpl( - std::move(instance), - std::move(jsBundleBasePath), - std::move(cxxModules), - std::move(turboModuleRegistry), - std::move(callback), - std::move(jsQueue), - std::move(nativeQueue), - std::move(devSettings), - std::move(devManager))); - - inner->loadBundle(std::move(jsBundleRelativePath)); - inner->RegisterForReloadIfNecessary(); - - return inner; -} - -void InstanceImpl::SetInError() noexcept { - m_isInError = true; -} - -namespace { -bool shouldStartHermesInspector(DevSettings &devSettings) { - bool isHermes = - ((devSettings.jsiEngineOverride == JSIEngineOverride::Hermes) || - (devSettings.jsiEngineOverride == JSIEngineOverride::Default && devSettings.jsiRuntimeHolder && - devSettings.jsiRuntimeHolder->getRuntimeType() == facebook::react::JSIEngineOverride::Hermes)); - - if (isHermes && devSettings.useDirectDebugger && !devSettings.useWebDebugger) - return true; - else - return false; -} -} // namespace - -InstanceImpl::InstanceImpl( - std::shared_ptr &&instance, - std::string &&jsBundleBasePath, - std::vector< - std::tuple>> - &&cxxModules, - std::shared_ptr turboModuleRegistry, - std::unique_ptr &&callback, - std::shared_ptr jsQueue, - std::shared_ptr nativeQueue, - std::shared_ptr devSettings, - std::shared_ptr devManager) - : m_turboModuleRegistry(std::move(turboModuleRegistry)), + std::unique_ptr&& callback, + std::shared_ptr jsQueue, + std::shared_ptr nativeQueue, + std::shared_ptr devSettings, + std::shared_ptr devManager) noexcept { + auto inner = std::shared_ptr(new InstanceImpl( + std::move(instance), + std::move(jsBundleBasePath), + std::move(cxxModules), + std::move(turboModuleRegistry), + std::move(callback), + std::move(jsQueue), + std::move(nativeQueue), + std::move(devSettings), + std::move(devManager))); + + inner->loadBundle(std::move(jsBundleRelativePath)); + inner->RegisterForReloadIfNecessary(); + + return inner; + } + + void InstanceImpl::SetInError() noexcept { + m_isInError = true; + } + + namespace { + bool shouldStartHermesInspector(DevSettings& devSettings) { + bool isHermes = + ((devSettings.jsiEngineOverride == JSIEngineOverride::Hermes) || + (devSettings.jsiEngineOverride == JSIEngineOverride::Default && devSettings.jsiRuntimeHolder && + devSettings.jsiRuntimeHolder->getRuntimeType() == facebook::react::JSIEngineOverride::Hermes)); + + if (isHermes && devSettings.useDirectDebugger && !devSettings.useWebDebugger) + return true; + else + return false; + } + } // namespace + + InstanceImpl::InstanceImpl( + std::shared_ptr&& instance, + std::string&& jsBundleBasePath, + std::vector< + std::tuple>> + && cxxModules, + std::shared_ptr turboModuleRegistry, + std::unique_ptr&& callback, + std::shared_ptr jsQueue, + std::shared_ptr nativeQueue, + std::shared_ptr devSettings, + std::shared_ptr devManager) + : m_turboModuleRegistry(std::move(turboModuleRegistry)), m_jsThread(std::move(jsQueue)), m_nativeQueue(nativeQueue), m_jsBundleBasePath(std::move(jsBundleBasePath)), m_devSettings(std::move(devSettings)), m_devManager(std::move(devManager)), m_innerInstance(std::move(instance)) { - // Temp set the logmarker here - facebook::react::ReactMarker::logTaggedMarker = logMarker; + // Temp set the logmarker here + facebook::react::ReactMarker::logTaggedMarker = logMarker; #ifdef ENABLE_ETW_TRACING - // TODO :: Find a better place to initialize ETW once per process. - facebook::react::tracing::initializeETW(); + // TODO :: Find a better place to initialize ETW once per process. + facebook::react::tracing::initializeETW(); #endif - if (shouldStartHermesInspector(*m_devSettings)) { - m_devManager->StartInspector(m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort); - } + if (shouldStartHermesInspector(*m_devSettings)) { + m_devManager->StartInspector(m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort); + } - // Default (common) NativeModules - auto modules = GetDefaultNativeModules(nativeQueue); + // Default (common) NativeModules + auto modules = GetDefaultNativeModules(nativeQueue); - // Add app provided modules. - for (auto &cxxModule : cxxModules) { - modules.push_back(std::make_unique( - m_innerInstance, move(std::get<0>(cxxModule)), move(std::get<1>(cxxModule)), move(std::get<2>(cxxModule)))); - } - m_moduleRegistry = std::make_shared(std::move(modules)); - - // Choose JSExecutor - std::shared_ptr jsef; - if (m_devSettings->useWebDebugger) { - try { - auto jseFunc = m_devManager->LoadJavaScriptInProxyMode(*m_devSettings, [weakthis = weak_from_this()]() { - if (auto strongThis = weakthis.lock()) { - strongThis->SetInError(); - } - }); - - if ((jseFunc == nullptr) || m_isInError) { - m_devSettings->errorCallback("Failed to create JavaScript Executor."); - return; + // Add app provided modules. + for (auto& cxxModule : cxxModules) { + modules.push_back(std::make_unique( + m_innerInstance, move(std::get<0>(cxxModule)), move(std::get<1>(cxxModule)), move(std::get<2>(cxxModule)))); } + m_moduleRegistry = std::make_shared(std::move(modules)); + + // Choose JSExecutor + std::shared_ptr jsef; + if (m_devSettings->useWebDebugger) { + try { + auto jseFunc = m_devManager->LoadJavaScriptInProxyMode(*m_devSettings, [weakthis = weak_from_this()]() { + if (auto strongThis = weakthis.lock()) { + strongThis->SetInError(); + } + }); + + if ((jseFunc == nullptr) || m_isInError) { + m_devSettings->errorCallback("Failed to create JavaScript Executor."); + return; + } - jsef = std::make_shared(std::move(jseFunc)); - } catch (std::exception &e) { - m_devSettings->errorCallback(e.what()); - return; - } - } else { - if (m_devSettings->useFastRefresh || m_devSettings->liveReloadCallback) { - Microsoft::ReactNative::PackagerConnection::CreateOrReusePackagerConnection(*m_devSettings); - } + jsef = std::make_shared(std::move(jseFunc)); + } + catch (std::exception& e) { + m_devSettings->errorCallback(e.what()); + return; + } + } + else { + if (m_devSettings->useFastRefresh || m_devSettings->liveReloadCallback) { + Microsoft::ReactNative::PackagerConnection::CreateOrReusePackagerConnection(*m_devSettings); + } - // If the consumer gives us a JSI runtime, then use it. - if (m_devSettings->jsiRuntimeHolder) { - assert(m_devSettings->jsiEngineOverride == JSIEngineOverride::Default); - jsef = std::make_shared( - m_devSettings->jsiRuntimeHolder, - m_devSettings->loggingCallback, - m_turboModuleRegistry, - !m_devSettings->useFastRefresh, - m_innerInstance->getJSCallInvoker()); - } else { - assert(m_devSettings->jsiEngineOverride != JSIEngineOverride::Default); - switch (m_devSettings->jsiEngineOverride) { - case JSIEngineOverride::Hermes: - m_devSettings->jsiRuntimeHolder = std::make_shared(m_devSettings, m_jsThread); - m_devSettings->inlineSourceMap = false; - break; - case JSIEngineOverride::V8: { + // If the consumer gives us a JSI runtime, then use it. + if (m_devSettings->jsiRuntimeHolder) { + assert(m_devSettings->jsiEngineOverride == JSIEngineOverride::Default); + jsef = std::make_shared( + m_devSettings->jsiRuntimeHolder, + m_devSettings->loggingCallback, + m_turboModuleRegistry, + !m_devSettings->useFastRefresh, + m_innerInstance->getJSCallInvoker()); + } + else { + assert(m_devSettings->jsiEngineOverride != JSIEngineOverride::Default); + switch (m_devSettings->jsiEngineOverride) { + case JSIEngineOverride::Hermes: + m_devSettings->jsiRuntimeHolder = std::make_shared(m_devSettings, m_jsThread); + m_devSettings->inlineSourceMap = false; + break; + case JSIEngineOverride::V8: { #if defined(USE_V8) - std::unique_ptr scriptStore = nullptr; - std::unique_ptr preparedScriptStore = nullptr; + std::unique_ptr scriptStore = nullptr; + std::unique_ptr preparedScriptStore = nullptr; - char tempPath[MAX_PATH]; - if (GetTempPathA(MAX_PATH, tempPath)) { - preparedScriptStore = std::make_unique(tempPath); - } + char tempPath[MAX_PATH]; + if (GetTempPathA(MAX_PATH, tempPath)) { + preparedScriptStore = std::make_unique(tempPath); + } - m_devSettings->jsiRuntimeHolder = std::make_shared( + m_devSettings->jsiRuntimeHolder = std::make_shared( m_devSettings, m_jsThread, std::move(scriptStore), std::move(preparedScriptStore)); - break; + break; #else - assert(false); // V8 is not available in this build, fallthrough - [[fallthrough]]; + assert(false); // V8 is not available in this build, fallthrough + [[fallthrough]]; #endif - } - case JSIEngineOverride::V8NodeApi: { + } + case JSIEngineOverride::V8NodeApi: { #if defined(USE_V8) - std::unique_ptr preparedScriptStore; + std::unique_ptr preparedScriptStore; - wchar_t tempPath[MAX_PATH]; - if (GetTempPathW(static_cast(std::size(tempPath)), tempPath)) { - preparedScriptStore = + wchar_t tempPath[MAX_PATH]; + if (GetTempPathW(static_cast(std::size(tempPath)), tempPath)) { + preparedScriptStore = std::make_unique(winrt::to_string(tempPath)); - } + } - if (!preparedScriptStore) { - if (m_devSettings->errorCallback) - m_devSettings->errorCallback("Could not initialize prepared script store"); + if (!preparedScriptStore) { + if (m_devSettings->errorCallback) + m_devSettings->errorCallback("Could not initialize prepared script store"); - break; - } + break; + } - m_devSettings->jsiRuntimeHolder = make_shared( + m_devSettings->jsiRuntimeHolder = make_shared( m_devSettings, m_jsThread, nullptr /*scriptStore*/, std::move(preparedScriptStore)); - break; + break; #else - if (m_devSettings->errorCallback) - m_devSettings->errorCallback("JSI/V8/NAPI engine is not available in this build"); - assert(false); - [[fallthrough]]; + if (m_devSettings->errorCallback) + m_devSettings->errorCallback("JSI/V8/NAPI engine is not available in this build"); + assert(false); + [[fallthrough]]; #endif - } - case JSIEngineOverride::Chakra: - case JSIEngineOverride::ChakraCore: - default: // TODO: Add other engines once supported - m_devSettings->jsiRuntimeHolder = + } + case JSIEngineOverride::Chakra: + case JSIEngineOverride::ChakraCore: + default: // TODO: Add other engines once supported + m_devSettings->jsiRuntimeHolder = std::make_shared(m_devSettings, m_jsThread, nullptr, nullptr); - break; + break; + } + jsef = std::make_shared( + m_devSettings->jsiRuntimeHolder, + m_devSettings->loggingCallback, + m_turboModuleRegistry, + !m_devSettings->useFastRefresh, + m_innerInstance->getJSCallInvoker()); + } + } + + m_innerInstance->initializeBridge(std::move(callback), jsef, m_jsThread, m_moduleRegistry); + + // All JSI runtimes do support host objects and hence the native modules + // proxy. + const bool isNativeModulesProxyAvailable = ((m_devSettings->jsiRuntimeHolder != nullptr) || + (m_devSettings->jsiEngineOverride != JSIEngineOverride::Default)) && + !m_devSettings->useWebDebugger; + if (!isNativeModulesProxyAvailable) { + folly::dynamic configArray = folly::dynamic::array; + for (auto const& moduleName : m_moduleRegistry->moduleNames()) { + auto moduleConfig = m_moduleRegistry->getConfig(moduleName); + configArray.push_back(moduleConfig ? std::move(moduleConfig->config) : nullptr); + } + + folly::dynamic configs = folly::dynamic::object("remoteModuleConfig", std::move(configArray)); + m_innerInstance->setGlobalVariable( + "__fbBatchedBridgeConfig", std::make_unique(folly::toJson(configs))); } - jsef = std::make_shared( - m_devSettings->jsiRuntimeHolder, - m_devSettings->loggingCallback, - m_turboModuleRegistry, - !m_devSettings->useFastRefresh, - m_innerInstance->getJSCallInvoker()); } - } - m_innerInstance->initializeBridge(std::move(callback), jsef, m_jsThread, m_moduleRegistry); - - // All JSI runtimes do support host objects and hence the native modules - // proxy. - const bool isNativeModulesProxyAvailable = ((m_devSettings->jsiRuntimeHolder != nullptr) || - (m_devSettings->jsiEngineOverride != JSIEngineOverride::Default)) && - !m_devSettings->useWebDebugger; - if (!isNativeModulesProxyAvailable) { - folly::dynamic configArray = folly::dynamic::array; - for (auto const &moduleName : m_moduleRegistry->moduleNames()) { - auto moduleConfig = m_moduleRegistry->getConfig(moduleName); - configArray.push_back(moduleConfig ? std::move(moduleConfig->config) : nullptr); + void InstanceImpl::loadBundle(std::string&& jsBundleRelativePath) { + loadBundleInternal(std::move(jsBundleRelativePath), /*synchronously:*/ false); } - folly::dynamic configs = folly::dynamic::object("remoteModuleConfig", std::move(configArray)); - m_innerInstance->setGlobalVariable( - "__fbBatchedBridgeConfig", std::make_unique(folly::toJson(configs))); - } -} - -void InstanceImpl::loadBundle(std::string &&jsBundleRelativePath) { - loadBundleInternal(std::move(jsBundleRelativePath), /*synchronously:*/ false); -} - -void InstanceImpl::loadBundleSync(std::string &&jsBundleRelativePath) { - loadBundleInternal(std::move(jsBundleRelativePath), /*synchronously:*/ true); -} - -// Note: Based on -// https://github.com/facebook/react-native/blob/24d91268b64c7abbd4b26547ffcc663dc90ec5e7/ReactCommon/cxxreact/Instance.cpp#L112 -bool isHBCBundle(const std::string &bundle) { - static uint32_t constexpr HBCBundleMagicNumber = 0xffe7c3c3; - - // Note:: Directly access the pointer to avoid copy/length-check. It matters as this string contains the bundle which - // can be potentially huge. - // https://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/#comment-483 - auto header = reinterpret_cast(&bundle[0]); - if (HBCBundleMagicNumber == folly::Endian::little(header->magic)) { - return true; - } else { - return false; - } -} + void InstanceImpl::loadBundleSync(std::string&& jsBundleRelativePath) { + loadBundleInternal(std::move(jsBundleRelativePath), /*synchronously:*/ true); + } + + // Note: Based on + // https://github.com/facebook/react-native/blob/24d91268b64c7abbd4b26547ffcc663dc90ec5e7/ReactCommon/cxxreact/Instance.cpp#L112 + bool isHBCBundle(const std::string& bundle) { + static uint32_t constexpr HBCBundleMagicNumber = 0xffe7c3c3; + + // Note:: Directly access the pointer to avoid copy/length-check. It matters as this string contains the bundle which + // can be potentially huge. + // https://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/#comment-483 + auto header = reinterpret_cast(&bundle[0]); + if (HBCBundleMagicNumber == folly::Endian::little(header->magic)) { + return true; + } + else { + return false; + } + } -void InstanceImpl::loadBundleInternal(std::string &&jsBundleRelativePath, bool synchronously) { - try { - if (m_devSettings->useWebDebugger || m_devSettings->liveReloadCallback != nullptr || - m_devSettings->useFastRefresh) { - // First attempt to get download the Js locally, to catch any bundling - // errors before attempting to load the actual script. + void InstanceImpl::loadBundleInternal(std::string&& jsBundleRelativePath, bool synchronously) { + try { + if (m_devSettings->useWebDebugger || m_devSettings->liveReloadCallback != nullptr || + m_devSettings->useFastRefresh) { + // First attempt to get download the Js locally, to catch any bundling + // errors before attempting to load the actual script. - uint32_t hermesBytecodeVersion = 0; + uint32_t hermesBytecodeVersion = 0; #if defined(USE_HERMES) && defined(ENABLE_DEVSERVER_HBCBUNDLES) - hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; + hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; #endif - auto [jsBundleString, success] = Microsoft::ReactNative::GetJavaScriptFromServer( - m_devSettings->sourceBundleHost, - m_devSettings->sourceBundlePort, - m_devSettings->debugBundlePath.empty() ? jsBundleRelativePath : m_devSettings->debugBundlePath, - m_devSettings->platformName, - true /* dev */, - m_devSettings->useFastRefresh, - m_devSettings->inlineSourceMap, - hermesBytecodeVersion); + auto [jsBundleString, success] = Microsoft::ReactNative::GetJavaScriptFromServer( + m_devSettings->sourceBundleHost, + m_devSettings->sourceBundlePort, + m_devSettings->debugBundlePath.empty() ? jsBundleRelativePath : m_devSettings->debugBundlePath, + m_devSettings->platformName, + true /* dev */, + m_devSettings->useFastRefresh, + m_devSettings->inlineSourceMap, + hermesBytecodeVersion); - if (!success) { - m_devManager->UpdateBundleStatus(false, -1); - m_devSettings->errorCallback(jsBundleString); - return; - } + if (!success) { + m_devManager->UpdateBundleStatus(false, -1); + m_devSettings->errorCallback(jsBundleString); + return; + } - int64_t currentTimeInMilliSeconds = - std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) - .count(); - m_devManager->UpdateBundleStatus(true, currentTimeInMilliSeconds); + int64_t currentTimeInMilliSeconds = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + m_devManager->UpdateBundleStatus(true, currentTimeInMilliSeconds); - auto bundleUrl = DevServerHelper::get_BundleUrl( - m_devSettings->sourceBundleHost, - m_devSettings->sourceBundlePort, - m_devSettings->debugBundlePath.empty() ? jsBundleRelativePath : m_devSettings->debugBundlePath, - m_devSettings->platformName, - /*dev*/ true, - /*hot*/ false, - m_devSettings->inlineSourceMap, - hermesBytecodeVersion); - - // This code is based on the HBC Bundle integration on Android - // Ref: - // https://github.com/facebook/react-native/blob/24d91268b64c7abbd4b26547ffcc663dc90ec5e7/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp#L231 - if (isHBCBundle(jsBundleString)) { - auto script = std::make_unique(jsBundleString, false); - const char *buffer = script->c_str(); - uint32_t bufferLength = (uint32_t)script->size(); - - // Please refer the code here for details on the file format: - // https://github.com/facebook/metro/blob/b1bacf52070be62872d6bd3420f37a4405ed34e6/packages/metro/src/lib/bundleToBytecode.js#L29 - // Essentially, there is an 8 byte long file header with 4 bytes of a magic number followed by 4 bytes to encode - // the number of modules.The module buffers follows, each one starts with 4 byte header which encodes module - // length.A properly formatted HBCB should have at least 8 bytes.. - uint32_t offset = 8; + auto bundleUrl = DevServerHelper::get_BundleUrl( + m_devSettings->sourceBundleHost, + m_devSettings->sourceBundlePort, + m_devSettings->debugBundlePath.empty() ? jsBundleRelativePath : m_devSettings->debugBundlePath, + m_devSettings->platformName, + /*dev*/ true, + /*hot*/ false, + m_devSettings->inlineSourceMap, + hermesBytecodeVersion); + + // This code is based on the HBC Bundle integration on Android + // Ref: + // https://github.com/facebook/react-native/blob/24d91268b64c7abbd4b26547ffcc663dc90ec5e7/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp#L231 + if (isHBCBundle(jsBundleString)) { + auto script = std::make_unique(jsBundleString, false); + const char* buffer = script->c_str(); + uint32_t bufferLength = (uint32_t)script->size(); + + // Please refer the code here for details on the file format: + // https://github.com/facebook/metro/blob/b1bacf52070be62872d6bd3420f37a4405ed34e6/packages/metro/src/lib/bundleToBytecode.js#L29 + // Essentially, there is an 8 byte long file header with 4 bytes of a magic number followed by 4 bytes to encode + // the number of modules.The module buffers follows, each one starts with 4 byte header which encodes module + // length.A properly formatted HBCB should have at least 8 bytes.. + uint32_t offset = 8; #define __SAFEADD__(s1, s2, t) \ if (!msl::utilities::SafeAdd(s1, s2, t)) \ break; - while (offset < bufferLength) { - uint32_t segment; - __SAFEADD__(offset, 4, segment) - uint32_t moduleLength = (bufferLength < segment) ? 0 : *(((uint32_t *)buffer) + offset / 4); - - // Early break if the module length is computed as 0.. as the segment start may be overflowing the buffer. - if (moduleLength == 0) - break; - - uint32_t segmentEnd; - __SAFEADD__(moduleLength, segment, segmentEnd) - // Early break if the segment overflows beyond the buffer. This is unlikely for a properly formatted - // HBCB though. - if (segmentEnd > bufferLength) - break; - - m_innerInstance->loadScriptFromString( - std::make_unique(std::string(buffer + segment, buffer + segmentEnd)), + while (offset < bufferLength) { + uint32_t segment; + __SAFEADD__(offset, 4, segment) + uint32_t moduleLength = (bufferLength < segment) ? 0 : *(((uint32_t*)buffer) + offset / 4); + + // Early break if the module length is computed as 0.. as the segment start may be overflowing the buffer. + if (moduleLength == 0) + break; + + uint32_t segmentEnd; + __SAFEADD__(moduleLength, segment, segmentEnd) + // Early break if the segment overflows beyond the buffer. This is unlikely for a properly formatted + // HBCB though. + if (segmentEnd > bufferLength) + break; + + m_innerInstance->loadScriptFromString( + std::make_unique(std::string(buffer + segment, buffer + segmentEnd)), + bundleUrl, + false); + + // Aligned at 4 byte boundary. + offset += ((moduleLength + 3) & ~3) + 4; + } +#undef __SAFEADD__ + } + else { + // Remote debug executor loads script from a Uri, rather than taking the actual bundle string + m_innerInstance->loadScriptFromString( + std::make_unique(m_devSettings->useWebDebugger ? bundleUrl : jsBundleString), bundleUrl, - false); - - // Aligned at 4 byte boundary. - offset += ((moduleLength + 3) & ~3) + 4; + synchronously); + } } -#undef __SAFEADD__ - } else { - // Remote debug executor loads script from a Uri, rather than taking the actual bundle string - m_innerInstance->loadScriptFromString( - std::make_unique(m_devSettings->useWebDebugger ? bundleUrl : jsBundleString), - bundleUrl, - synchronously); - } - } else { + else { #if (defined(_MSC_VER) && !defined(WINRT)) - std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / jsBundleRelativePath).string(); - auto bundleString = FileMappingBigString::fromPath(bundlePath); + std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / jsBundleRelativePath).string(); + auto bundleString = FileMappingBigString::fromPath(bundlePath); #else - std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / (jsBundleRelativePath + ".bundle")).string(); - auto bundleString = std::make_unique<::Microsoft::ReactNative::StorageFileBigString>(bundlePath); + std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / (jsBundleRelativePath + ".bundle")).string(); + auto bundleString = std::make_unique<::Microsoft::ReactNative::StorageFileBigString>(bundlePath); #endif - m_innerInstance->loadScriptFromString(std::move(bundleString), std::move(jsBundleRelativePath), synchronously); + m_innerInstance->loadScriptFromString(std::move(bundleString), std::move(jsBundleRelativePath), synchronously); + } + } + catch (const std::exception& e) { + m_devSettings->errorCallback(e.what()); + } + catch (const winrt::hresult_error& hrerr) { + std::stringstream ss; + ss << "[" << std::hex << std::showbase << std::setw(8) << static_cast(hrerr.code()) << "] " + << winrt::to_string(hrerr.message()); + + m_devSettings->errorCallback(std::move(ss.str())); + } } - } catch (const std::exception &e) { - m_devSettings->errorCallback(e.what()); - } catch (const winrt::hresult_error &hrerr) { - std::stringstream ss; - ss << "[" << std::hex << std::showbase << std::setw(8) << static_cast(hrerr.code()) << "] " - << winrt::to_string(hrerr.message()); - - m_devSettings->errorCallback(std::move(ss.str())); - } -} -InstanceImpl::~InstanceImpl() { - if (m_devSettings->jsiEngineOverride == JSIEngineOverride::Hermes) { - m_devManager->StopInspector(); - } - m_nativeQueue->quitSynchronous(); -} - -std::vector> InstanceImpl::GetDefaultNativeModules( - std::shared_ptr nativeQueue) { - std::vector> modules; - auto transitionalProps{ReactPropertyBagHelper::CreatePropertyBag()}; - - modules.push_back(std::make_unique( - m_innerInstance, - Microsoft::React::GetHttpModuleName(), - [nativeQueue, transitionalProps]() -> std::unique_ptr { - return Microsoft::React::CreateHttpModule(transitionalProps); - }, - nativeQueue)); - - modules.push_back(std::make_unique( - m_innerInstance, - Microsoft::React::GetWebSocketModuleName(), - [nativeQueue, transitionalProps]() -> std::unique_ptr { - return Microsoft::React::CreateWebSocketModule(transitionalProps); - }, - nativeQueue)); - - // TODO: This is not included for UWP because we have a different module which - // is added later. However, this one is designed - // so that we can base a UWP version on it. We need to do that but is not high - // priority. + InstanceImpl::~InstanceImpl() { + if (m_devSettings->jsiEngineOverride == JSIEngineOverride::Hermes) { + m_devManager->StopInspector(); + } + m_nativeQueue->quitSynchronous(); + } + + std::vector> InstanceImpl::GetDefaultNativeModules( + std::shared_ptr nativeQueue) { + std::vector> modules; + auto transitionalProps{ ReactPropertyBagHelper::CreatePropertyBag() }; + + modules.push_back(std::make_unique( + m_innerInstance, + Microsoft::React::GetHttpModuleName(), + [nativeQueue, transitionalProps]() -> std::unique_ptr { + return Microsoft::React::CreateHttpModule(transitionalProps); + }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + Microsoft::React::GetWebSocketModuleName(), + [nativeQueue, transitionalProps]() -> std::unique_ptr { + return Microsoft::React::CreateWebSocketModule(transitionalProps); + }, + nativeQueue)); + + // TODO: This is not included for UWP because we have a different module which + // is added later. However, this one is designed + // so that we can base a UWP version on it. We need to do that but is not high + // priority. #if (defined(_MSC_VER) && !defined(WINRT)) - modules.push_back(std::make_unique( - m_innerInstance, - "Timing", - [nativeQueue]() -> std::unique_ptr { return react::CreateTimingModule(nativeQueue); }, - nativeQueue)); + modules.push_back(std::make_unique( + m_innerInstance, + "Timing", + [nativeQueue]() -> std::unique_ptr { return react::CreateTimingModule(nativeQueue); }, + nativeQueue)); #endif - uint32_t hermesBytecodeVersion = 0; + uint32_t hermesBytecodeVersion = 0; #if defined(USE_HERMES) && defined(ENABLE_DEVSERVER_HBCBUNDLES) - hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; + hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; #endif - // TODO - Encapsulate this in a helpers, and make sure callers add it to their - // list - std::string bundleUrl = (m_devSettings->useWebDebugger || m_devSettings->liveReloadCallback) - ? DevServerHelper::get_BundleUrl( - m_devSettings->sourceBundleHost, - m_devSettings->sourceBundlePort, - m_devSettings->debugBundlePath, - m_devSettings->platformName, - true /*dev*/, - m_devSettings->useFastRefresh, - m_devSettings->inlineSourceMap, - hermesBytecodeVersion) - : std::string(); - modules.push_back(std::make_unique( - m_innerInstance, - facebook::react::SourceCodeModule::Name, - [bundleUrl]() -> std::unique_ptr { - return std::make_unique(bundleUrl); - }, - nativeQueue)); - - modules.push_back(std::make_unique( - m_innerInstance, - "ExceptionsManager", - [redboxHandler = m_devSettings->redboxHandler]() mutable { - return std::make_unique(redboxHandler); - }, - nativeQueue)); - - modules.push_back(std::make_unique( - m_innerInstance, - PlatformConstantsModule::Name, - []() { return std::make_unique(); }, - nativeQueue)); - - modules.push_back(std::make_unique( - m_innerInstance, - StatusBarManagerModule::Name, - []() { return std::make_unique(); }, - nativeQueue)); - -// TODO: Follow up - Blob module not supported in UWP. Need to define property bag lifetime and onwership. -#if (defined(_MSC_VER) && !defined(WINRT)) - modules.push_back(std::make_unique( - m_innerInstance, - Microsoft::React::GetBlobModuleName(), - [transitionalProps]() { return Microsoft::React::CreateBlobModule(transitionalProps); }, - nativeQueue)); - - modules.push_back(std::make_unique( - m_innerInstance, - Microsoft::React::GetFileReaderModuleName(), - [transitionalProps]() { return Microsoft::React::CreateFileReaderModule(transitionalProps); }, - nativeQueue)); -#endif + // TODO - Encapsulate this in a helpers, and make sure callers add it to their + // list + std::string bundleUrl = (m_devSettings->useWebDebugger || m_devSettings->liveReloadCallback) + ? DevServerHelper::get_BundleUrl( + m_devSettings->sourceBundleHost, + m_devSettings->sourceBundlePort, + m_devSettings->debugBundlePath, + m_devSettings->platformName, + true /*dev*/, + m_devSettings->useFastRefresh, + m_devSettings->inlineSourceMap, + hermesBytecodeVersion) + : std::string(); + modules.push_back(std::make_unique( + m_innerInstance, + facebook::react::SourceCodeModule::Name, + [bundleUrl]() -> std::unique_ptr { + return std::make_unique(bundleUrl); + }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + "ExceptionsManager", + [redboxHandler = m_devSettings->redboxHandler]() mutable { + return std::make_unique(redboxHandler); + }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + PlatformConstantsModule::Name, + []() { return std::make_unique(); }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + StatusBarManagerModule::Name, + []() { return std::make_unique(); }, + nativeQueue)); + + // #10036 - Blob module not supported in UWP. Need to define property bag lifetime and onwership. + if (Microsoft::React::GetRuntimeOptionBool("Blob.EnableModule")) { + modules.push_back(std::make_unique( + m_innerInstance, + Microsoft::React::GetBlobModuleName(), + [transitionalProps]() { return Microsoft::React::CreateBlobModule(transitionalProps); }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + Microsoft::React::GetFileReaderModuleName(), + [transitionalProps]() { return Microsoft::React::CreateFileReaderModule(transitionalProps); }, + nativeQueue)); + } - return modules; -} + return modules; + } -void InstanceImpl::RegisterForReloadIfNecessary() noexcept { - // setup polling for live reload - if (!m_isInError && !m_devSettings->useFastRefresh && m_devSettings->liveReloadCallback != nullptr) { - m_devManager->StartPollingLiveReload( - m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort, m_devSettings->liveReloadCallback); - } -} + void InstanceImpl::RegisterForReloadIfNecessary() noexcept { + // setup polling for live reload + if (!m_isInError && !m_devSettings->useFastRefresh && m_devSettings->liveReloadCallback != nullptr) { + m_devManager->StartPollingLiveReload( + m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort, m_devSettings->liveReloadCallback); + } + } -void InstanceImpl::DispatchEvent(int64_t viewTag, std::string eventName, folly::dynamic &&eventData) { - if (m_isInError) { - return; - } + void InstanceImpl::DispatchEvent(int64_t viewTag, std::string eventName, folly::dynamic&& eventData) { + if (m_isInError) { + return; + } - folly::dynamic params = folly::dynamic::array(viewTag, eventName, std::move(eventData)); - m_innerInstance->callJSFunction("RCTEventEmitter", "receiveEvent", std::move(params)); -} + folly::dynamic params = folly::dynamic::array(viewTag, eventName, std::move(eventData)); + m_innerInstance->callJSFunction("RCTEventEmitter", "receiveEvent", std::move(params)); + } -void InstanceImpl::invokeCallback(const int64_t callbackId, folly::dynamic &¶ms) { - if (m_isInError) { - return; - } + void InstanceImpl::invokeCallback(const int64_t callbackId, folly::dynamic&& params) { + if (m_isInError) { + return; + } - m_innerInstance->callJSCallback(callbackId, std::move(params)); -} + m_innerInstance->callJSCallback(callbackId, std::move(params)); + } -} // namespace react + } // namespace react } // namespace facebook From 8ceacdf16d2c574aba22d07e32d63521d61db27f Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 8 Jun 2022 17:53:04 -0700 Subject: [PATCH 2/3] Change files --- ...ative-windows-a8df45ea-26f4-44f5-9b82-712c9aad497e.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-a8df45ea-26f4-44f5-9b82-712c9aad497e.json diff --git a/change/react-native-windows-a8df45ea-26f4-44f5-9b82-712c9aad497e.json b/change/react-native-windows-a8df45ea-26f4-44f5-9b82-712c9aad497e.json new file mode 100644 index 00000000000..bdc304a634d --- /dev/null +++ b/change/react-native-windows-a8df45ea-26f4-44f5-9b82-712c9aad497e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Enable Blob module with runtime option", + "packageName": "react-native-windows", + "email": "julio.rocha@microsoft.com", + "dependentChangeType": "patch" +} From dfc50fd0e317a45ab5320e721364f87c0088238e Mon Sep 17 00:00:00 2001 From: "Julio C. Rocha" Date: Wed, 8 Jun 2022 18:14:37 -0700 Subject: [PATCH 3/3] clang format --- vnext/Shared/OInstance.cpp | 1012 ++++++++++++++++++------------------ 1 file changed, 501 insertions(+), 511 deletions(-) diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index d6f09896269..6bae7ab25ba 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -71,601 +71,591 @@ using winrt::Microsoft::ReactNative::ReactPropertyBagHelper; namespace Microsoft::React { - /*extern*/ std::unique_ptr CreateHttpModule( - winrt::Windows::Foundation::IInspectable const& inspectableProperties) noexcept { - if (GetRuntimeOptionBool("Http.UseMonolithicModule")) { - return std::make_unique(); - } - else { - return std::make_unique(inspectableProperties); - } +/*extern*/ std::unique_ptr CreateHttpModule( + winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept { + if (GetRuntimeOptionBool("Http.UseMonolithicModule")) { + return std::make_unique(); + } else { + return std::make_unique(inspectableProperties); } +} } // namespace Microsoft::React namespace facebook { - namespace react { - - namespace { - - class OJSIExecutorFactory : public JSExecutorFactory { - public: - std::unique_ptr createJSExecutor( - std::shared_ptr delegate, - std::shared_ptr jsQueue) override { - Logger logger; - if (loggingHook_) { - // TODO :: Ensure the logLevels are mapped properly. - logger = [loggingHook = std::move(loggingHook_)](const std::string& message, unsigned int logLevel) { - loggingHook(static_cast(logLevel), message.c_str()); - }; - } - else { - logger = [loggingHook = std::move(loggingHook_)](const std::string& /*message*/, unsigned int /*logLevel*/) {}; - } - bindNativeLogger(*runtimeHolder_->getRuntime(), logger); - - auto turboModuleManager = std::make_shared(turboModuleRegistry_, jsCallInvoker_); - - // TODO: The binding here should also add the proxys that convert cxxmodules into turbomodules - auto binding = [turboModuleManager](const std::string& name) -> std::shared_ptr { - return turboModuleManager->getModule(name); - }; - - TurboModuleBinding::install(*runtimeHolder_->getRuntime(), std::function(binding)); - - // init TurboModule - for (const auto& moduleName : turboModuleManager->getEagerInitModuleNames()) { - turboModuleManager->getModule(moduleName); - } - - return std::make_unique( - runtimeHolder_->getRuntime(), - std::move(delegate), - JSIExecutor::defaultTimeoutInvoker, - [isProfiling = isProfilingEnabled_]([[maybe_unused]] jsi::Runtime& runtime) { -#ifdef ENABLE_JS_SYSTRACE_TO_ETW - facebook::react::tracing::initializeJSHooks(runtime, isProfiling); -#endif - }); - } - - OJSIExecutorFactory( - std::shared_ptr runtimeHolder, - NativeLoggingHook loggingHook, - std::shared_ptr turboModuleRegistry, - bool isProfilingEnabled, - std::shared_ptr jsCallInvoker) noexcept - : runtimeHolder_{ std::move(runtimeHolder) }, - loggingHook_{ std::move(loggingHook) }, - turboModuleRegistry_{ std::move(turboModuleRegistry) }, - jsCallInvoker_{ std::move(jsCallInvoker) }, - isProfilingEnabled_{ isProfilingEnabled } {} - - private: - std::shared_ptr runtimeHolder_; - std::shared_ptr turboModuleRegistry_; - std::shared_ptr jsCallInvoker_; - NativeLoggingHook loggingHook_; - bool isProfilingEnabled_; +namespace react { + +namespace { + +class OJSIExecutorFactory : public JSExecutorFactory { + public: + std::unique_ptr createJSExecutor( + std::shared_ptr delegate, + std::shared_ptr jsQueue) override { + Logger logger; + if (loggingHook_) { + // TODO :: Ensure the logLevels are mapped properly. + logger = [loggingHook = std::move(loggingHook_)](const std::string &message, unsigned int logLevel) { + loggingHook(static_cast(logLevel), message.c_str()); }; + } else { + logger = [loggingHook = std::move(loggingHook_)](const std::string & /*message*/, unsigned int /*logLevel*/) {}; + } + bindNativeLogger(*runtimeHolder_->getRuntime(), logger); - } // namespace + auto turboModuleManager = std::make_shared(turboModuleRegistry_, jsCallInvoker_); - void logMarker(const facebook::react::ReactMarker::ReactMarkerId /*id*/, const char* /*tag*/) {} + // TODO: The binding here should also add the proxys that convert cxxmodules into turbomodules + auto binding = [turboModuleManager](const std::string &name) -> std::shared_ptr { + return turboModuleManager->getModule(name); + }; - /*static*/ std::shared_ptr InstanceImpl::MakeNoBundle( - std::shared_ptr&& instance, - std::string&& jsBundleBasePath, - std::vector< - std::tuple>> - && cxxModules, - std::shared_ptr turboModuleRegistry, - std::unique_ptr&& callback, - std::shared_ptr jsQueue, - std::shared_ptr nativeQueue, - std::shared_ptr devSettings, - std::shared_ptr devManager) noexcept { - auto inner = std::shared_ptr(new InstanceImpl( - std::move(instance), - std::move(jsBundleBasePath), - std::move(cxxModules), - std::move(turboModuleRegistry), - std::move(callback), - std::move(jsQueue), - std::move(nativeQueue), - std::move(devSettings), - std::move(devManager))); - - inner->RegisterForReloadIfNecessary(); - - return inner; - } + TurboModuleBinding::install(*runtimeHolder_->getRuntime(), std::function(binding)); - /*static*/ std::shared_ptr InstanceImpl::MakeAndLoadBundle( - std::shared_ptr&& instance, - std::string&& jsBundleBasePath, - std::string&& jsBundleRelativePath, - std::vector< - std::tuple>> - && cxxModules, - std::shared_ptr turboModuleRegistry, - std::unique_ptr&& callback, - std::shared_ptr jsQueue, - std::shared_ptr nativeQueue, - std::shared_ptr devSettings, - std::shared_ptr devManager) noexcept { - auto inner = std::shared_ptr(new InstanceImpl( - std::move(instance), - std::move(jsBundleBasePath), - std::move(cxxModules), - std::move(turboModuleRegistry), - std::move(callback), - std::move(jsQueue), - std::move(nativeQueue), - std::move(devSettings), - std::move(devManager))); - - inner->loadBundle(std::move(jsBundleRelativePath)); - inner->RegisterForReloadIfNecessary(); - - return inner; + // init TurboModule + for (const auto &moduleName : turboModuleManager->getEagerInitModuleNames()) { + turboModuleManager->getModule(moduleName); } - void InstanceImpl::SetInError() noexcept { - m_isInError = true; - } + return std::make_unique( + runtimeHolder_->getRuntime(), + std::move(delegate), + JSIExecutor::defaultTimeoutInvoker, + [isProfiling = isProfilingEnabled_]([[maybe_unused]] jsi::Runtime &runtime) { +#ifdef ENABLE_JS_SYSTRACE_TO_ETW + facebook::react::tracing::initializeJSHooks(runtime, isProfiling); +#endif + }); + } - namespace { - bool shouldStartHermesInspector(DevSettings& devSettings) { - bool isHermes = - ((devSettings.jsiEngineOverride == JSIEngineOverride::Hermes) || - (devSettings.jsiEngineOverride == JSIEngineOverride::Default && devSettings.jsiRuntimeHolder && - devSettings.jsiRuntimeHolder->getRuntimeType() == facebook::react::JSIEngineOverride::Hermes)); - - if (isHermes && devSettings.useDirectDebugger && !devSettings.useWebDebugger) - return true; - else - return false; - } - } // namespace - - InstanceImpl::InstanceImpl( - std::shared_ptr&& instance, - std::string&& jsBundleBasePath, - std::vector< - std::tuple>> - && cxxModules, + OJSIExecutorFactory( + std::shared_ptr runtimeHolder, + NativeLoggingHook loggingHook, std::shared_ptr turboModuleRegistry, - std::unique_ptr&& callback, - std::shared_ptr jsQueue, - std::shared_ptr nativeQueue, - std::shared_ptr devSettings, - std::shared_ptr devManager) - : m_turboModuleRegistry(std::move(turboModuleRegistry)), + bool isProfilingEnabled, + std::shared_ptr jsCallInvoker) noexcept + : runtimeHolder_{std::move(runtimeHolder)}, + loggingHook_{std::move(loggingHook)}, + turboModuleRegistry_{std::move(turboModuleRegistry)}, + jsCallInvoker_{std::move(jsCallInvoker)}, + isProfilingEnabled_{isProfilingEnabled} {} + + private: + std::shared_ptr runtimeHolder_; + std::shared_ptr turboModuleRegistry_; + std::shared_ptr jsCallInvoker_; + NativeLoggingHook loggingHook_; + bool isProfilingEnabled_; +}; + +} // namespace + +void logMarker(const facebook::react::ReactMarker::ReactMarkerId /*id*/, const char * /*tag*/) {} + +/*static*/ std::shared_ptr InstanceImpl::MakeNoBundle( + std::shared_ptr &&instance, + std::string &&jsBundleBasePath, + std::vector< + std::tuple>> + &&cxxModules, + std::shared_ptr turboModuleRegistry, + std::unique_ptr &&callback, + std::shared_ptr jsQueue, + std::shared_ptr nativeQueue, + std::shared_ptr devSettings, + std::shared_ptr devManager) noexcept { + auto inner = std::shared_ptr(new InstanceImpl( + std::move(instance), + std::move(jsBundleBasePath), + std::move(cxxModules), + std::move(turboModuleRegistry), + std::move(callback), + std::move(jsQueue), + std::move(nativeQueue), + std::move(devSettings), + std::move(devManager))); + + inner->RegisterForReloadIfNecessary(); + + return inner; +} + +/*static*/ std::shared_ptr InstanceImpl::MakeAndLoadBundle( + std::shared_ptr &&instance, + std::string &&jsBundleBasePath, + std::string &&jsBundleRelativePath, + std::vector< + std::tuple>> + &&cxxModules, + std::shared_ptr turboModuleRegistry, + std::unique_ptr &&callback, + std::shared_ptr jsQueue, + std::shared_ptr nativeQueue, + std::shared_ptr devSettings, + std::shared_ptr devManager) noexcept { + auto inner = std::shared_ptr(new InstanceImpl( + std::move(instance), + std::move(jsBundleBasePath), + std::move(cxxModules), + std::move(turboModuleRegistry), + std::move(callback), + std::move(jsQueue), + std::move(nativeQueue), + std::move(devSettings), + std::move(devManager))); + + inner->loadBundle(std::move(jsBundleRelativePath)); + inner->RegisterForReloadIfNecessary(); + + return inner; +} + +void InstanceImpl::SetInError() noexcept { + m_isInError = true; +} + +namespace { +bool shouldStartHermesInspector(DevSettings &devSettings) { + bool isHermes = + ((devSettings.jsiEngineOverride == JSIEngineOverride::Hermes) || + (devSettings.jsiEngineOverride == JSIEngineOverride::Default && devSettings.jsiRuntimeHolder && + devSettings.jsiRuntimeHolder->getRuntimeType() == facebook::react::JSIEngineOverride::Hermes)); + + if (isHermes && devSettings.useDirectDebugger && !devSettings.useWebDebugger) + return true; + else + return false; +} +} // namespace + +InstanceImpl::InstanceImpl( + std::shared_ptr &&instance, + std::string &&jsBundleBasePath, + std::vector< + std::tuple>> + &&cxxModules, + std::shared_ptr turboModuleRegistry, + std::unique_ptr &&callback, + std::shared_ptr jsQueue, + std::shared_ptr nativeQueue, + std::shared_ptr devSettings, + std::shared_ptr devManager) + : m_turboModuleRegistry(std::move(turboModuleRegistry)), m_jsThread(std::move(jsQueue)), m_nativeQueue(nativeQueue), m_jsBundleBasePath(std::move(jsBundleBasePath)), m_devSettings(std::move(devSettings)), m_devManager(std::move(devManager)), m_innerInstance(std::move(instance)) { - // Temp set the logmarker here - facebook::react::ReactMarker::logTaggedMarker = logMarker; + // Temp set the logmarker here + facebook::react::ReactMarker::logTaggedMarker = logMarker; #ifdef ENABLE_ETW_TRACING - // TODO :: Find a better place to initialize ETW once per process. - facebook::react::tracing::initializeETW(); + // TODO :: Find a better place to initialize ETW once per process. + facebook::react::tracing::initializeETW(); #endif - if (shouldStartHermesInspector(*m_devSettings)) { - m_devManager->StartInspector(m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort); - } - - // Default (common) NativeModules - auto modules = GetDefaultNativeModules(nativeQueue); + if (shouldStartHermesInspector(*m_devSettings)) { + m_devManager->StartInspector(m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort); + } - // Add app provided modules. - for (auto& cxxModule : cxxModules) { - modules.push_back(std::make_unique( - m_innerInstance, move(std::get<0>(cxxModule)), move(std::get<1>(cxxModule)), move(std::get<2>(cxxModule)))); - } - m_moduleRegistry = std::make_shared(std::move(modules)); - - // Choose JSExecutor - std::shared_ptr jsef; - if (m_devSettings->useWebDebugger) { - try { - auto jseFunc = m_devManager->LoadJavaScriptInProxyMode(*m_devSettings, [weakthis = weak_from_this()]() { - if (auto strongThis = weakthis.lock()) { - strongThis->SetInError(); - } - }); - - if ((jseFunc == nullptr) || m_isInError) { - m_devSettings->errorCallback("Failed to create JavaScript Executor."); - return; - } + // Default (common) NativeModules + auto modules = GetDefaultNativeModules(nativeQueue); - jsef = std::make_shared(std::move(jseFunc)); - } - catch (std::exception& e) { - m_devSettings->errorCallback(e.what()); - return; + // Add app provided modules. + for (auto &cxxModule : cxxModules) { + modules.push_back(std::make_unique( + m_innerInstance, move(std::get<0>(cxxModule)), move(std::get<1>(cxxModule)), move(std::get<2>(cxxModule)))); + } + m_moduleRegistry = std::make_shared(std::move(modules)); + + // Choose JSExecutor + std::shared_ptr jsef; + if (m_devSettings->useWebDebugger) { + try { + auto jseFunc = m_devManager->LoadJavaScriptInProxyMode(*m_devSettings, [weakthis = weak_from_this()]() { + if (auto strongThis = weakthis.lock()) { + strongThis->SetInError(); } + }); + + if ((jseFunc == nullptr) || m_isInError) { + m_devSettings->errorCallback("Failed to create JavaScript Executor."); + return; } - else { - if (m_devSettings->useFastRefresh || m_devSettings->liveReloadCallback) { - Microsoft::ReactNative::PackagerConnection::CreateOrReusePackagerConnection(*m_devSettings); - } - // If the consumer gives us a JSI runtime, then use it. - if (m_devSettings->jsiRuntimeHolder) { - assert(m_devSettings->jsiEngineOverride == JSIEngineOverride::Default); - jsef = std::make_shared( - m_devSettings->jsiRuntimeHolder, - m_devSettings->loggingCallback, - m_turboModuleRegistry, - !m_devSettings->useFastRefresh, - m_innerInstance->getJSCallInvoker()); - } - else { - assert(m_devSettings->jsiEngineOverride != JSIEngineOverride::Default); - switch (m_devSettings->jsiEngineOverride) { - case JSIEngineOverride::Hermes: - m_devSettings->jsiRuntimeHolder = std::make_shared(m_devSettings, m_jsThread); - m_devSettings->inlineSourceMap = false; - break; - case JSIEngineOverride::V8: { + jsef = std::make_shared(std::move(jseFunc)); + } catch (std::exception &e) { + m_devSettings->errorCallback(e.what()); + return; + } + } else { + if (m_devSettings->useFastRefresh || m_devSettings->liveReloadCallback) { + Microsoft::ReactNative::PackagerConnection::CreateOrReusePackagerConnection(*m_devSettings); + } + + // If the consumer gives us a JSI runtime, then use it. + if (m_devSettings->jsiRuntimeHolder) { + assert(m_devSettings->jsiEngineOverride == JSIEngineOverride::Default); + jsef = std::make_shared( + m_devSettings->jsiRuntimeHolder, + m_devSettings->loggingCallback, + m_turboModuleRegistry, + !m_devSettings->useFastRefresh, + m_innerInstance->getJSCallInvoker()); + } else { + assert(m_devSettings->jsiEngineOverride != JSIEngineOverride::Default); + switch (m_devSettings->jsiEngineOverride) { + case JSIEngineOverride::Hermes: + m_devSettings->jsiRuntimeHolder = std::make_shared(m_devSettings, m_jsThread); + m_devSettings->inlineSourceMap = false; + break; + case JSIEngineOverride::V8: { #if defined(USE_V8) - std::unique_ptr scriptStore = nullptr; - std::unique_ptr preparedScriptStore = nullptr; + std::unique_ptr scriptStore = nullptr; + std::unique_ptr preparedScriptStore = nullptr; - char tempPath[MAX_PATH]; - if (GetTempPathA(MAX_PATH, tempPath)) { - preparedScriptStore = std::make_unique(tempPath); - } + char tempPath[MAX_PATH]; + if (GetTempPathA(MAX_PATH, tempPath)) { + preparedScriptStore = std::make_unique(tempPath); + } - m_devSettings->jsiRuntimeHolder = std::make_shared( + m_devSettings->jsiRuntimeHolder = std::make_shared( m_devSettings, m_jsThread, std::move(scriptStore), std::move(preparedScriptStore)); - break; + break; #else - assert(false); // V8 is not available in this build, fallthrough - [[fallthrough]]; + assert(false); // V8 is not available in this build, fallthrough + [[fallthrough]]; #endif - } - case JSIEngineOverride::V8NodeApi: { + } + case JSIEngineOverride::V8NodeApi: { #if defined(USE_V8) - std::unique_ptr preparedScriptStore; + std::unique_ptr preparedScriptStore; - wchar_t tempPath[MAX_PATH]; - if (GetTempPathW(static_cast(std::size(tempPath)), tempPath)) { - preparedScriptStore = + wchar_t tempPath[MAX_PATH]; + if (GetTempPathW(static_cast(std::size(tempPath)), tempPath)) { + preparedScriptStore = std::make_unique(winrt::to_string(tempPath)); - } + } - if (!preparedScriptStore) { - if (m_devSettings->errorCallback) - m_devSettings->errorCallback("Could not initialize prepared script store"); + if (!preparedScriptStore) { + if (m_devSettings->errorCallback) + m_devSettings->errorCallback("Could not initialize prepared script store"); - break; - } + break; + } - m_devSettings->jsiRuntimeHolder = make_shared( + m_devSettings->jsiRuntimeHolder = make_shared( m_devSettings, m_jsThread, nullptr /*scriptStore*/, std::move(preparedScriptStore)); - break; + break; #else - if (m_devSettings->errorCallback) - m_devSettings->errorCallback("JSI/V8/NAPI engine is not available in this build"); - assert(false); - [[fallthrough]]; + if (m_devSettings->errorCallback) + m_devSettings->errorCallback("JSI/V8/NAPI engine is not available in this build"); + assert(false); + [[fallthrough]]; #endif - } - case JSIEngineOverride::Chakra: - case JSIEngineOverride::ChakraCore: - default: // TODO: Add other engines once supported - m_devSettings->jsiRuntimeHolder = - std::make_shared(m_devSettings, m_jsThread, nullptr, nullptr); - break; - } - jsef = std::make_shared( - m_devSettings->jsiRuntimeHolder, - m_devSettings->loggingCallback, - m_turboModuleRegistry, - !m_devSettings->useFastRefresh, - m_innerInstance->getJSCallInvoker()); } + case JSIEngineOverride::Chakra: + case JSIEngineOverride::ChakraCore: + default: // TODO: Add other engines once supported + m_devSettings->jsiRuntimeHolder = + std::make_shared(m_devSettings, m_jsThread, nullptr, nullptr); + break; } - - m_innerInstance->initializeBridge(std::move(callback), jsef, m_jsThread, m_moduleRegistry); - - // All JSI runtimes do support host objects and hence the native modules - // proxy. - const bool isNativeModulesProxyAvailable = ((m_devSettings->jsiRuntimeHolder != nullptr) || - (m_devSettings->jsiEngineOverride != JSIEngineOverride::Default)) && - !m_devSettings->useWebDebugger; - if (!isNativeModulesProxyAvailable) { - folly::dynamic configArray = folly::dynamic::array; - for (auto const& moduleName : m_moduleRegistry->moduleNames()) { - auto moduleConfig = m_moduleRegistry->getConfig(moduleName); - configArray.push_back(moduleConfig ? std::move(moduleConfig->config) : nullptr); - } - - folly::dynamic configs = folly::dynamic::object("remoteModuleConfig", std::move(configArray)); - m_innerInstance->setGlobalVariable( - "__fbBatchedBridgeConfig", std::make_unique(folly::toJson(configs))); - } - } - - void InstanceImpl::loadBundle(std::string&& jsBundleRelativePath) { - loadBundleInternal(std::move(jsBundleRelativePath), /*synchronously:*/ false); + jsef = std::make_shared( + m_devSettings->jsiRuntimeHolder, + m_devSettings->loggingCallback, + m_turboModuleRegistry, + !m_devSettings->useFastRefresh, + m_innerInstance->getJSCallInvoker()); } + } - void InstanceImpl::loadBundleSync(std::string&& jsBundleRelativePath) { - loadBundleInternal(std::move(jsBundleRelativePath), /*synchronously:*/ true); + m_innerInstance->initializeBridge(std::move(callback), jsef, m_jsThread, m_moduleRegistry); + + // All JSI runtimes do support host objects and hence the native modules + // proxy. + const bool isNativeModulesProxyAvailable = ((m_devSettings->jsiRuntimeHolder != nullptr) || + (m_devSettings->jsiEngineOverride != JSIEngineOverride::Default)) && + !m_devSettings->useWebDebugger; + if (!isNativeModulesProxyAvailable) { + folly::dynamic configArray = folly::dynamic::array; + for (auto const &moduleName : m_moduleRegistry->moduleNames()) { + auto moduleConfig = m_moduleRegistry->getConfig(moduleName); + configArray.push_back(moduleConfig ? std::move(moduleConfig->config) : nullptr); } - // Note: Based on - // https://github.com/facebook/react-native/blob/24d91268b64c7abbd4b26547ffcc663dc90ec5e7/ReactCommon/cxxreact/Instance.cpp#L112 - bool isHBCBundle(const std::string& bundle) { - static uint32_t constexpr HBCBundleMagicNumber = 0xffe7c3c3; - - // Note:: Directly access the pointer to avoid copy/length-check. It matters as this string contains the bundle which - // can be potentially huge. - // https://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/#comment-483 - auto header = reinterpret_cast(&bundle[0]); - if (HBCBundleMagicNumber == folly::Endian::little(header->magic)) { - return true; - } - else { - return false; - } - } + folly::dynamic configs = folly::dynamic::object("remoteModuleConfig", std::move(configArray)); + m_innerInstance->setGlobalVariable( + "__fbBatchedBridgeConfig", std::make_unique(folly::toJson(configs))); + } +} + +void InstanceImpl::loadBundle(std::string &&jsBundleRelativePath) { + loadBundleInternal(std::move(jsBundleRelativePath), /*synchronously:*/ false); +} + +void InstanceImpl::loadBundleSync(std::string &&jsBundleRelativePath) { + loadBundleInternal(std::move(jsBundleRelativePath), /*synchronously:*/ true); +} + +// Note: Based on +// https://github.com/facebook/react-native/blob/24d91268b64c7abbd4b26547ffcc663dc90ec5e7/ReactCommon/cxxreact/Instance.cpp#L112 +bool isHBCBundle(const std::string &bundle) { + static uint32_t constexpr HBCBundleMagicNumber = 0xffe7c3c3; + + // Note:: Directly access the pointer to avoid copy/length-check. It matters as this string contains the bundle which + // can be potentially huge. + // https://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/#comment-483 + auto header = reinterpret_cast(&bundle[0]); + if (HBCBundleMagicNumber == folly::Endian::little(header->magic)) { + return true; + } else { + return false; + } +} - void InstanceImpl::loadBundleInternal(std::string&& jsBundleRelativePath, bool synchronously) { - try { - if (m_devSettings->useWebDebugger || m_devSettings->liveReloadCallback != nullptr || - m_devSettings->useFastRefresh) { - // First attempt to get download the Js locally, to catch any bundling - // errors before attempting to load the actual script. +void InstanceImpl::loadBundleInternal(std::string &&jsBundleRelativePath, bool synchronously) { + try { + if (m_devSettings->useWebDebugger || m_devSettings->liveReloadCallback != nullptr || + m_devSettings->useFastRefresh) { + // First attempt to get download the Js locally, to catch any bundling + // errors before attempting to load the actual script. - uint32_t hermesBytecodeVersion = 0; + uint32_t hermesBytecodeVersion = 0; #if defined(USE_HERMES) && defined(ENABLE_DEVSERVER_HBCBUNDLES) - hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; + hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; #endif - auto [jsBundleString, success] = Microsoft::ReactNative::GetJavaScriptFromServer( - m_devSettings->sourceBundleHost, - m_devSettings->sourceBundlePort, - m_devSettings->debugBundlePath.empty() ? jsBundleRelativePath : m_devSettings->debugBundlePath, - m_devSettings->platformName, - true /* dev */, - m_devSettings->useFastRefresh, - m_devSettings->inlineSourceMap, - hermesBytecodeVersion); + auto [jsBundleString, success] = Microsoft::ReactNative::GetJavaScriptFromServer( + m_devSettings->sourceBundleHost, + m_devSettings->sourceBundlePort, + m_devSettings->debugBundlePath.empty() ? jsBundleRelativePath : m_devSettings->debugBundlePath, + m_devSettings->platformName, + true /* dev */, + m_devSettings->useFastRefresh, + m_devSettings->inlineSourceMap, + hermesBytecodeVersion); - if (!success) { - m_devManager->UpdateBundleStatus(false, -1); - m_devSettings->errorCallback(jsBundleString); - return; - } + if (!success) { + m_devManager->UpdateBundleStatus(false, -1); + m_devSettings->errorCallback(jsBundleString); + return; + } - int64_t currentTimeInMilliSeconds = - std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) - .count(); - m_devManager->UpdateBundleStatus(true, currentTimeInMilliSeconds); + int64_t currentTimeInMilliSeconds = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + m_devManager->UpdateBundleStatus(true, currentTimeInMilliSeconds); - auto bundleUrl = DevServerHelper::get_BundleUrl( - m_devSettings->sourceBundleHost, - m_devSettings->sourceBundlePort, - m_devSettings->debugBundlePath.empty() ? jsBundleRelativePath : m_devSettings->debugBundlePath, - m_devSettings->platformName, - /*dev*/ true, - /*hot*/ false, - m_devSettings->inlineSourceMap, - hermesBytecodeVersion); - - // This code is based on the HBC Bundle integration on Android - // Ref: - // https://github.com/facebook/react-native/blob/24d91268b64c7abbd4b26547ffcc663dc90ec5e7/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp#L231 - if (isHBCBundle(jsBundleString)) { - auto script = std::make_unique(jsBundleString, false); - const char* buffer = script->c_str(); - uint32_t bufferLength = (uint32_t)script->size(); - - // Please refer the code here for details on the file format: - // https://github.com/facebook/metro/blob/b1bacf52070be62872d6bd3420f37a4405ed34e6/packages/metro/src/lib/bundleToBytecode.js#L29 - // Essentially, there is an 8 byte long file header with 4 bytes of a magic number followed by 4 bytes to encode - // the number of modules.The module buffers follows, each one starts with 4 byte header which encodes module - // length.A properly formatted HBCB should have at least 8 bytes.. - uint32_t offset = 8; + auto bundleUrl = DevServerHelper::get_BundleUrl( + m_devSettings->sourceBundleHost, + m_devSettings->sourceBundlePort, + m_devSettings->debugBundlePath.empty() ? jsBundleRelativePath : m_devSettings->debugBundlePath, + m_devSettings->platformName, + /*dev*/ true, + /*hot*/ false, + m_devSettings->inlineSourceMap, + hermesBytecodeVersion); + + // This code is based on the HBC Bundle integration on Android + // Ref: + // https://github.com/facebook/react-native/blob/24d91268b64c7abbd4b26547ffcc663dc90ec5e7/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp#L231 + if (isHBCBundle(jsBundleString)) { + auto script = std::make_unique(jsBundleString, false); + const char *buffer = script->c_str(); + uint32_t bufferLength = (uint32_t)script->size(); + + // Please refer the code here for details on the file format: + // https://github.com/facebook/metro/blob/b1bacf52070be62872d6bd3420f37a4405ed34e6/packages/metro/src/lib/bundleToBytecode.js#L29 + // Essentially, there is an 8 byte long file header with 4 bytes of a magic number followed by 4 bytes to encode + // the number of modules.The module buffers follows, each one starts with 4 byte header which encodes module + // length.A properly formatted HBCB should have at least 8 bytes.. + uint32_t offset = 8; #define __SAFEADD__(s1, s2, t) \ if (!msl::utilities::SafeAdd(s1, s2, t)) \ break; - while (offset < bufferLength) { - uint32_t segment; - __SAFEADD__(offset, 4, segment) - uint32_t moduleLength = (bufferLength < segment) ? 0 : *(((uint32_t*)buffer) + offset / 4); - - // Early break if the module length is computed as 0.. as the segment start may be overflowing the buffer. - if (moduleLength == 0) - break; - - uint32_t segmentEnd; - __SAFEADD__(moduleLength, segment, segmentEnd) - // Early break if the segment overflows beyond the buffer. This is unlikely for a properly formatted - // HBCB though. - if (segmentEnd > bufferLength) - break; - - m_innerInstance->loadScriptFromString( - std::make_unique(std::string(buffer + segment, buffer + segmentEnd)), - bundleUrl, - false); - - // Aligned at 4 byte boundary. - offset += ((moduleLength + 3) & ~3) + 4; - } -#undef __SAFEADD__ - } - else { - // Remote debug executor loads script from a Uri, rather than taking the actual bundle string - m_innerInstance->loadScriptFromString( - std::make_unique(m_devSettings->useWebDebugger ? bundleUrl : jsBundleString), + while (offset < bufferLength) { + uint32_t segment; + __SAFEADD__(offset, 4, segment) + uint32_t moduleLength = (bufferLength < segment) ? 0 : *(((uint32_t *)buffer) + offset / 4); + + // Early break if the module length is computed as 0.. as the segment start may be overflowing the buffer. + if (moduleLength == 0) + break; + + uint32_t segmentEnd; + __SAFEADD__(moduleLength, segment, segmentEnd) + // Early break if the segment overflows beyond the buffer. This is unlikely for a properly formatted + // HBCB though. + if (segmentEnd > bufferLength) + break; + + m_innerInstance->loadScriptFromString( + std::make_unique(std::string(buffer + segment, buffer + segmentEnd)), bundleUrl, - synchronously); - } + false); + + // Aligned at 4 byte boundary. + offset += ((moduleLength + 3) & ~3) + 4; } - else { +#undef __SAFEADD__ + } else { + // Remote debug executor loads script from a Uri, rather than taking the actual bundle string + m_innerInstance->loadScriptFromString( + std::make_unique(m_devSettings->useWebDebugger ? bundleUrl : jsBundleString), + bundleUrl, + synchronously); + } + } else { #if (defined(_MSC_VER) && !defined(WINRT)) - std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / jsBundleRelativePath).string(); - auto bundleString = FileMappingBigString::fromPath(bundlePath); + std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / jsBundleRelativePath).string(); + auto bundleString = FileMappingBigString::fromPath(bundlePath); #else - std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / (jsBundleRelativePath + ".bundle")).string(); - auto bundleString = std::make_unique<::Microsoft::ReactNative::StorageFileBigString>(bundlePath); + std::string bundlePath = (fs::path(m_devSettings->bundleRootPath) / (jsBundleRelativePath + ".bundle")).string(); + auto bundleString = std::make_unique<::Microsoft::ReactNative::StorageFileBigString>(bundlePath); #endif - m_innerInstance->loadScriptFromString(std::move(bundleString), std::move(jsBundleRelativePath), synchronously); - } - } - catch (const std::exception& e) { - m_devSettings->errorCallback(e.what()); - } - catch (const winrt::hresult_error& hrerr) { - std::stringstream ss; - ss << "[" << std::hex << std::showbase << std::setw(8) << static_cast(hrerr.code()) << "] " - << winrt::to_string(hrerr.message()); - - m_devSettings->errorCallback(std::move(ss.str())); - } + m_innerInstance->loadScriptFromString(std::move(bundleString), std::move(jsBundleRelativePath), synchronously); } + } catch (const std::exception &e) { + m_devSettings->errorCallback(e.what()); + } catch (const winrt::hresult_error &hrerr) { + std::stringstream ss; + ss << "[" << std::hex << std::showbase << std::setw(8) << static_cast(hrerr.code()) << "] " + << winrt::to_string(hrerr.message()); + + m_devSettings->errorCallback(std::move(ss.str())); + } +} - InstanceImpl::~InstanceImpl() { - if (m_devSettings->jsiEngineOverride == JSIEngineOverride::Hermes) { - m_devManager->StopInspector(); - } - m_nativeQueue->quitSynchronous(); - } - - std::vector> InstanceImpl::GetDefaultNativeModules( - std::shared_ptr nativeQueue) { - std::vector> modules; - auto transitionalProps{ ReactPropertyBagHelper::CreatePropertyBag() }; - - modules.push_back(std::make_unique( - m_innerInstance, - Microsoft::React::GetHttpModuleName(), - [nativeQueue, transitionalProps]() -> std::unique_ptr { - return Microsoft::React::CreateHttpModule(transitionalProps); - }, - nativeQueue)); - - modules.push_back(std::make_unique( - m_innerInstance, - Microsoft::React::GetWebSocketModuleName(), - [nativeQueue, transitionalProps]() -> std::unique_ptr { - return Microsoft::React::CreateWebSocketModule(transitionalProps); - }, - nativeQueue)); - - // TODO: This is not included for UWP because we have a different module which - // is added later. However, this one is designed - // so that we can base a UWP version on it. We need to do that but is not high - // priority. +InstanceImpl::~InstanceImpl() { + if (m_devSettings->jsiEngineOverride == JSIEngineOverride::Hermes) { + m_devManager->StopInspector(); + } + m_nativeQueue->quitSynchronous(); +} + +std::vector> InstanceImpl::GetDefaultNativeModules( + std::shared_ptr nativeQueue) { + std::vector> modules; + auto transitionalProps{ReactPropertyBagHelper::CreatePropertyBag()}; + + modules.push_back(std::make_unique( + m_innerInstance, + Microsoft::React::GetHttpModuleName(), + [nativeQueue, transitionalProps]() -> std::unique_ptr { + return Microsoft::React::CreateHttpModule(transitionalProps); + }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + Microsoft::React::GetWebSocketModuleName(), + [nativeQueue, transitionalProps]() -> std::unique_ptr { + return Microsoft::React::CreateWebSocketModule(transitionalProps); + }, + nativeQueue)); + + // TODO: This is not included for UWP because we have a different module which + // is added later. However, this one is designed + // so that we can base a UWP version on it. We need to do that but is not high + // priority. #if (defined(_MSC_VER) && !defined(WINRT)) - modules.push_back(std::make_unique( - m_innerInstance, - "Timing", - [nativeQueue]() -> std::unique_ptr { return react::CreateTimingModule(nativeQueue); }, - nativeQueue)); + modules.push_back(std::make_unique( + m_innerInstance, + "Timing", + [nativeQueue]() -> std::unique_ptr { return react::CreateTimingModule(nativeQueue); }, + nativeQueue)); #endif - uint32_t hermesBytecodeVersion = 0; + uint32_t hermesBytecodeVersion = 0; #if defined(USE_HERMES) && defined(ENABLE_DEVSERVER_HBCBUNDLES) - hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; + hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; #endif - // TODO - Encapsulate this in a helpers, and make sure callers add it to their - // list - std::string bundleUrl = (m_devSettings->useWebDebugger || m_devSettings->liveReloadCallback) - ? DevServerHelper::get_BundleUrl( - m_devSettings->sourceBundleHost, - m_devSettings->sourceBundlePort, - m_devSettings->debugBundlePath, - m_devSettings->platformName, - true /*dev*/, - m_devSettings->useFastRefresh, - m_devSettings->inlineSourceMap, - hermesBytecodeVersion) - : std::string(); - modules.push_back(std::make_unique( - m_innerInstance, - facebook::react::SourceCodeModule::Name, - [bundleUrl]() -> std::unique_ptr { - return std::make_unique(bundleUrl); - }, - nativeQueue)); - - modules.push_back(std::make_unique( - m_innerInstance, - "ExceptionsManager", - [redboxHandler = m_devSettings->redboxHandler]() mutable { - return std::make_unique(redboxHandler); - }, - nativeQueue)); - - modules.push_back(std::make_unique( + // TODO - Encapsulate this in a helpers, and make sure callers add it to their + // list + std::string bundleUrl = (m_devSettings->useWebDebugger || m_devSettings->liveReloadCallback) + ? DevServerHelper::get_BundleUrl( + m_devSettings->sourceBundleHost, + m_devSettings->sourceBundlePort, + m_devSettings->debugBundlePath, + m_devSettings->platformName, + true /*dev*/, + m_devSettings->useFastRefresh, + m_devSettings->inlineSourceMap, + hermesBytecodeVersion) + : std::string(); + modules.push_back(std::make_unique( + m_innerInstance, + facebook::react::SourceCodeModule::Name, + [bundleUrl]() -> std::unique_ptr { + return std::make_unique(bundleUrl); + }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + "ExceptionsManager", + [redboxHandler = m_devSettings->redboxHandler]() mutable { + return std::make_unique(redboxHandler); + }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + PlatformConstantsModule::Name, + []() { return std::make_unique(); }, + nativeQueue)); + + modules.push_back(std::make_unique( + m_innerInstance, + StatusBarManagerModule::Name, + []() { return std::make_unique(); }, + nativeQueue)); + + // #10036 - Blob module not supported in UWP. Need to define property bag lifetime and onwership. + if (Microsoft::React::GetRuntimeOptionBool("Blob.EnableModule")) { + modules.push_back(std::make_unique( m_innerInstance, - PlatformConstantsModule::Name, - []() { return std::make_unique(); }, + Microsoft::React::GetBlobModuleName(), + [transitionalProps]() { return Microsoft::React::CreateBlobModule(transitionalProps); }, nativeQueue)); - modules.push_back(std::make_unique( + modules.push_back(std::make_unique( m_innerInstance, - StatusBarManagerModule::Name, - []() { return std::make_unique(); }, + Microsoft::React::GetFileReaderModuleName(), + [transitionalProps]() { return Microsoft::React::CreateFileReaderModule(transitionalProps); }, nativeQueue)); + } - // #10036 - Blob module not supported in UWP. Need to define property bag lifetime and onwership. - if (Microsoft::React::GetRuntimeOptionBool("Blob.EnableModule")) { - modules.push_back(std::make_unique( - m_innerInstance, - Microsoft::React::GetBlobModuleName(), - [transitionalProps]() { return Microsoft::React::CreateBlobModule(transitionalProps); }, - nativeQueue)); - - modules.push_back(std::make_unique( - m_innerInstance, - Microsoft::React::GetFileReaderModuleName(), - [transitionalProps]() { return Microsoft::React::CreateFileReaderModule(transitionalProps); }, - nativeQueue)); - } - - return modules; - } + return modules; +} - void InstanceImpl::RegisterForReloadIfNecessary() noexcept { - // setup polling for live reload - if (!m_isInError && !m_devSettings->useFastRefresh && m_devSettings->liveReloadCallback != nullptr) { - m_devManager->StartPollingLiveReload( - m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort, m_devSettings->liveReloadCallback); - } - } +void InstanceImpl::RegisterForReloadIfNecessary() noexcept { + // setup polling for live reload + if (!m_isInError && !m_devSettings->useFastRefresh && m_devSettings->liveReloadCallback != nullptr) { + m_devManager->StartPollingLiveReload( + m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort, m_devSettings->liveReloadCallback); + } +} - void InstanceImpl::DispatchEvent(int64_t viewTag, std::string eventName, folly::dynamic&& eventData) { - if (m_isInError) { - return; - } +void InstanceImpl::DispatchEvent(int64_t viewTag, std::string eventName, folly::dynamic &&eventData) { + if (m_isInError) { + return; + } - folly::dynamic params = folly::dynamic::array(viewTag, eventName, std::move(eventData)); - m_innerInstance->callJSFunction("RCTEventEmitter", "receiveEvent", std::move(params)); - } + folly::dynamic params = folly::dynamic::array(viewTag, eventName, std::move(eventData)); + m_innerInstance->callJSFunction("RCTEventEmitter", "receiveEvent", std::move(params)); +} - void InstanceImpl::invokeCallback(const int64_t callbackId, folly::dynamic&& params) { - if (m_isInError) { - return; - } +void InstanceImpl::invokeCallback(const int64_t callbackId, folly::dynamic &¶ms) { + if (m_isInError) { + return; + } - m_innerInstance->callJSCallback(callbackId, std::move(params)); - } + m_innerInstance->callJSCallback(callbackId, std::move(params)); +} - } // namespace react +} // namespace react } // namespace facebook