diff --git a/change/@react-native-windows-telemetry-778ea18a-3b80-476b-92e7-d46058128073.json b/change/@react-native-windows-telemetry-778ea18a-3b80-476b-92e7-d46058128073.json
new file mode 100644
index 00000000000..8d57ff071d1
--- /dev/null
+++ b/change/@react-native-windows-telemetry-778ea18a-3b80-476b-92e7-d46058128073.json
@@ -0,0 +1,7 @@
+{
+ "type": "prerelease",
+ "comment": "Use latest Hermes ABI-safe API",
+ "packageName": "@react-native-windows/telemetry",
+ "email": "vmoroz@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/react-native-windows-af5ee70c-4767-4145-82a9-61331fa11ab8.json b/change/react-native-windows-af5ee70c-4767-4145-82a9-61331fa11ab8.json
new file mode 100644
index 00000000000..2fd7a34e344
--- /dev/null
+++ b/change/react-native-windows-af5ee70c-4767-4145-82a9-61331fa11ab8.json
@@ -0,0 +1,7 @@
+{
+ "type": "prerelease",
+ "comment": "Fix Hermes sampling profiler",
+ "packageName": "react-native-windows",
+ "email": "vmoroz@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/@react-native-windows/telemetry/src/test/projects/UsesPackagesConfig/packages.config b/packages/@react-native-windows/telemetry/src/test/projects/UsesPackagesConfig/packages.config
index bfdf98cee24..36e557597d4 100644
--- a/packages/@react-native-windows/telemetry/src/test/projects/UsesPackagesConfig/packages.config
+++ b/packages/@react-native-windows/telemetry/src/test/projects/UsesPackagesConfig/packages.config
@@ -2,5 +2,5 @@
-
+
\ No newline at end of file
diff --git a/packages/playground/windows/playground-composition/packages.config b/packages/playground/windows/playground-composition/packages.config
index 5ae64eb1317..c48e07e6e6e 100644
--- a/packages/playground/windows/playground-composition/packages.config
+++ b/packages/playground/windows/playground-composition/packages.config
@@ -9,5 +9,5 @@
-
+
\ No newline at end of file
diff --git a/packages/playground/windows/playground-win32/packages.config b/packages/playground/windows/playground-win32/packages.config
index 4bfd09e1c4f..4a8c3a4ded2 100644
--- a/packages/playground/windows/playground-win32/packages.config
+++ b/packages/playground/windows/playground-win32/packages.config
@@ -9,5 +9,5 @@
-
+
\ No newline at end of file
diff --git a/packages/playground/windows/playground/packages.config b/packages/playground/windows/playground/packages.config
index 157347a6a7b..1b6be79b5ec 100644
--- a/packages/playground/windows/playground/packages.config
+++ b/packages/playground/windows/playground/packages.config
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative/IReactDispatcher.cpp b/vnext/Microsoft.ReactNative/IReactDispatcher.cpp
index e45aa59eb71..5787721515c 100644
--- a/vnext/Microsoft.ReactNative/IReactDispatcher.cpp
+++ b/vnext/Microsoft.ReactNative/IReactDispatcher.cpp
@@ -124,6 +124,10 @@ void ReactDispatcher::InvokeElsePost(Mso::DispatchTask &&task) const noexcept {
return jsThreadDispatcherProperty;
}
+/*static*/ IReactDispatcher ReactDispatcher::GetJSDispatcher(IReactPropertyBag const &properties) noexcept {
+ return properties.Get(JSDispatcherProperty()).try_as();
+}
+
/*static*/ IReactPropertyName ReactDispatcher::JSDispatcherTaskStartingEventName() noexcept {
static IReactPropertyName jsThreadDispatcherProperty{ReactPropertyBagHelper::GetName(
ReactPropertyBagHelper::GetNamespace(L"ReactNative.Dispatcher"), L"JSDispatcherTaskStartingEventName")};
diff --git a/vnext/Microsoft.ReactNative/IReactDispatcher.h b/vnext/Microsoft.ReactNative/IReactDispatcher.h
index f13cfb0b4b4..52e749b782b 100644
--- a/vnext/Microsoft.ReactNative/IReactDispatcher.h
+++ b/vnext/Microsoft.ReactNative/IReactDispatcher.h
@@ -38,6 +38,7 @@ struct ReactDispatcher : implementsProperties()), nullptr);
if (onDestroyed) {
onDestroyed.Get()->Invoke(reactContext);
}
@@ -484,10 +485,14 @@ void ReactInstanceWin::Initialize() noexcept {
std::unique_ptr preparedScriptStore = nullptr;
switch (m_options.JsiEngine()) {
- case JSIEngine::Hermes:
- devSettings->jsiRuntimeHolder =
+ case JSIEngine::Hermes: {
+ auto hermesRuntimeHolder =
std::make_shared(devSettings, m_jsMessageThread.Load());
+ facebook::react::HermesRuntimeHolder::storeTo(
+ ReactPropertyBag(m_reactContext->Properties()), hermesRuntimeHolder);
+ devSettings->jsiRuntimeHolder = hermesRuntimeHolder;
break;
+ }
case JSIEngine::V8:
#if defined(USE_V8)
{
diff --git a/vnext/Microsoft.ReactNative/Views/DevMenu.cpp b/vnext/Microsoft.ReactNative/Views/DevMenu.cpp
index 98943f8b16a..d79f0f3662b 100644
--- a/vnext/Microsoft.ReactNative/Views/DevMenu.cpp
+++ b/vnext/Microsoft.ReactNative/Views/DevMenu.cpp
@@ -119,7 +119,7 @@ void DevMenuManager::CreateAndShowUI() noexcept {
std::ostringstream os;
if (Microsoft::ReactNative::HermesSamplingProfiler::IsStarted()) {
- os << "Hermes Sampling profiler is running.. !";
+ os << "Hermes Sampling profiler is running!";
} else {
os << "Click to start.";
}
@@ -210,9 +210,9 @@ void DevMenuManager::CreateAndShowUI() noexcept {
if (auto strongThis = wkThis.lock()) {
strongThis->Hide();
if (!Microsoft::ReactNative::HermesSamplingProfiler::IsStarted()) {
- Microsoft::ReactNative::HermesSamplingProfiler::Start();
+ Microsoft::ReactNative::HermesSamplingProfiler::Start(strongThis->m_context);
} else {
- auto traceFilePath = co_await Microsoft::ReactNative::HermesSamplingProfiler::Stop();
+ auto traceFilePath = co_await Microsoft::ReactNative::HermesSamplingProfiler::Stop(strongThis->m_context);
auto uiDispatcher =
React::implementation::ReactDispatcher::GetUIDispatcher(strongThis->m_context->Properties());
uiDispatcher.Post([traceFilePath]() {
diff --git a/vnext/PropertySheets/JSEngine.props b/vnext/PropertySheets/JSEngine.props
index 3418fb228c6..d62a7ac852b 100644
--- a/vnext/PropertySheets/JSEngine.props
+++ b/vnext/PropertySheets/JSEngine.props
@@ -14,7 +14,7 @@
false
- 0.0.0-2302.1001-19052299
+ 0.0.0-2302.1002-2d4bf1df
$(PkgReactNative_Hermes_Windows)
$(NuGetPackageRoot)\ReactNative.Hermes.Windows\$(HermesVersion)
false
diff --git a/vnext/Shared/HermesRuntimeHolder.cpp b/vnext/Shared/HermesRuntimeHolder.cpp
index 5496151ad6d..d37af39f65b 100644
--- a/vnext/Shared/HermesRuntimeHolder.cpp
+++ b/vnext/Shared/HermesRuntimeHolder.cpp
@@ -3,38 +3,40 @@
#include "pch.h"
-#include
-#include
+#include "HermesRuntimeHolder.h"
#include
+#include
#include
#include
#include
-#include "HermesRuntimeHolder.h"
#include "HermesShim.h"
#if defined(HERMES_ENABLE_DEBUGGER)
#include
#endif
+#include
+#include
+
using namespace facebook;
using namespace Microsoft::ReactNative;
-namespace facebook {
-namespace react {
+namespace React {
+using namespace winrt::Microsoft::ReactNative;
+}
-namespace {
+namespace facebook::react {
-std::unique_ptr makeHermesRuntimeSystraced(bool enableDefaultCrashHandler) {
- SystraceSection s("HermesExecutorFactory::makeHermesRuntimeSystraced");
- if (enableDefaultCrashHandler) {
- return HermesShim::makeHermesRuntimeWithWER();
- } else {
- auto runtimeConfig = ::hermes::vm::RuntimeConfig();
- return HermesShim::makeHermesRuntime(runtimeConfig);
- }
+React::ReactPropertyId>>
+HermesRuntimeHolderProperty() noexcept {
+ static React::ReactPropertyId>> propId{
+ L"ReactNative.HermesRuntimeHolder", L"HermesRuntimeHolder"};
+ return propId;
}
+namespace {
+
#ifdef HERMES_ENABLE_DEBUGGER
class HermesExecutorRuntimeAdapter final : public facebook::hermes::inspector::RuntimeAdapter {
public:
@@ -71,10 +73,19 @@ class HermesExecutorRuntimeAdapter final : public facebook::hermes::inspector::R
};
#endif
+std::shared_ptr makeHermesShimSystraced(bool enableDefaultCrashHandler) {
+ SystraceSection s("HermesExecutorFactory::makeHermesRuntimeSystraced");
+ if (enableDefaultCrashHandler) {
+ return HermesShim::makeWithWER();
+ } else {
+ return HermesShim::make();
+ }
+}
+
} // namespace
void HermesRuntimeHolder::crashHandler(int fileDescriptor) noexcept {
- HermesShim::hermesCrashHandler(*m_hermesRuntime, fileDescriptor);
+ m_hermesShim->dumpCrashData(fileDescriptor);
}
void HermesRuntimeHolder::teardown() noexcept {
@@ -90,15 +101,9 @@ facebook::react::JSIEngineOverride HermesRuntimeHolder::getRuntimeType() noexcep
}
std::shared_ptr HermesRuntimeHolder::getRuntime() noexcept {
- std::call_once(m_once_flag, [this]() { initRuntime(); });
-
- if (!m_hermesRuntime)
- std::terminate();
-
- // Make sure that the runtime instance is not consumed from multiple threads.
- if (m_own_thread_id != std::this_thread::get_id())
- std::terminate();
-
+ std::call_once(m_onceFlag, [this]() { initRuntime(); });
+ VerifyElseCrash(m_hermesRuntime);
+ VerifyElseCrashSz(m_ownThreadId == std::this_thread::get_id(), "Must be accessed from JS thread.");
return m_hermesRuntime;
}
@@ -109,11 +114,11 @@ HermesRuntimeHolder::HermesRuntimeHolder(
void HermesRuntimeHolder::initRuntime() noexcept {
auto devSettings = m_weakDevSettings.lock();
- if (!devSettings)
- std::terminate();
+ VerifyElseCrash(devSettings);
- m_hermesRuntime = makeHermesRuntimeSystraced(devSettings->enableDefaultCrashHandler);
- m_own_thread_id = std::this_thread::get_id();
+ m_hermesShim = makeHermesShimSystraced(devSettings->enableDefaultCrashHandler);
+ m_hermesRuntime = m_hermesShim->getRuntime();
+ m_ownThreadId = std::this_thread::get_id();
#ifdef HERMES_ENABLE_DEBUGGER
if (devSettings->useDirectDebugger) {
@@ -132,5 +137,23 @@ void HermesRuntimeHolder::initRuntime() noexcept {
errorPrototype.setProperty(*m_hermesRuntime, "jsEngine", "hermes");
}
-} // namespace react
-} // namespace facebook
+std::shared_ptr HermesRuntimeHolder::loadFrom(
+ React::ReactPropertyBag const &propertyBag) noexcept {
+ return *(propertyBag.Get(HermesRuntimeHolderProperty()));
+}
+
+void HermesRuntimeHolder::storeTo(
+ React::ReactPropertyBag const &propertyBag,
+ std::shared_ptr const &holder) noexcept {
+ propertyBag.Set(HermesRuntimeHolderProperty(), holder);
+}
+
+void HermesRuntimeHolder::addToProfiling() const noexcept {
+ m_hermesShim->addToProfiling();
+}
+
+void HermesRuntimeHolder::removeFromProfiling() const noexcept {
+ m_hermesShim->removeFromProfiling();
+}
+
+} // namespace facebook::react
diff --git a/vnext/Shared/HermesRuntimeHolder.h b/vnext/Shared/HermesRuntimeHolder.h
index a9d6befb34d..9a5b357742d 100644
--- a/vnext/Shared/HermesRuntimeHolder.h
+++ b/vnext/Shared/HermesRuntimeHolder.h
@@ -8,37 +8,52 @@
#include
#include
+#include
namespace facebook::hermes {
class HermesRuntime;
}
-namespace facebook {
-namespace react {
+namespace Microsoft::ReactNative {
+class HermesShim;
+}
+
+namespace facebook::react {
+
+class MessageQueueThread;
class HermesRuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit {
- public:
+ public: // RuntimeHolderLazyInit implementation.
std::shared_ptr getRuntime() noexcept override;
facebook::react::JSIEngineOverride getRuntimeType() noexcept override;
-
void crashHandler(int fileDescriptor) noexcept override;
-
void teardown() noexcept override;
+ public:
HermesRuntimeHolder(
std::shared_ptr devSettings,
std::shared_ptr jsQueue) noexcept;
+ static std::shared_ptr loadFrom(
+ winrt::Microsoft::ReactNative::ReactPropertyBag const &propertyBag) noexcept;
+
+ static void storeTo(
+ winrt::Microsoft::ReactNative::ReactPropertyBag const &propertyBag,
+ std::shared_ptr const &holder) noexcept;
+
+ void addToProfiling() const noexcept;
+ void removeFromProfiling() const noexcept;
+
private:
void initRuntime() noexcept;
- std::shared_ptr m_hermesRuntime;
-
- std::once_flag m_once_flag;
- std::thread::id m_own_thread_id;
+ private:
+ std::shared_ptr m_hermesShim;
+ std::shared_ptr m_hermesRuntime;
+ std::once_flag m_onceFlag{};
+ std::thread::id m_ownThreadId{};
std::weak_ptr m_weakDevSettings;
std::shared_ptr m_jsQueue;
};
-} // namespace react
-} // namespace facebook
+} // namespace facebook::react
diff --git a/vnext/Shared/HermesSamplingProfiler.cpp b/vnext/Shared/HermesSamplingProfiler.cpp
index 52f0f8128cc..8ed58e08fcf 100644
--- a/vnext/Shared/HermesSamplingProfiler.cpp
+++ b/vnext/Shared/HermesSamplingProfiler.cpp
@@ -7,13 +7,46 @@
#include
#include
+#include "HermesRuntimeHolder.h"
#include "HermesSamplingProfiler.h"
#include "HermesShim.h"
+#include "IReactDispatcher.h"
+#include "ReactPropertyBag.h"
#include "Utils.h"
+using namespace winrt::Microsoft::ReactNative;
+using namespace facebook::react;
+
namespace Microsoft::ReactNative {
namespace {
+
+// Implements an awaiter for Mso::DispatchQueue
+auto resume_in_dispatcher(const IReactDispatcher &dispatcher) noexcept {
+ struct awaitable {
+ awaitable(const IReactDispatcher &dispatcher) noexcept : dispatcher_(dispatcher) {}
+
+ bool await_ready() const noexcept {
+ return false;
+ }
+
+ void await_resume() const noexcept {}
+
+ void await_suspend(std::experimental::coroutine_handle<> resume) noexcept {
+ callback_ = [context = resume.address()]() noexcept {
+ std::experimental::coroutine_handle<>::from_address(context)();
+ };
+ dispatcher_.Post(std::move(callback_));
+ }
+
+ private:
+ IReactDispatcher dispatcher_;
+ ReactDispatcherCallback callback_;
+ };
+
+ return awaitable{dispatcher};
+}
+
std::future getTraceFilePath() noexcept {
auto hermesDataPath = co_await Microsoft::React::getApplicationDataPath(L"Hermes");
std::ostringstream os;
@@ -23,18 +56,27 @@ std::future getTraceFilePath() noexcept {
os << hermesDataPath << "\\cpu_" << now << ".cpuprofile";
co_return os.str();
}
+
} // namespace
-bool HermesSamplingProfiler::s_isStarted = false;
+std::atomic_bool HermesSamplingProfiler::s_isStarted{false};
std::string HermesSamplingProfiler::s_lastTraceFilePath;
std::string HermesSamplingProfiler::GetLastTraceFilePath() noexcept {
return s_lastTraceFilePath;
}
-winrt::fire_and_forget HermesSamplingProfiler::Start() noexcept {
- if (!s_isStarted) {
- s_isStarted = true;
+winrt::fire_and_forget HermesSamplingProfiler::Start(
+ Mso::CntPtr const &reactContext) noexcept {
+ bool expectedIsStarted = false;
+ if (s_isStarted.compare_exchange_strong(expectedIsStarted, true)) {
+ IReactDispatcher jsDispatcher = implementation::ReactDispatcher::GetJSDispatcher(reactContext->Properties());
+ ReactPropertyBag propertyBag = ReactPropertyBag(reactContext->Properties());
+
+ co_await resume_in_dispatcher(jsDispatcher);
+ std::shared_ptr hermesRuntimeHolder = HermesRuntimeHolder::loadFrom(propertyBag);
+ hermesRuntimeHolder->addToProfiling();
+
co_await winrt::resume_background();
HermesShim::enableSamplingProfiler();
}
@@ -42,20 +84,31 @@ winrt::fire_and_forget HermesSamplingProfiler::Start() noexcept {
co_return;
}
-std::future HermesSamplingProfiler::Stop() noexcept {
- if (s_isStarted) {
- s_isStarted = false;
+std::future HermesSamplingProfiler::Stop(
+ Mso::CntPtr const &reactContext) noexcept {
+ bool expectedIsStarted = true;
+ if (s_isStarted.compare_exchange_strong(expectedIsStarted, false)) {
+ IReactDispatcher jsDispatcher = implementation::ReactDispatcher::GetJSDispatcher(reactContext->Properties());
+ ReactPropertyBag propertyBag = ReactPropertyBag(reactContext->Properties());
+
co_await winrt::resume_background();
HermesShim::disableSamplingProfiler();
+
s_lastTraceFilePath = co_await getTraceFilePath();
HermesShim::dumpSampledTraceToFile(s_lastTraceFilePath);
+
+ co_await resume_in_dispatcher(jsDispatcher);
+ std::shared_ptr hermesRuntimeHolder = HermesRuntimeHolder::loadFrom(propertyBag);
+ hermesRuntimeHolder->removeFromProfiling();
+
+ co_await winrt::resume_background();
}
co_return s_lastTraceFilePath;
}
bool HermesSamplingProfiler::IsStarted() noexcept {
- return s_isStarted;
+ return s_isStarted.load();
}
} // namespace Microsoft::ReactNative
diff --git a/vnext/Shared/HermesSamplingProfiler.h b/vnext/Shared/HermesSamplingProfiler.h
index d6d06bfedaf..8b7dec02a1e 100644
--- a/vnext/Shared/HermesSamplingProfiler.h
+++ b/vnext/Shared/HermesSamplingProfiler.h
@@ -3,19 +3,21 @@
#pragma once
+#include
+#include
#include
namespace Microsoft::ReactNative {
class HermesSamplingProfiler final {
public:
- static winrt::fire_and_forget Start() noexcept;
- static std::future Stop() noexcept;
+ static winrt::fire_and_forget Start(Mso::CntPtr const &reactContext) noexcept;
+ static std::future Stop(Mso::CntPtr const &reactContext) noexcept;
static std::string GetLastTraceFilePath() noexcept;
static bool IsStarted() noexcept;
private:
- static bool s_isStarted;
+ static std::atomic_bool s_isStarted;
static std::string s_lastTraceFilePath;
};
diff --git a/vnext/Shared/HermesShim.cpp b/vnext/Shared/HermesShim.cpp
index b33709e8c32..44a50f29092 100644
--- a/vnext/Shared/HermesShim.cpp
+++ b/vnext/Shared/HermesShim.cpp
@@ -4,115 +4,119 @@
#include "HermesShim.h"
#include "Crash.h"
-namespace Microsoft::ReactNative::HermesShim {
-
-static HMODULE s_hermesModule{nullptr};
-static decltype(&facebook::hermes::makeHermesRuntime) s_makeHermesRuntime{nullptr};
-static decltype(&facebook::hermes::HermesRuntime::enableSamplingProfiler) s_enableSamplingProfiler{nullptr};
-static decltype(&facebook::hermes::HermesRuntime::disableSamplingProfiler) s_disableSamplingProfiler{nullptr};
-static decltype(&facebook::hermes::HermesRuntime::dumpSampledTraceToFile) s_dumpSampledTraceToFile{nullptr};
-static decltype(&facebook::hermes::makeHermesRuntimeWithWER) s_makeHermesRuntimeWithWER{nullptr};
-static decltype(&facebook::hermes::hermesCrashHandler) s_hermesCrashHandler{nullptr};
-
-#if _M_X64
-constexpr const char *makeHermesRuntimeSymbol =
- "?makeHermesRuntime@hermes@facebook@@YA?AV?$unique_ptr@VHermesRuntime@hermes@facebook@@U?$default_delete@VHermesRuntime@hermes@facebook@@@std@@@std@@AEBVRuntimeConfig@vm@1@@Z";
-constexpr const char *enableSamlingProfilerSymbol = "?enableSamplingProfiler@HermesRuntime@hermes@facebook@@SAXXZ";
-constexpr const char *disableSamlingProfilerSymbol = "?disableSamplingProfiler@HermesRuntime@hermes@facebook@@SAXXZ";
-constexpr const char *dumpSampledTraceToFileSymbol =
- "?dumpSampledTraceToFile@HermesRuntime@hermes@facebook@@SAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z";
-constexpr const char *makeHermesRuntimeWithWERSymbol =
- "?makeHermesRuntimeWithWER@hermes@facebook@@YA?AV?$unique_ptr@VHermesRuntime@hermes@facebook@@U?$default_delete@VHermesRuntime@hermes@facebook@@@std@@@std@@XZ";
-constexpr const char *hermesCrashHandlerSymbol = "?hermesCrashHandler@hermes@facebook@@YAXAEAVHermesRuntime@12@H@Z";
-#endif
-
-#if _M_ARM64
-constexpr const char *makeHermesRuntimeSymbol =
- "?makeHermesRuntime@hermes@facebook@@YA?AV?$unique_ptr@VHermesRuntime@hermes@facebook@@U?$default_delete@VHermesRuntime@hermes@facebook@@@std@@@std@@AEBVRuntimeConfig@vm@1@@Z";
-constexpr const char *enableSamlingProfilerSymbol = "?enableSamplingProfiler@HermesRuntime@hermes@facebook@@SAXXZ";
-constexpr const char *disableSamlingProfilerSymbol = "?disableSamplingProfiler@HermesRuntime@hermes@facebook@@SAXXZ";
-constexpr const char *dumpSampledTraceToFileSymbol =
- "?dumpSampledTraceToFile@HermesRuntime@hermes@facebook@@SAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z";
-constexpr const char *makeHermesRuntimeWithWERSymbol =
- "?makeHermesRuntimeWithWER@hermes@facebook@@YA?AV?$unique_ptr@VHermesRuntime@hermes@facebook@@U?$default_delete@VHermesRuntime@hermes@facebook@@@std@@@std@@XZ";
-constexpr const char *hermesCrashHandlerSymbol = "?hermesCrashHandler@hermes@facebook@@YAXAEAVHermesRuntime@12@H@Z";
-#endif
-
-#if _M_IX86
-constexpr const char *makeHermesRuntimeSymbol =
- "?makeHermesRuntime@hermes@facebook@@YA?AV?$unique_ptr@VHermesRuntime@hermes@facebook@@U?$default_delete@VHermesRuntime@hermes@facebook@@@std@@@std@@ABVRuntimeConfig@vm@1@@Z";
-constexpr const char *enableSamlingProfilerSymbol = "?enableSamplingProfiler@HermesRuntime@hermes@facebook@@SAXXZ";
-constexpr const char *disableSamlingProfilerSymbol = "?disableSamplingProfiler@HermesRuntime@hermes@facebook@@SAXXZ";
-constexpr const char *dumpSampledTraceToFileSymbol =
- "?dumpSampledTraceToFile@HermesRuntime@hermes@facebook@@SAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z";
-constexpr const char *makeHermesRuntimeWithWERSymbol =
- "?makeHermesRuntimeWithWER@hermes@facebook@@YA?AV?$unique_ptr@VHermesRuntime@hermes@facebook@@U?$default_delete@VHermesRuntime@hermes@facebook@@@std@@@std@@XZ";
-constexpr const char *hermesCrashHandlerSymbol = "?hermesCrashHandler@hermes@facebook@@YAXAAVHermesRuntime@12@H@Z";
-#endif
-
-static std::once_flag s_hermesLoading;
-
-static void EnsureHermesLoaded() noexcept {
+#define DECLARE_SYMBOL(symbol, importedSymbol) \
+ decltype(&importedSymbol) s_##symbol{}; \
+ constexpr const char symbol##Symbol[] = #importedSymbol;
+
+#define INIT_SYMBOL(symbol) \
+ s_##symbol = reinterpret_cast(GetProcAddress(s_hermesModule, symbol##Symbol)); \
+ VerifyElseCrash(s_##symbol);
+
+#define CRASH_ON_ERROR(result) VerifyElseCrash(result == hermes_ok);
+
+namespace Microsoft::ReactNative {
+
+namespace {
+
+DECLARE_SYMBOL(createRuntime, hermes_create_runtime);
+DECLARE_SYMBOL(createRuntimeWithWER, hermes_create_runtime_with_wer);
+DECLARE_SYMBOL(deleteRuntime, hermes_delete_runtime);
+DECLARE_SYMBOL(getNonAbiSafeRuntime, hermes_get_non_abi_safe_runtime);
+DECLARE_SYMBOL(dumpCrashData, hermes_dump_crash_data);
+DECLARE_SYMBOL(samplingProfilerEnable, hermes_sampling_profiler_enable);
+DECLARE_SYMBOL(samplingProfilerDisable, hermes_sampling_profiler_disable);
+DECLARE_SYMBOL(samplingProfilerAdd, hermes_sampling_profiler_add);
+DECLARE_SYMBOL(samplingProfilerRemove, hermes_sampling_profiler_remove);
+DECLARE_SYMBOL(samplingProfilerDumpToFile, hermes_sampling_profiler_dump_to_file);
+
+HMODULE s_hermesModule{};
+std::once_flag s_hermesLoading;
+
+void EnsureHermesLoaded() noexcept {
std::call_once(s_hermesLoading, []() {
VerifyElseCrashSz(!s_hermesModule, "Invalid state: \"hermes.dll\" being loaded again.");
s_hermesModule = LoadLibrary(L"hermes.dll");
VerifyElseCrashSz(s_hermesModule, "Could not load \"hermes.dll\"");
- s_makeHermesRuntime =
- reinterpret_cast(GetProcAddress(s_hermesModule, makeHermesRuntimeSymbol));
- VerifyElseCrash(s_makeHermesRuntime);
+ INIT_SYMBOL(createRuntime);
+ INIT_SYMBOL(createRuntimeWithWER);
+ INIT_SYMBOL(deleteRuntime);
+ INIT_SYMBOL(getNonAbiSafeRuntime);
+ INIT_SYMBOL(dumpCrashData);
+ INIT_SYMBOL(samplingProfilerEnable);
+ INIT_SYMBOL(samplingProfilerDisable);
+ INIT_SYMBOL(samplingProfilerAdd);
+ INIT_SYMBOL(samplingProfilerRemove);
+ INIT_SYMBOL(samplingProfilerDumpToFile);
+ });
+}
+
+struct RuntimeDeleter {
+ RuntimeDeleter(std::shared_ptr &&hermesShimPtr) noexcept
+ : hermesShimPtr_(std::move(hermesShimPtr)) {}
- s_enableSamplingProfiler = reinterpret_cast(
- GetProcAddress(s_hermesModule, enableSamlingProfilerSymbol));
- VerifyElseCrash(s_enableSamplingProfiler);
+ void operator()(facebook::hermes::HermesRuntime * /*runtime*/) {
+ // Do nothing. Instead, we rely on the RuntimeDeleter destructor.
+ }
- s_disableSamplingProfiler = reinterpret_cast(
- GetProcAddress(s_hermesModule, disableSamlingProfilerSymbol));
- VerifyElseCrash(s_disableSamplingProfiler);
+ private:
+ std::shared_ptr hermesShimPtr_;
+};
- s_dumpSampledTraceToFile = reinterpret_cast(
- GetProcAddress(s_hermesModule, dumpSampledTraceToFileSymbol));
- VerifyElseCrash(s_dumpSampledTraceToFile);
+} // namespace
- s_makeHermesRuntimeWithWER = reinterpret_cast(
- GetProcAddress(s_hermesModule, makeHermesRuntimeWithWERSymbol));
- VerifyElseCrash(s_makeHermesRuntimeWithWER);
+HermesShim::HermesShim(hermes_runtime runtime) noexcept : runtime_(runtime) {
+ CRASH_ON_ERROR(s_getNonAbiSafeRuntime(runtime_, reinterpret_cast(&nonAbiSafeRuntime_)));
+}
- s_hermesCrashHandler =
- reinterpret_cast(GetProcAddress(s_hermesModule, hermesCrashHandlerSymbol));
- VerifyElseCrash(s_hermesCrashHandler);
- });
+HermesShim::~HermesShim() {
+ CRASH_ON_ERROR(s_deleteRuntime(runtime_));
}
-std::unique_ptr makeHermesRuntime(const hermes::vm::RuntimeConfig &runtimeConfig) {
+/*static*/ std::shared_ptr HermesShim::make() noexcept {
EnsureHermesLoaded();
- return s_makeHermesRuntime(runtimeConfig);
+ hermes_runtime runtime{};
+ CRASH_ON_ERROR(s_createRuntime(&runtime));
+ return std::make_shared(runtime);
}
-void enableSamplingProfiler() {
+/*static*/
+std::shared_ptr HermesShim::makeWithWER() noexcept {
EnsureHermesLoaded();
- s_enableSamplingProfiler();
+ hermes_runtime runtime{};
+ CRASH_ON_ERROR(s_createRuntimeWithWER(&runtime));
+ return std::make_shared(runtime);
}
-void disableSamplingProfiler() {
- EnsureHermesLoaded();
- s_disableSamplingProfiler();
+std::shared_ptr HermesShim::getRuntime() const noexcept {
+ return std::shared_ptr(nonAbiSafeRuntime_, RuntimeDeleter(shared_from_this()));
}
-void dumpSampledTraceToFile(const std::string &fileName) {
+void HermesShim::dumpCrashData(int fileDescriptor) const noexcept {
+ CRASH_ON_ERROR(s_dumpCrashData(runtime_, fileDescriptor));
+}
+
+/*static*/ void HermesShim::enableSamplingProfiler() noexcept {
EnsureHermesLoaded();
- s_dumpSampledTraceToFile(fileName);
+ CRASH_ON_ERROR(s_samplingProfilerEnable());
}
-std::unique_ptr makeHermesRuntimeWithWER() {
+/*static*/ void HermesShim::disableSamplingProfiler() noexcept {
EnsureHermesLoaded();
- return s_makeHermesRuntimeWithWER();
+ CRASH_ON_ERROR(s_samplingProfilerDisable());
}
-void hermesCrashHandler(facebook::hermes::HermesRuntime &runtime, int fileDescriptor) {
+/*static*/ void HermesShim::dumpSampledTraceToFile(const std::string &fileName) noexcept {
EnsureHermesLoaded();
- s_hermesCrashHandler(runtime, fileDescriptor);
+ CRASH_ON_ERROR(s_samplingProfilerDumpToFile(fileName.c_str()));
+}
+void HermesShim::addToProfiling() const noexcept {
+ CRASH_ON_ERROR(s_samplingProfilerAdd(runtime_));
+}
+
+void HermesShim::removeFromProfiling() const noexcept {
+ CRASH_ON_ERROR(s_samplingProfilerRemove(runtime_));
}
-} // namespace Microsoft::ReactNative::HermesShim
+} // namespace Microsoft::ReactNative
diff --git a/vnext/Shared/HermesShim.h b/vnext/Shared/HermesShim.h
index 2bd285024b4..04ca1ef72ae 100644
--- a/vnext/Shared/HermesShim.h
+++ b/vnext/Shared/HermesShim.h
@@ -9,13 +9,33 @@
//! use pure delay-loading to achieve this, since WACK will detect the
//! non-present DLL. Functions in this namespace shim to the Hermes DLL via
//! GetProcAddress.
-namespace Microsoft::ReactNative::HermesShim {
+namespace Microsoft::ReactNative {
-std::unique_ptr makeHermesRuntime(const hermes::vm::RuntimeConfig &runtimeConfig);
-void enableSamplingProfiler();
-void disableSamplingProfiler();
-void dumpSampledTraceToFile(const std::string &fileName);
-std::unique_ptr makeHermesRuntimeWithWER();
-void hermesCrashHandler(facebook::hermes::HermesRuntime &runtime, int fileDescriptor);
+class HermesShim : public std::enable_shared_from_this {
+ public:
+ HermesShim(hermes_runtime runtimeAbiPtr) noexcept;
+ ~HermesShim();
-} // namespace Microsoft::ReactNative::HermesShim
+ static std::shared_ptr make() noexcept;
+ static std::shared_ptr makeWithWER() noexcept;
+
+ std::shared_ptr getRuntime() const noexcept;
+
+ void dumpCrashData(int fileDescriptor) const noexcept;
+
+ static void enableSamplingProfiler() noexcept;
+ static void disableSamplingProfiler() noexcept;
+ static void dumpSampledTraceToFile(const std::string &fileName) noexcept;
+ void addToProfiling() const noexcept;
+ void removeFromProfiling() const noexcept;
+
+ HermesShim(const HermesShim &) = delete;
+ HermesShim &operator=(const HermesShim &) = delete;
+
+ private:
+ // It must be a raw pointer to avoid circular reference.
+ facebook::hermes::HermesRuntime *nonAbiSafeRuntime_{};
+ hermes_runtime runtime_{};
+};
+
+} // namespace Microsoft::ReactNative
diff --git a/vnext/Shared/JSI/RuntimeHolder.h b/vnext/Shared/JSI/RuntimeHolder.h
index b3642fcfabe..69a95b5a1cd 100644
--- a/vnext/Shared/JSI/RuntimeHolder.h
+++ b/vnext/Shared/JSI/RuntimeHolder.h
@@ -11,7 +11,7 @@ namespace Microsoft::JSI {
// a. lazily create a JSI Runtime on the first call to getRuntime
// b. subsequent calls to getRuntime should return the Runtime created in (a)
-// Note :: All calls to getRuntime() should happen on the same thread unless you are sure that
+// Note: all calls to getRuntime() should happen on the same thread unless you are sure that
// the underlying Runtime instance is thread safe.
struct RuntimeHolderLazyInit {
@@ -21,7 +21,7 @@ struct RuntimeHolderLazyInit {
virtual void teardown() noexcept {};
// You can call this when a crash happens to attempt recording additional data
- // The fd supplied is a raw file stream an implementation might write JSON to
+ // The fileDescriptor supplied is a raw file stream an implementation might write JSON to.
virtual void crashHandler(int fileDescriptor) noexcept {};
};