From f81e14c58575a4c69cc70bf859e4d2d9d434abe6 Mon Sep 17 00:00:00 2001
From: Vladimir Morozov <vmorozov@microsoft.com>
Date: Mon, 4 Jan 2021 14:14:33 -0800
Subject: [PATCH] Enable support for C++ TurboModules (#6804)

* Enable support for C++ TurboModules

* Change files

* Fix compilation issues

* Attempt to fix the CLI test for Nuget with binaries

* Address PR feedback

* Format code

* Address PR feedback

* Throw an exception in AbiCallInvoker::invokeSync as in Instance::JSCallInvoker::invokeSync

* Format code
# Conflicts:
#	vnext/JSI/Universal/ChakraJsiRuntime_edgemode.cpp
---
 ...-ac778b2a-4dd4-46a3-ab77-fd7c7e5fb99f.json |   7 +
 vnext/Desktop/React.Windows.Desktop.vcxproj   |   6 +
 .../Universal/ChakraJsiRuntime_edgemode.cpp   |  24 +-
 ...icrosoft.ReactNative.Cxx.UnitTests.vcxproj |   4 +
 .../ReactModuleBuilderMock.cpp                |  19 +
 .../JSI/{JsiApi.h => JsiApiContext.cpp}       |  24 +-
 .../JSI/JsiApiContext.h                       |  35 ++
 .../Microsoft.ReactNative.Cxx.vcxitems        |  45 +-
 ...Microsoft.ReactNative.Cxx.vcxitems.filters |  36 +-
 .../ReactCommon/TurboModuleUtils.cpp          | 100 +++
 .../ReactCommon/TurboModuleUtils.h            |  83 +++
 .../ReactPropertyBag.h                        |   1 +
 .../TurboModuleProvider.cpp                   |  32 +
 .../TurboModuleProvider.h                     |  35 ++
 .../JsiTurboModuleTests.cpp                   | 420 +++++++++++++
 .../JsiTurboModuleTests.js                    |  38 ++
 ...osoft.ReactNative.IntegrationTests.vcxproj |   3 +
 ...actNative.IntegrationTests.vcxproj.filters |  30 +
 .../ReactNativeHostTests.cpp                  |   2 +-
 vnext/Microsoft.ReactNative/IReactContext.cpp |   2 +-
 .../IReactModuleBuilder.idl                   |  39 +-
 .../IReactPackageBuilder.idl                  |  20 +-
 vnext/Microsoft.ReactNative/JsiApi.cpp        | 572 ++++++++++--------
 vnext/Microsoft.ReactNative/JsiApi.h          |  31 +-
 vnext/Microsoft.ReactNative/ReactHost/React.h |   5 +
 .../TurboModulesProvider.cpp                  |  22 +
 vnext/Scripts/Tfs/Layout-Headers.ps1          |   7 +
 27 files changed, 1330 insertions(+), 312 deletions(-)
 create mode 100644 change/react-native-windows-ac778b2a-4dd4-46a3-ab77-fd7c7e5fb99f.json
 rename vnext/Microsoft.ReactNative.Cxx/JSI/{JsiApi.h => JsiApiContext.cpp} (75%)
 create mode 100644 vnext/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.h
 create mode 100644 vnext/Microsoft.ReactNative.Cxx/ReactCommon/TurboModuleUtils.cpp
 create mode 100644 vnext/Microsoft.ReactNative.Cxx/ReactCommon/TurboModuleUtils.h
 create mode 100644 vnext/Microsoft.ReactNative.Cxx/TurboModuleProvider.cpp
 create mode 100644 vnext/Microsoft.ReactNative.Cxx/TurboModuleProvider.h
 create mode 100644 vnext/Microsoft.ReactNative.IntegrationTests/JsiTurboModuleTests.cpp
 create mode 100644 vnext/Microsoft.ReactNative.IntegrationTests/JsiTurboModuleTests.js
 create mode 100644 vnext/Microsoft.ReactNative.IntegrationTests/Microsoft.ReactNative.IntegrationTests.vcxproj.filters

diff --git a/change/react-native-windows-ac778b2a-4dd4-46a3-ab77-fd7c7e5fb99f.json b/change/react-native-windows-ac778b2a-4dd4-46a3-ab77-fd7c7e5fb99f.json
new file mode 100644
index 00000000000..169e55f5c5f
--- /dev/null
+++ b/change/react-native-windows-ac778b2a-4dd4-46a3-ab77-fd7c7e5fb99f.json
@@ -0,0 +1,7 @@
+{
+  "type": "prerelease",
+  "comment": "Enable support for C++ TurboModules",
+  "packageName": "react-native-windows",
+  "email": "vmorozov@microsoft.com",
+  "dependentChangeType": "patch"
+}
diff --git a/vnext/Desktop/React.Windows.Desktop.vcxproj b/vnext/Desktop/React.Windows.Desktop.vcxproj
index d62fa494a19..ed2bc898c6e 100644
--- a/vnext/Desktop/React.Windows.Desktop.vcxproj
+++ b/vnext/Desktop/React.Windows.Desktop.vcxproj
@@ -194,6 +194,9 @@
     <ClCompile Include="..\Microsoft.ReactNative\IReactPropertyBag.cpp">
       <DependentUpon>..\Microsoft.ReactNative\IReactPropertyBag.idl</DependentUpon>
     </ClCompile>
+    <ClCompile Include="..\Microsoft.ReactNative\JsiApi.cpp">
+      <DependentUpon>..\Microsoft.ReactNative\JsiApi.idl</DependentUpon>
+    </ClCompile>
     <ClCompile Include="..\Microsoft.ReactNative\JsiReader.cpp">
       <DependentUpon>..\Microsoft.ReactNative\IJSValueReader.idl</DependentUpon>
     </ClCompile>
@@ -288,6 +291,9 @@
       <DependentUpon>..\Microsoft.ReactNative\IReactPropertyBag.idl</DependentUpon>
       <SubType>Code</SubType>
     </ClInclude>
+    <ClInclude Include="..\Microsoft.ReactNative\JsiApi.h">
+      <DependentUpon>..\Microsoft.ReactNative\JsiApi.idl</DependentUpon>
+    </ClInclude>
     <ClInclude Include="..\Microsoft.ReactNative\RedBoxErrorFrameInfo.h" />
     <ClInclude Include="..\Microsoft.ReactNative\RedBoxErrorInfo.h" />
     <ClInclude Include="JSBigStringResourceDll.h" />
diff --git a/vnext/JSI/Universal/ChakraJsiRuntime_edgemode.cpp b/vnext/JSI/Universal/ChakraJsiRuntime_edgemode.cpp
index a89eee19acb..919a474a26b 100644
--- a/vnext/JSI/Universal/ChakraJsiRuntime_edgemode.cpp
+++ b/vnext/JSI/Universal/ChakraJsiRuntime_edgemode.cpp
@@ -1,9 +1,9 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-#include "ChakraRuntime.h"
-
+#include <cxxreact/MessageQueueThread.h>
 #include "ByteArrayBuffer.h"
+#include "ChakraRuntime.h"
 #include "Unicode.h"
 
 #if !defined(CHAKRACORE)
@@ -12,7 +12,25 @@
 namespace Microsoft::JSI {
 
 void ChakraRuntime::setupNativePromiseContinuation() noexcept {
-  // NOP
+  JsSetPromiseContinuationCallback(PromiseContinuationCallback, this);
+}
+
+// ES6 Promise callback
+void CALLBACK ChakraRuntime::PromiseContinuationCallback(JsValueRef funcRef, void *callbackState) noexcept {
+  ChakraRuntime *runtime = static_cast<ChakraRuntime *>(callbackState);
+  runtime->PromiseContinuation(funcRef);
+}
+
+void ChakraRuntime::PromiseContinuation(JsValueRef funcRef) noexcept {
+  if (runtimeArgs().jsQueue) {
+    JsAddRef(funcRef, nullptr);
+    runtimeArgs().jsQueue->runOnQueue([this, funcRef]() {
+      JsValueRef undefinedValue;
+      JsGetUndefinedValue(&undefinedValue);
+      ChakraVerifyJsErrorElseThrow(JsCallFunction(funcRef, &undefinedValue, 1, nullptr));
+      JsRelease(funcRef, nullptr);
+    });
+  }
 }
 
 void ChakraRuntime::startDebuggingIfNeeded() {
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj
index fb8002d95a9..bced92d7c41 100644
--- a/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/Microsoft.ReactNative.Cxx.UnitTests.vcxproj
@@ -144,10 +144,14 @@
     <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IReactNonAbiValue.idl" />
     <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IReactNotificationService.idl" />
     <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IReactPackageBuilder.idl" />
+    <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IReactPackageProvider.idl" />
     <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IReactPropertyBag.idl" />
     <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IViewManager.idl" />
     <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\IViewManagerCore.idl" />
     <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\JsiApi.idl" />
+    <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\ReactInstanceSettings.idl" />
+    <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\ReactNativeHost.idl" />
+    <Midl Include="$(ReactNativeWindowsDir)Microsoft.ReactNative\RedBoxHandler.idl" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.cpp b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.cpp
index c1ac6e2bb06..4a96beace57 100644
--- a/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.cpp
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/ReactModuleBuilderMock.cpp
@@ -160,4 +160,23 @@ void ReactContextMock::EmitJSEvent(
   m_builderMock->EmitJSEvent(eventEmitterName, eventName, paramsArgWriter);
 }
 
+Microsoft::ReactNative::IReactPropertyNamespace ReactPropertyBagHelper::GlobalNamespace() {
+  VerifyElseCrashSz(false, "Not implemented");
+}
+
+Microsoft::ReactNative::IReactPropertyNamespace ReactPropertyBagHelper::GetNamespace(
+    param::hstring const & /*namespaceName*/) {
+  VerifyElseCrashSz(false, "Not implemented");
+}
+
+Microsoft::ReactNative::IReactPropertyName ReactPropertyBagHelper::GetName(
+    Microsoft::ReactNative::IReactPropertyNamespace const & /*ns*/,
+    param::hstring const & /*localName*/) {
+  VerifyElseCrashSz(false, "Not implemented");
+}
+
+Microsoft::ReactNative::IReactPropertyBag ReactPropertyBagHelper::CreatePropertyBag() {
+  VerifyElseCrashSz(false, "Not implemented");
+}
+
 } // namespace winrt::Microsoft::ReactNative
diff --git a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiApi.h b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.cpp
similarity index 75%
rename from vnext/Microsoft.ReactNative.Cxx/JSI/JsiApi.h
rename to vnext/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.cpp
index 2cbe0f6be06..d7019c73e8a 100644
--- a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiApi.h
+++ b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.cpp
@@ -1,11 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-#pragma once
-#ifndef MICROSOFT_REACTNATIVE_JSI_JSIAPI
-#define MICROSOFT_REACTNATIVE_JSI_JSIAPI
-#include "../ReactContext.h"
-#include "JsiAbiApi.h"
+#include "pch.h"
+#include "JsiApiContext.h"
 
 // Use __ImageBase to get current DLL handle.
 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
@@ -58,21 +55,4 @@ facebook::jsi::Runtime &GetOrCreateContextRuntime(ReactContext const &context) n
   return *runtime;
 }
 
-// Call provided lambda with the facebook::jsi::Runtime& parameter.
-// For example: ExecuteJsi(context, [](facebook::jsi::Runtime& runtime){...})
-// The code is executed synchronously if it is already in JSDispatcher, or asynchronously otherwise.
-template <class TCodeWithRuntime>
-void ExecuteJsi(ReactContext const &context, TCodeWithRuntime const &code) {
-  ReactDispatcher jsDispatcher = context.JSDispatcher();
-  if (jsDispatcher.HasThreadAccess()) {
-    // Execute immediately if we are in JS thread.
-    code(GetOrCreateContextRuntime(context));
-  } else {
-    // Otherwise, schedule work in JS thread.
-    jsDispatcher.Post([ context, code ]() noexcept { code(GetOrCreateContextRuntime(context)); });
-  }
-}
-
 } // namespace winrt::Microsoft::ReactNative
-
-#endif // MICROSOFT_REACTNATIVE_JSI_JSIAPI
diff --git a/vnext/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.h b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.h
new file mode 100644
index 00000000000..1b39517f368
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx/JSI/JsiApiContext.h
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#pragma once
+#ifndef MICROSOFT_REACTNATIVE_JSI_JSIAPI
+#define MICROSOFT_REACTNATIVE_JSI_JSIAPI
+
+#include "../ReactContext.h"
+#include "JsiAbiApi.h"
+
+namespace winrt::Microsoft::ReactNative {
+
+// Get JSI Runtime from the current JS dispatcher thread.
+// If it is not found, then create it and store it in the context.Properties().
+// Make sure that the JSI runtime holder is removed when the instance is unloaded.
+facebook::jsi::Runtime &GetOrCreateContextRuntime(ReactContext const &context) noexcept;
+
+// Call provided lambda with the facebook::jsi::Runtime& parameter.
+// For example: ExecuteJsi(context, [](facebook::jsi::Runtime& runtime){...})
+// The code is executed synchronously if it is already in JSDispatcher, or asynchronously otherwise.
+template <class TCodeWithRuntime>
+void ExecuteJsi(ReactContext const &context, TCodeWithRuntime const &code) {
+  ReactDispatcher jsDispatcher = context.JSDispatcher();
+  if (jsDispatcher.HasThreadAccess()) {
+    // Execute immediately if we are in JS thread.
+    code(GetOrCreateContextRuntime(context));
+  } else {
+    // Otherwise, schedule work in JS thread.
+    jsDispatcher.Post([ context, code ]() noexcept { code(GetOrCreateContextRuntime(context)); });
+  }
+}
+
+} // namespace winrt::Microsoft::ReactNative
+
+#endif // MICROSOFT_REACTNATIVE_JSI_JSIAPI
diff --git a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems
index b3a6f45a384..743f14f92df 100644
--- a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems
+++ b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems
@@ -4,14 +4,24 @@
     <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
     <HasSharedItems>true</HasSharedItems>
     <ItemsProjectGuid>{da8b35b3-da00-4b02-bde6-6a397b3fd46b}</ItemsProjectGuid>
-    <ReactNativeDir Condition="'$(ReactNativeDir)' == '' AND Exists('$(MSBuildThisFileDirectory)\..\..\node_modules\react-native\package.json')">$(MSBuildThisFileDirectory)\..\..\node_modules\react-native</ReactNativeDir>
-    <ReactNativeDir Condition="'$(ReactNativeDir)' == '' AND Exists('$(MSBuildThisFileDirectory)\..\..\..\node_modules\react-native\package.json')">$(MSBuildThisFileDirectory)\..\..\..\node_modules\react-native</ReactNativeDir>
+    <ReactNativeDir Condition="'$(ReactNativeDir)' == '' AND Exists('$(MSBuildThisFileDirectory)..\..\node_modules\react-native\package.json')">$(MSBuildThisFileDirectory)..\..\node_modules\react-native</ReactNativeDir>
+    <ReactNativeDir Condition="'$(ReactNativeDir)' == '' AND Exists('$(MSBuildThisFileDirectory)..\..\..\node_modules\react-native\package.json')">$(MSBuildThisFileDirectory)..\..\..\node_modules\react-native</ReactNativeDir>
     <JSI_SourcePath Condition="'$(JSI_SourcePath)' == '' AND '$(ReactNativeDir)' != ''">$(ReactNativeDir)\ReactCommon\jsi</JSI_SourcePath>
-    <JSI_SourcePath Condition="'$(JSI_SourcePath)' == '' AND Exists('$(MSBuildThisFileDirectory)\jsi\jsi.h')">$(MSBuildThisFileDirectory)</JSI_SourcePath>
+    <JSI_SourcePath Condition="'$(JSI_SourcePath)' == '' AND Exists('$(MSBuildThisFileDirectory)jsi\jsi.h')">$(MSBuildThisFileDirectory)</JSI_SourcePath>
+    <CallInvoker_SourcePath Condition="'$(CallInvoker_SourcePath)' == '' AND '$(ReactNativeDir)' != ''">$(ReactNativeDir)\ReactCommon\callinvoker</CallInvoker_SourcePath>
+    <CallInvoker_SourcePath Condition="'$(CallInvoker_SourcePath)' == '' AND Exists('$(MSBuildThisFileDirectory)ReactCommon\CallInvoker.h')">$(MSBuildThisFileDirectory)</CallInvoker_SourcePath>
+    <TurboModule_SourcePath Condition="'$(TurboModule_SourcePath)' == '' AND '$(ReactNativeDir)' != ''">$(ReactNativeDir)\ReactCommon\react\nativemodule\core</TurboModule_SourcePath>
+    <TurboModule_SourcePath Condition="'$(TurboModule_SourcePath)' == '' AND Exists('$(MSBuildThisFileDirectory)ReactCommon\TurboModule.h')">$(MSBuildThisFileDirectory)</TurboModule_SourcePath>
   </PropertyGroup>
   <ItemDefinitionGroup>
     <ClCompile>
-      <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory);$(JSI_SourcePath);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>
+        $(MSBuildThisFileDirectory);
+        $(JSI_SourcePath);
+        $(CallInvoker_SourcePath);
+        $(TurboModule_SourcePath);
+        %(AdditionalIncludeDirectories)
+      </AdditionalIncludeDirectories>
     </ClCompile>
     <Midl>
       <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -24,10 +34,16 @@
     <ClInclude Include="$(JSI_SourcePath)\jsi\instrumentation.h" />
     <ClInclude Include="$(JSI_SourcePath)\jsi\jsi-inl.h" />
     <ClInclude Include="$(JSI_SourcePath)\jsi\jsi.h" />
+    <ClInclude Include="$(MSBuildThisFileDirectory)TurboModuleProvider.h" />
+    <ClInclude Include="$(CallInvoker_SourcePath)\ReactCommon\CallInvoker.h" />
+    <ClInclude Include="$(TurboModule_SourcePath)\ReactCommon\LongLivedObject.h" />
+    <ClInclude Include="$(TurboModule_SourcePath)\ReactCommon\TurboModule.h" />
+    <!-- TODO: change TurboModuleUtils.h to use the $(TurboModule_SourcePath) after we fix and 'de-fork' it. -->
+    <ClInclude Include="$(MSBuildThisFileDirectory)ReactCommon\TurboModuleUtils.h" />
     <ClInclude Include="$(MSBuildThisFileDirectory)CppWinRTIncludes.h" />
     <ClInclude Include="$(MSBuildThisFileDirectory)Crash.h" />
     <ClInclude Include="$(MSBuildThisFileDirectory)JSI\JsiAbiApi.h" />
-    <ClInclude Include="$(MSBuildThisFileDirectory)JSI\JsiApi.h" />
+    <ClInclude Include="$(MSBuildThisFileDirectory)JSI\JsiApiContext.h" />
     <ClInclude Include="$(MSBuildThisFileDirectory)ReactHandleHelper.h" />
     <ClInclude Include="$(MSBuildThisFileDirectory)JSValue.h" />
     <ClInclude Include="$(MSBuildThisFileDirectory)JSValueReader.h" />
@@ -71,11 +87,28 @@
     <ClCompile Include="$(JSI_SourcePath)\jsi\jsi.cpp">
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
     </ClCompile>
+    <ClCompile Include="$(MSBuildThisFileDirectory)JSI\JsiAbiApi.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)JSI\JsiApiContext.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)JSValue.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)JSValueTreeReader.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)JSValueTreeWriter.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)ModuleRegistration.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)ReactPromise.cpp" />
-    <ClCompile Include="$(MSBuildThisFileDirectory)JSI\JsiAbiApi.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)TurboModuleProvider.cpp" />
+    <ClCompile Include="$(TurboModule_SourcePath)\ReactCommon\LongLivedObject.cpp">
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="$(TurboModule_SourcePath)\ReactCommon\TurboModule.cpp">
+      <DisableSpecificWarnings>4100;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <!-- TODO: change TurboModuleUtils.cpp to use the $(TurboModule_SourcePath) after we fix and 'de-fork' TurboModuleUtils.h. -->
+    <ClCompile Include="$(MSBuildThisFileDirectory)ReactCommon\TurboModuleUtils.cpp">
+      <DisableSpecificWarnings>4100;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="$(MSBuildThisFileDirectory)README.md" />
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters
index b5d8704fdfd..80b0ea06560 100644
--- a/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters
+++ b/vnext/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters
@@ -12,6 +12,21 @@
     <ClCompile Include="$(MSBuildThisFileDirectory)JSI\JsiAbiApi.cpp">
       <Filter>JSI</Filter>
     </ClCompile>
+    <ClCompile Include="$(MSBuildThisFileDirectory)ReactCommon\TurboModuleUtils.cpp">
+      <Filter>TurboModule</Filter>
+    </ClCompile>
+    <ClCompile Include="$(MSBuildThisFileDirectory)TurboModuleProvider.cpp">
+      <Filter>TurboModule</Filter>
+    </ClCompile>
+    <ClCompile Include="$(TurboModule_SourcePath)\ReactCommon\TurboModule.cpp">
+      <Filter>TurboModule</Filter>
+    </ClCompile>
+    <ClCompile Include="$(TurboModule_SourcePath)\ReactCommon\LongLivedObject.cpp">
+      <Filter>TurboModule</Filter>
+    </ClCompile>
+    <ClCompile Include="$(MSBuildThisFileDirectory)JSI\JsiApiContext.cpp">
+      <Filter>JSI</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(MSBuildThisFileDirectory)Crash.h" />
@@ -112,9 +127,22 @@
     <ClInclude Include="$(JSI_SourcePath)\jsi\instrumentation.h">
       <Filter>JSI</Filter>
     </ClInclude>
-    <ClInclude Include="$(MSBuildThisFileDirectory)JSI\JsiApi.h">
+    <ClInclude Include="$(MSBuildThisFileDirectory)TurboModuleProvider.h">
+      <Filter>TurboModule</Filter>
+    </ClInclude>
+    <ClInclude Include="$(MSBuildThisFileDirectory)ReactCommon\TurboModuleUtils.h">
+      <Filter>TurboModule</Filter>
+    </ClInclude>
+    <ClInclude Include="$(MSBuildThisFileDirectory)JSI\JsiApiContext.h">
       <Filter>JSI</Filter>
     </ClInclude>
+    <ClInclude Include="$(CallInvoker_SourcePath)\ReactCommon\CallInvoker.h" />
+    <ClInclude Include="$(TurboModule_SourcePath)\ReactCommon\TurboModule.h">
+      <Filter>TurboModule</Filter>
+    </ClInclude>
+    <ClInclude Include="$(TurboModule_SourcePath)\ReactCommon\LongLivedObject.h">
+      <Filter>TurboModule</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="JSI">
@@ -123,5 +151,11 @@
     <Filter Include="UI">
       <UniqueIdentifier>{acac5f8a-4b6a-4139-9b74-6e3c36aceb20}</UniqueIdentifier>
     </Filter>
+    <Filter Include="TurboModule">
+      <UniqueIdentifier>{b5c0294c-d72f-44fa-8509-369f7d3e4a56}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="$(MSBuildThisFileDirectory)README.md" />
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative.Cxx/ReactCommon/TurboModuleUtils.cpp b/vnext/Microsoft.ReactNative.Cxx/ReactCommon/TurboModuleUtils.cpp
new file mode 100644
index 00000000000..ff51b1a1934
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx/ReactCommon/TurboModuleUtils.cpp
@@ -0,0 +1,100 @@
+// This file is a copy from react-native NPM package to avoid dependency on folly.
+// It must be removed after the TurboModuleUtils.h file is fixed in react-native repo:
+// https://github.com/facebook/react-native/pull/30672
+
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#include "TurboModuleUtils.h"
+
+namespace facebook {
+namespace react {
+
+static jsi::Value deepCopyJSIValue(jsi::Runtime &rt, const jsi::Value &value) {
+  if (value.isNull()) {
+    return jsi::Value::null();
+  }
+
+  if (value.isBool()) {
+    return jsi::Value(value.getBool());
+  }
+
+  if (value.isNumber()) {
+    return jsi::Value(value.getNumber());
+  }
+
+  if (value.isString()) {
+    return value.getString(rt);
+  }
+
+  if (value.isObject()) {
+    jsi::Object o = value.getObject(rt);
+    if (o.isArray(rt)) {
+      return deepCopyJSIArray(rt, o.getArray(rt));
+    }
+    if (o.isFunction(rt)) {
+      return o.getFunction(rt);
+    }
+    return deepCopyJSIObject(rt, o);
+  }
+
+  return jsi::Value::undefined();
+}
+
+jsi::Object deepCopyJSIObject(jsi::Runtime &rt, const jsi::Object &obj) {
+  jsi::Object copy(rt);
+  jsi::Array propertyNames = obj.getPropertyNames(rt);
+  size_t size = propertyNames.size(rt);
+  for (size_t i = 0; i < size; i++) {
+    jsi::String name = propertyNames.getValueAtIndex(rt, i).getString(rt);
+    jsi::Value value = obj.getProperty(rt, name);
+    copy.setProperty(rt, name, deepCopyJSIValue(rt, value));
+  }
+  return copy;
+}
+
+jsi::Array deepCopyJSIArray(jsi::Runtime &rt, const jsi::Array &arr) {
+  size_t size = arr.size(rt);
+  jsi::Array copy(rt, size);
+  for (size_t i = 0; i < size; i++) {
+    copy.setValueAtIndex(rt, i, deepCopyJSIValue(rt, arr.getValueAtIndex(rt, i)));
+  }
+  return copy;
+}
+
+Promise::Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject)
+    : runtime_(rt), resolve_(std::move(resolve)), reject_(std::move(reject)) {}
+
+void Promise::resolve(const jsi::Value &result) {
+  resolve_.call(runtime_, result);
+}
+
+void Promise::reject(const std::string &message) {
+  jsi::Object error(runtime_);
+  error.setProperty(runtime_, "message", jsi::String::createFromUtf8(runtime_, message));
+  reject_.call(runtime_, error);
+}
+
+jsi::Value createPromiseAsJSIValue(jsi::Runtime &rt, const PromiseSetupFunctionType func) {
+  jsi::Function JSPromise = rt.global().getPropertyAsFunction(rt, "Promise");
+  jsi::Function fn = jsi::Function::createFromHostFunction(
+      rt,
+      jsi::PropNameID::forAscii(rt, "fn"),
+      2,
+      [func](jsi::Runtime &rt2, const jsi::Value &thisVal, const jsi::Value *args, size_t count) {
+        jsi::Function resolve = args[0].getObject(rt2).getFunction(rt2);
+        jsi::Function reject = args[1].getObject(rt2).getFunction(rt2);
+        auto wrapper = std::make_shared<Promise>(rt2, std::move(resolve), std::move(reject));
+        func(rt2, wrapper);
+        return jsi::Value::undefined();
+      });
+
+  return JSPromise.callAsConstructor(rt, fn);
+}
+
+} // namespace react
+} // namespace facebook
diff --git a/vnext/Microsoft.ReactNative.Cxx/ReactCommon/TurboModuleUtils.h b/vnext/Microsoft.ReactNative.Cxx/ReactCommon/TurboModuleUtils.h
new file mode 100644
index 00000000000..61853594729
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx/ReactCommon/TurboModuleUtils.h
@@ -0,0 +1,83 @@
+// This file is a copy from react-native NPM package to avoid dependency on folly.
+// It must be removed after the file is fixed in react-native repo:
+// https://github.com/facebook/react-native/pull/30672
+
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#pragma once
+
+#include <cassert>
+#include <string>
+
+// RNW changes: commented out the unused include that creates unnecessary dependency on Folly.
+//#include <folly/Optional.h>
+#include <jsi/jsi.h>
+
+#include <ReactCommon/CallInvoker.h>
+#include <ReactCommon/LongLivedObject.h>
+
+using namespace facebook;
+
+namespace facebook {
+namespace react {
+
+jsi::Object deepCopyJSIObject(jsi::Runtime &rt, const jsi::Object &obj);
+jsi::Array deepCopyJSIArray(jsi::Runtime &rt, const jsi::Array &arr);
+
+struct Promise : public LongLivedObject {
+  Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject);
+
+  void resolve(const jsi::Value &result);
+  void reject(const std::string &error);
+
+  jsi::Runtime &runtime_;
+  jsi::Function resolve_;
+  jsi::Function reject_;
+};
+
+using PromiseSetupFunctionType = std::function<void(jsi::Runtime &rt, std::shared_ptr<Promise>)>;
+jsi::Value createPromiseAsJSIValue(jsi::Runtime &rt, const PromiseSetupFunctionType func);
+
+// Helper for passing jsi::Function arg to other methods.
+class CallbackWrapper : public LongLivedObject {
+ private:
+  CallbackWrapper(jsi::Function &&callback, jsi::Runtime &runtime, std::shared_ptr<CallInvoker> jsInvoker)
+      : callback_(std::move(callback)), runtime_(runtime), jsInvoker_(std::move(jsInvoker)) {}
+
+  jsi::Function callback_;
+  jsi::Runtime &runtime_;
+  std::shared_ptr<CallInvoker> jsInvoker_;
+
+ public:
+  static std::weak_ptr<CallbackWrapper>
+  createWeak(jsi::Function &&callback, jsi::Runtime &runtime, std::shared_ptr<CallInvoker> jsInvoker) {
+    auto wrapper = std::shared_ptr<CallbackWrapper>(new CallbackWrapper(std::move(callback), runtime, jsInvoker));
+    LongLivedObjectCollection::get().add(wrapper);
+    return wrapper;
+  }
+
+  // Delete the enclosed jsi::Function
+  void destroy() {
+    allowRelease();
+  }
+
+  jsi::Function &callback() {
+    return callback_;
+  }
+
+  jsi::Runtime &runtime() {
+    return runtime_;
+  }
+
+  CallInvoker &jsInvoker() {
+    return *(jsInvoker_);
+  }
+};
+
+} // namespace react
+} // namespace facebook
diff --git a/vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h b/vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h
index eb5811340f8..88c7f536269 100644
--- a/vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h
+++ b/vnext/Microsoft.ReactNative.Cxx/ReactPropertyBag.h
@@ -71,6 +71,7 @@
 //
 
 #include <winrt/Microsoft.ReactNative.h>
+#include <winrt/Windows.Foundation.h>
 #include <optional>
 #include "ReactHandleHelper.h"
 #include "ReactNonAbiValue.h"
diff --git a/vnext/Microsoft.ReactNative.Cxx/TurboModuleProvider.cpp b/vnext/Microsoft.ReactNative.Cxx/TurboModuleProvider.cpp
new file mode 100644
index 00000000000..dcc19c8a434
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx/TurboModuleProvider.cpp
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#include "pch.h"
+#include "TurboModuleProvider.h"
+
+namespace winrt::Microsoft::ReactNative {
+
+// CallInvoker implementation based on JSDispatcher.
+struct AbiCallInvoker final : facebook::react::CallInvoker {
+  AbiCallInvoker(IReactDispatcher const &jsDispatcher) : m_jsDispatcher(jsDispatcher) {}
+
+  void invokeAsync(std::function<void()> &&func) override {
+    m_jsDispatcher.Post([func = std::move(func)]() { func(); });
+  }
+
+  void invokeSync(std::function<void()> &&func) override {
+    // Throwing an exception in this method matches the behavior of
+    // Instance::JSCallInvoker::invokeSync in react-native\ReactCommon\cxxreact\Instance.cpp
+    throw std::runtime_error("Synchronous native -> JS calls are currently not supported.");
+  }
+
+ private:
+  IReactDispatcher m_jsDispatcher{nullptr};
+};
+
+// Creates CallInvoker based on JSDispatcher.
+std::shared_ptr<facebook::react::CallInvoker> MakeAbiCallInvoker(IReactDispatcher const &jsDispatcher) noexcept {
+  return std::make_shared<AbiCallInvoker>(jsDispatcher);
+}
+
+} // namespace winrt::Microsoft::ReactNative
diff --git a/vnext/Microsoft.ReactNative.Cxx/TurboModuleProvider.h b/vnext/Microsoft.ReactNative.Cxx/TurboModuleProvider.h
new file mode 100644
index 00000000000..c3b74567a79
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.Cxx/TurboModuleProvider.h
@@ -0,0 +1,35 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#pragma once
+#include <JSI/JsiAbiApi.h>
+#include <JSI/JsiApiContext.h>
+#include <ReactCommon/TurboModule.h>
+#include <winrt/Microsoft.ReactNative.h>
+#include <winrt/Windows.Foundation.h>
+
+namespace winrt::Microsoft::ReactNative {
+
+// Creates CallInvoker based on JSDispatcher.
+std::shared_ptr<facebook::react::CallInvoker> MakeAbiCallInvoker(IReactDispatcher const &jsDispatcher) noexcept;
+
+template <
+    typename TTurboModule,
+    std::enable_if_t<std::is_base_of_v<::facebook::react::TurboModule, TTurboModule>, int> = 0>
+void AddTurboModuleProvider(IReactPackageBuilder const &packageBuilder, std::wstring_view moduleName) {
+  auto experimental = packageBuilder.as<IReactPackageBuilderExperimental>();
+  experimental.AddTurboModule(
+      moduleName, [](IReactModuleBuilder const &moduleBuilder) noexcept->winrt::Windows::Foundation::IInspectable {
+        IJsiHostObject abiTurboModule{nullptr};
+        // We expect the initializer to be called immediately for TurboModules
+        moduleBuilder.AddInitializer([&abiTurboModule](IReactContext const &context) mutable {
+          GetOrCreateContextRuntime(ReactContext{context}); // Ensure the JSI runtime is created.
+          auto callInvoker = MakeAbiCallInvoker(context.JSDispatcher());
+          auto turboModule = std::make_shared<TTurboModule>(callInvoker);
+          abiTurboModule = winrt::make<JsiHostObjectWrapper>(std::move(turboModule));
+        });
+        return abiTurboModule.as<winrt::Windows::Foundation::IInspectable>();
+      });
+}
+
+} // namespace winrt::Microsoft::ReactNative
diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/JsiTurboModuleTests.cpp b/vnext/Microsoft.ReactNative.IntegrationTests/JsiTurboModuleTests.cpp
new file mode 100644
index 00000000000..b82352c3e48
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.IntegrationTests/JsiTurboModuleTests.cpp
@@ -0,0 +1,420 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// The TurboModule sample is based on the code from the react-native NPM package.
+// Below is the original copyright notice for that code.
+// The referred LICENSE file is in the root of the react-native NPM package.
+/*
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+// This test shows how we can use the react-native style of C++ TurboModules in
+// ReactNative Windows. They work on top of ABI safe JSI implementation.
+// The only RNW specific part is the registration of TurboModule class using IReactPackageBuilder.
+// See the details for the MySimpleTurboModulePackageProvider below.
+
+#include "pch.h"
+#include <JSValue.h>
+#include <ReactCommon/TurboModule.h> // This comes from the react-native package.
+#include <ReactCommon/TurboModuleUtils.h> // This must come from the react-native package, but we use a local version to fix issues.
+#include <TurboModuleProvider.h> // It is RNW specific
+
+#include <condition_variable>
+#include <limits>
+#include <mutex>
+#include <sstream>
+#include <string>
+
+using namespace winrt::Microsoft::ReactNative;
+using namespace winrt::Windows::Foundation;
+
+namespace ReactNativeIntegrationTests {
+
+// Use anonymous namespace to avoid any linking conflicts
+namespace {
+
+// In this test we put spec definition that normally must be generated.
+// >>>> Start generated
+
+// The spec from .h file
+struct MySimpleTurboModuleSpec : ::facebook::react::TurboModule {
+  virtual void
+  logAction(facebook::jsi::Runtime &rt, const facebook::jsi::String &actionName, const facebook::jsi::Value &value) = 0;
+  virtual void voidFunc(::facebook::jsi::Runtime &rt) = 0;
+  virtual bool getBool(::facebook::jsi::Runtime &rt, bool arg) = 0;
+  virtual double getNumber(::facebook::jsi::Runtime &rt, double arg) = 0;
+  virtual ::facebook::jsi::String getString(::facebook::jsi::Runtime &rt, const ::facebook::jsi::String &arg) = 0;
+  virtual ::facebook::jsi::Array getArray(::facebook::jsi::Runtime &rt, const ::facebook::jsi::Array &arg) = 0;
+  virtual ::facebook::jsi::Object getObject(::facebook::jsi::Runtime &rt, const ::facebook::jsi::Object &arg) = 0;
+  virtual ::facebook::jsi::Object getValue(
+      ::facebook::jsi::Runtime &rt,
+      double x,
+      const ::facebook::jsi::String &y,
+      const ::facebook::jsi::Object &z) = 0;
+  virtual void getValueWithCallback(::facebook::jsi::Runtime &rt, const ::facebook::jsi::Function &callback) = 0;
+  virtual ::facebook::jsi::Value getValueWithPromise(::facebook::jsi::Runtime &rt, bool error) = 0;
+  virtual ::facebook::jsi::Object getConstants(::facebook::jsi::Runtime &rt) = 0;
+
+ protected:
+  MySimpleTurboModuleSpec(std::shared_ptr<facebook::react::CallInvoker> jsInvoker);
+};
+
+// The spec from .cpp file
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_logAction(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 2);
+  static_cast<MySimpleTurboModuleSpec *>(&turboModule)->logAction(rt, args[0].getString(rt), args[1]);
+  return ::facebook::jsi::Value::undefined();
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_voidFunc(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 0);
+  static_cast<MySimpleTurboModuleSpec *>(&turboModule)->voidFunc(rt);
+  return ::facebook::jsi::Value::undefined();
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getBool(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 1);
+  return ::facebook::jsi::Value(static_cast<MySimpleTurboModuleSpec *>(&turboModule)->getBool(rt, args[0].getBool()));
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getNumber(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 1);
+  return ::facebook::jsi::Value(
+      static_cast<MySimpleTurboModuleSpec *>(&turboModule)->getNumber(rt, args[0].getNumber()));
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getString(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 1);
+  return static_cast<MySimpleTurboModuleSpec *>(&turboModule)->getString(rt, args[0].getString(rt));
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getArray(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 1);
+  return static_cast<MySimpleTurboModuleSpec *>(&turboModule)->getArray(rt, args[0].getObject(rt).getArray(rt));
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getObject(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 1);
+  return static_cast<MySimpleTurboModuleSpec *>(&turboModule)->getObject(rt, args[0].getObject(rt));
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getValue(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 3);
+  return static_cast<MySimpleTurboModuleSpec *>(&turboModule)
+      ->getValue(rt, args[0].getNumber(), args[1].getString(rt), args[2].getObject(rt));
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getValueWithCallback(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 1);
+  static_cast<MySimpleTurboModuleSpec *>(&turboModule)
+      ->getValueWithCallback(rt, std::move(args[0].getObject(rt).getFunction(rt)));
+  return ::facebook::jsi::Value::undefined();
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getValueWithPromise(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 1);
+  return static_cast<MySimpleTurboModuleSpec *>(&turboModule)->getValueWithPromise(rt, args[0].getBool());
+}
+
+static ::facebook::jsi::Value MySimpleTurboModuleSpec_getConstants(
+    ::facebook::jsi::Runtime &rt,
+    ::facebook::react::TurboModule &turboModule,
+    [[maybe_unused]] const ::facebook::jsi::Value *args,
+    [[maybe_unused]] size_t count) {
+  assert(count >= 0);
+  return static_cast<MySimpleTurboModuleSpec *>(&turboModule)->getConstants(rt);
+}
+
+MySimpleTurboModuleSpec::MySimpleTurboModuleSpec(std::shared_ptr<::facebook::react::CallInvoker> jsInvoker)
+    : ::facebook::react::TurboModule("MySimpleTurboModule", std::move(jsInvoker)) {
+  methodMap_.try_emplace("logAction", MethodMetadata{0, MySimpleTurboModuleSpec_logAction});
+  methodMap_.try_emplace("voidFunc", MethodMetadata{0, MySimpleTurboModuleSpec_voidFunc});
+  methodMap_.try_emplace("getBool", MethodMetadata{1, MySimpleTurboModuleSpec_getBool});
+  methodMap_.try_emplace("getNumber", MethodMetadata{1, MySimpleTurboModuleSpec_getNumber});
+  methodMap_.try_emplace("getString", MethodMetadata{1, MySimpleTurboModuleSpec_getString});
+  methodMap_.try_emplace("getArray", MethodMetadata{1, MySimpleTurboModuleSpec_getArray});
+  methodMap_.try_emplace("getObject", MethodMetadata{1, MySimpleTurboModuleSpec_getObject});
+  methodMap_.try_emplace("getValue", MethodMetadata{3, MySimpleTurboModuleSpec_getValue});
+  methodMap_.try_emplace("getValueWithCallback", MethodMetadata{1, MySimpleTurboModuleSpec_getValueWithCallback});
+  methodMap_.try_emplace("getValueWithPromise", MethodMetadata{1, MySimpleTurboModuleSpec_getValueWithPromise});
+  methodMap_.try_emplace("getConstants", MethodMetadata{0, MySimpleTurboModuleSpec_getConstants});
+}
+
+// <<<< End generated
+
+struct TestAction {
+  std::string ActionName;
+  JSValue Value;
+};
+
+struct MySimpleTurboModule : MySimpleTurboModuleSpec {
+  MySimpleTurboModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker);
+
+  void logAction(facebook::jsi::Runtime &rt, const facebook::jsi::String &actionName, const facebook::jsi::Value &value)
+      override;
+  void voidFunc(facebook::jsi::Runtime &rt) override;
+  bool getBool(facebook::jsi::Runtime &rt, bool arg) override;
+  double getNumber(facebook::jsi::Runtime &rt, double arg) override;
+  facebook::jsi::String getString(facebook::jsi::Runtime &rt, const facebook::jsi::String &arg) override;
+  facebook::jsi::Array getArray(facebook::jsi::Runtime &rt, const facebook::jsi::Array &arg) override;
+  facebook::jsi::Object getObject(facebook::jsi::Runtime &rt, const facebook::jsi::Object &arg) override;
+  facebook::jsi::Object getValue(
+      facebook::jsi::Runtime &rt,
+      double x,
+      const facebook::jsi::String &y,
+      const facebook::jsi::Object &z) override;
+  void getValueWithCallback(facebook::jsi::Runtime &rt, const facebook::jsi::Function &callback) override;
+  facebook::jsi::Value getValueWithPromise(facebook::jsi::Runtime &rt, bool error) override;
+  facebook::jsi::Object getConstants(facebook::jsi::Runtime &rt) override;
+
+ public: // Code to report test results
+  static void LogAction(std::string actionName, JSValue value) {
+    auto lock = std::scoped_lock{s_mutex};
+    s_currentAction.ActionName = std::move(actionName);
+    s_currentAction.Value = std::move(value);
+    ++s_actionIndex;
+    s_cv.notify_all();
+  }
+
+  static std::mutex s_mutex;
+  static std::condition_variable s_cv;
+  static TestAction s_currentAction;
+  static int s_actionIndex;
+};
+
+/*static*/ std::mutex MySimpleTurboModule::s_mutex;
+/*static*/ std::condition_variable MySimpleTurboModule::s_cv;
+/*static*/ TestAction MySimpleTurboModule::s_currentAction;
+/*static*/ int MySimpleTurboModule::s_actionIndex{-1};
+
+MySimpleTurboModule::MySimpleTurboModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker)
+    : MySimpleTurboModuleSpec(std::move(jsInvoker)) {}
+
+void MySimpleTurboModule::logAction(
+    facebook::jsi::Runtime &rt,
+    const facebook::jsi::String &actionName,
+    const facebook::jsi::Value &value) {
+  JSValue jsValue{};
+  if (value.isBool()) {
+    jsValue = JSValue(value.getBool());
+  } else if (value.isNumber()) {
+    jsValue = JSValue(value.getNumber());
+  } else if (value.isString()) {
+    jsValue = JSValue(value.getString(rt).utf8(rt));
+  }
+  LogAction(actionName.utf8(rt), std::move(jsValue));
+}
+
+void MySimpleTurboModule::voidFunc(facebook::jsi::Runtime & /*rt*/) {
+  LogAction("voidFunc called", nullptr);
+}
+
+bool MySimpleTurboModule::getBool(facebook::jsi::Runtime & /*rt*/, bool arg) {
+  LogAction("getBool called", arg);
+  return arg;
+}
+
+double MySimpleTurboModule::getNumber(facebook::jsi::Runtime & /*rt*/, double arg) {
+  LogAction("getNumber called", arg);
+  return arg;
+}
+
+facebook::jsi::String MySimpleTurboModule::getString(facebook::jsi::Runtime &rt, const facebook::jsi::String &arg) {
+  LogAction("getString called", arg.utf8(rt));
+  return facebook::jsi::String::createFromUtf8(rt, arg.utf8(rt));
+}
+
+facebook::jsi::Array MySimpleTurboModule::getArray(facebook::jsi::Runtime &rt, const facebook::jsi::Array &arg) {
+  LogAction("getArray called", arg.length(rt));
+  return facebook::react::deepCopyJSIArray(rt, arg);
+}
+
+facebook::jsi::Object MySimpleTurboModule::getObject(facebook::jsi::Runtime &rt, const facebook::jsi::Object &arg) {
+  LogAction("getObject called", "OK");
+  return facebook::react::deepCopyJSIObject(rt, arg);
+}
+
+facebook::jsi::Object MySimpleTurboModule::getValue(
+    facebook::jsi::Runtime &rt,
+    double x,
+    const facebook::jsi::String &y,
+    const facebook::jsi::Object &z) {
+  LogAction("getValue called", "OK");
+  // Note: return type isn't type-safe.
+  facebook::jsi::Object result(rt);
+  result.setProperty(rt, "x", facebook::jsi::Value(x));
+  result.setProperty(rt, "y", facebook::jsi::String::createFromUtf8(rt, y.utf8(rt)));
+  result.setProperty(rt, "z", facebook::react::deepCopyJSIObject(rt, z));
+  return result;
+}
+
+void MySimpleTurboModule::getValueWithCallback(facebook::jsi::Runtime &rt, const facebook::jsi::Function &callback) {
+  LogAction("getValueWithCallback called", "OK");
+  callback.call(rt, facebook::jsi::String::createFromUtf8(rt, "value from callback!"));
+}
+
+facebook::jsi::Value MySimpleTurboModule::getValueWithPromise(facebook::jsi::Runtime &rt, bool error) {
+  LogAction("getValueWithPromise called", error);
+  return facebook::react::createPromiseAsJSIValue(
+      rt, [error](facebook::jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
+        if (error) {
+          promise->reject("intentional promise rejection");
+        } else {
+          promise->resolve(facebook::jsi::String::createFromUtf8(rt2, "result!"));
+        }
+      });
+}
+
+facebook::jsi::Object MySimpleTurboModule::getConstants(facebook::jsi::Runtime &rt) {
+  LogAction("getConstants called", "OK");
+  // Note: return type isn't type-safe.
+  facebook::jsi::Object result(rt);
+  result.setProperty(rt, "const1", facebook::jsi::Value(true));
+  result.setProperty(rt, "const2", facebook::jsi::Value(375));
+  result.setProperty(rt, "const3", facebook::jsi::String::createFromUtf8(rt, "something"));
+  return result;
+}
+
+struct MySimpleTurboModulePackageProvider
+    : winrt::implements<MySimpleTurboModulePackageProvider, IReactPackageProvider> {
+  void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept {
+    AddTurboModuleProvider<MySimpleTurboModule>(packageBuilder, L"MySimpleTurboModule");
+  }
+};
+
+} // namespace
+
+TEST_CLASS (JsiTurboModuleTests) {
+  TEST_METHOD(ExecuteSampleTurboModule) {
+    MySimpleTurboModule::s_actionIndex = -1;
+    TestAction expectedActions[] = {
+        TestAction{"voidFunc called", nullptr},
+        TestAction{"getBool called", true},
+        TestAction{"getBool result", true},
+        TestAction{"getBool called", false},
+        TestAction{"getBool result", false},
+        TestAction{"getNumber called", 5.0},
+        TestAction{"getNumber result", 5.0},
+        TestAction{"getNumber called", std::numeric_limits<double>::quiet_NaN()},
+        TestAction{"getNumber result", std::numeric_limits<double>::quiet_NaN()},
+        TestAction{"getNumber called", std::numeric_limits<double>::infinity()},
+        TestAction{"getNumber result", std::numeric_limits<double>::infinity()},
+        TestAction{"getString called", "Hello"},
+        TestAction{"getString result", "Hello"},
+        TestAction{"getString called", ""},
+        TestAction{"getString result", ""},
+        TestAction{"getArray called", 3},
+        TestAction{"getArray result", "OK"},
+        TestAction{"getObject called", "OK"},
+        TestAction{"getObject result", "OK"},
+        TestAction{"getValue called", "OK"},
+        TestAction{"getValue result", "OK"},
+        TestAction{"getConstants called", "OK"},
+        TestAction{"getConstants result", "OK"},
+        TestAction{"getValueWithCallback called", "OK"},
+        TestAction{"getValueWithCallback result", "value from callback!"},
+        TestAction{"getValueWithPromise called", false},
+        TestAction{"getValueWithPromise called", true},
+        TestAction{"getValueWithPromise result resolve", "result!"},
+        TestAction{"getValueWithPromise result reject", "intentional promise rejection"},
+    };
+
+    ReactNativeHost host{};
+
+    auto queueController = winrt::Windows::System::DispatcherQueueController::CreateOnDedicatedThread();
+    queueController.DispatcherQueue().TryEnqueue([&]() noexcept {
+      host.PackageProviders().Append(winrt::make<MySimpleTurboModulePackageProvider>());
+
+      // bundle is assumed to be co-located with the test binary
+      wchar_t testBinaryPath[MAX_PATH];
+      TestCheck(GetModuleFileNameW(NULL, testBinaryPath, MAX_PATH) < MAX_PATH);
+      testBinaryPath[std::wstring_view{testBinaryPath}.rfind(L"\\")] = 0;
+
+      host.InstanceSettings().BundleRootPath(testBinaryPath);
+      host.InstanceSettings().JavaScriptBundleFile(L"JsiTurboModuleTests");
+      host.InstanceSettings().UseDeveloperSupport(false);
+      host.InstanceSettings().UseWebDebugger(false);
+      host.InstanceSettings().UseFastRefresh(false);
+      host.InstanceSettings().UseLiveReload(false);
+      host.InstanceSettings().EnableDeveloperMenu(false);
+
+      host.LoadInstance().Completed(
+          [](IAsyncAction asyncInfo, AsyncStatus asyncStatus) { TestCheckEqual(AsyncStatus::Completed, asyncStatus); });
+    });
+
+    auto lock = std::unique_lock{MySimpleTurboModule::s_mutex};
+    MySimpleTurboModule::s_cv.wait(lock, [&]() {
+      if (MySimpleTurboModule::s_actionIndex >= 0) {
+        auto const &expectedAction = expectedActions[MySimpleTurboModule::s_actionIndex];
+        TestCheckEqual(expectedAction.ActionName, MySimpleTurboModule::s_currentAction.ActionName);
+        if (auto d1 = expectedAction.Value.TryGetDouble(),
+            d2 = MySimpleTurboModule::s_currentAction.Value.TryGetDouble();
+            d1 && d2) {
+          if (!isnan(*d1) && !isnan(*d2)) {
+            TestCheckEqual(*d1, *d2);
+          }
+        } else if (expectedAction.Value != MySimpleTurboModule::s_currentAction.Value) {
+          std::stringstream os;
+          os << "Action index: " << MySimpleTurboModule::s_actionIndex << '\n'
+             << "Expected: " << expectedAction.Value.ToString() << '\n'
+             << "Actual: " << MySimpleTurboModule::s_currentAction.Value.ToString();
+          TestCheckFail("%s", os.str().c_str());
+        }
+      }
+      return MySimpleTurboModule::s_actionIndex >= static_cast<ptrdiff_t>(std::size(expectedActions)) - 1;
+    });
+
+    // Make sure that we did all actions
+    TestCheckEqual(MySimpleTurboModule::s_actionIndex, static_cast<ptrdiff_t>(std::size(expectedActions)) - 1);
+    lock.unlock();
+
+    host.UnloadInstance().get();
+    queueController.ShutdownQueueAsync().get();
+  }
+};
+
+} // namespace ReactNativeIntegrationTests
diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/JsiTurboModuleTests.js b/vnext/Microsoft.ReactNative.IntegrationTests/JsiTurboModuleTests.js
new file mode 100644
index 00000000000..999656cd075
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.IntegrationTests/JsiTurboModuleTests.js
@@ -0,0 +1,38 @@
+import * as TurboModuleRegistry from '../Libraries/TurboModule/TurboModuleRegistry';
+const mySimpleTurboModule = TurboModuleRegistry.getEnforcing('MySimpleTurboModule');
+
+// The logging of the TurboModule functions is verified against the test action sequence.
+
+mySimpleTurboModule.voidFunc();
+mySimpleTurboModule.logAction("getBool result", mySimpleTurboModule.getBool(true));
+mySimpleTurboModule.logAction("getBool result", mySimpleTurboModule.getBool(false));
+mySimpleTurboModule.logAction("getNumber result", mySimpleTurboModule.getNumber(5));
+mySimpleTurboModule.logAction("getNumber result", mySimpleTurboModule.getNumber(Number.NaN));
+mySimpleTurboModule.logAction("getNumber result", mySimpleTurboModule.getNumber(Number.POSITIVE_INFINITY));
+mySimpleTurboModule.logAction("getString result", mySimpleTurboModule.getString("Hello"));
+mySimpleTurboModule.logAction("getString result", mySimpleTurboModule.getString(""));
+
+const arr1 = mySimpleTurboModule.getArray(["Hello", 5, false]);
+const arr1Matches = (arr1.length === 3) && (arr1[0] === "Hello") && (arr1[1] === 5) && (arr1[2] === false);
+mySimpleTurboModule.logAction("getArray result", arr1Matches ? "OK" : "Failed");
+
+const obj1 = mySimpleTurboModule.getObject({ x: "Hello", y: 5, z: false });
+const obj1Matches = (obj1.x === "Hello") && (obj1.y === 5) && (obj1.z === false);
+mySimpleTurboModule.logAction("getObject result", obj1Matches ? "OK" : "Failed");
+
+const obj2 = mySimpleTurboModule.getValue(42, "Hello", { x: 5, y: 12 });
+const obj2Matches = (obj2.x === 42) && (obj2.y === "Hello") && (obj2.z.x === 5) && (obj2.z.y === 12);
+mySimpleTurboModule.logAction("getValue result", obj2Matches ? "OK" : "Failed");
+
+const obj3 = mySimpleTurboModule.getConstants();
+const obj3Matches = (obj3.const1 === true) && (obj3.const2 === 375) && (obj3.const3 === "something");
+mySimpleTurboModule.logAction("getConstants result", obj3Matches ? "OK" : "Failed");
+
+mySimpleTurboModule.getValueWithCallback(
+  (value) => mySimpleTurboModule.logAction("getValueWithCallback result", value));
+
+mySimpleTurboModule.getValueWithPromise(/*error:*/false)
+  .then((value) => mySimpleTurboModule.logAction("getValueWithPromise result resolve", value));
+
+mySimpleTurboModule.getValueWithPromise(/*error:*/true).catch(
+  (err) => mySimpleTurboModule.logAction("getValueWithPromise result reject", err.message));
diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/Microsoft.ReactNative.IntegrationTests.vcxproj b/vnext/Microsoft.ReactNative.IntegrationTests/Microsoft.ReactNative.IntegrationTests.vcxproj
index 4bcb11f5316..c7332560777 100644
--- a/vnext/Microsoft.ReactNative.IntegrationTests/Microsoft.ReactNative.IntegrationTests.vcxproj
+++ b/vnext/Microsoft.ReactNative.IntegrationTests/Microsoft.ReactNative.IntegrationTests.vcxproj
@@ -108,6 +108,7 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="JsiRuntimeTests.cpp" />
+    <ClCompile Include="JsiTurboModuleTests.cpp" />
     <ClCompile Include="ReactInstanceSettingsTests.cpp" />
     <ClCompile Include="ReactNonAbiValueTests.cpp" />
     <ClCompile Include="ReactNotificationServiceTests.cpp" />
@@ -137,9 +138,11 @@
   <ItemGroup>
     <Manifest Include="Application.manifest" />
     <None Include="AddValues.js" />
+    <None Include="JsiTurboModuleTests.js" />
     <None Include="TurboModuleTests.js" />
     <None Include="packages.config" />
     <JsBundleEntry Include="AddValues.js" />
+    <JsBundleEntry Include="JsiTurboModuleTests.js" />
     <JsBundleEntry Include="TurboModuleTests.js" />
   </ItemGroup>
   <ItemGroup>
diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/Microsoft.ReactNative.IntegrationTests.vcxproj.filters b/vnext/Microsoft.ReactNative.IntegrationTests/Microsoft.ReactNative.IntegrationTests.vcxproj.filters
new file mode 100644
index 00000000000..789add985c2
--- /dev/null
+++ b/vnext/Microsoft.ReactNative.IntegrationTests/Microsoft.ReactNative.IntegrationTests.vcxproj.filters
@@ -0,0 +1,30 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Manifest Include="Application.manifest" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="JsiRuntimeTests.cpp" />
+    <ClCompile Include="ReactInstanceSettingsTests.cpp" />
+    <ClCompile Include="ReactNonAbiValueTests.cpp" />
+    <ClCompile Include="ReactNotificationServiceTests.cpp" />
+    <ClCompile Include="ReactPropertyBagTests.cpp" />
+    <ClCompile Include="ReactNativeHostTests.cpp" />
+    <ClCompile Include="TurboModuleTests.cpp" />
+    <ClCompile Include="main.cpp" />
+    <ClCompile Include="pch.cpp" />
+    <ClCompile Include="$(ReactNativeDir)\ReactCommon\jsi\jsi\test\testlib.cpp" />
+    <ClCompile Include="JsiTurboModuleTests.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="MockReactPackageProvider.h" />
+    <ClInclude Include="$(ReactNativeDir)\ReactCommon\jsi\jsi\test\testlib.h" />
+    <ClInclude Include="pch.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="AddValues.js" />
+    <None Include="TurboModuleTests.js" />
+    <None Include="packages.config" />
+    <None Include="JsiTurboModuleTests.js" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp b/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp
index 80bddf5fd8d..f53161abc59 100644
--- a/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp
+++ b/vnext/Microsoft.ReactNative.IntegrationTests/ReactNativeHostTests.cpp
@@ -2,7 +2,7 @@
 // Licensed under the MIT License.
 
 #include "pch.h"
-#include <JSI/JsiApi.h>
+#include <JSI/JsiApiContext.h>
 #include <NativeModules.h>
 #include <winrt/Windows.System.h>
 #include "MockReactPackageProvider.h"
diff --git a/vnext/Microsoft.ReactNative/IReactContext.cpp b/vnext/Microsoft.ReactNative/IReactContext.cpp
index feb7acf2e25..7e92e45de7e 100644
--- a/vnext/Microsoft.ReactNative/IReactContext.cpp
+++ b/vnext/Microsoft.ReactNative/IReactContext.cpp
@@ -100,7 +100,7 @@ IReactDispatcher ReactContext::JSDispatcher() noexcept {
   return Properties().Get(ReactDispatcherHelper::JSDispatcherProperty()).try_as<IReactDispatcher>();
 }
 
-IInspectable ReactContext::JSRuntime() noexcept {
+Windows::Foundation::IInspectable ReactContext::JSRuntime() noexcept {
   return m_context->JsiRuntime();
 }
 
diff --git a/vnext/Microsoft.ReactNative/IReactModuleBuilder.idl b/vnext/Microsoft.ReactNative/IReactModuleBuilder.idl
index f90223e2e65..062c368177a 100644
--- a/vnext/Microsoft.ReactNative/IReactModuleBuilder.idl
+++ b/vnext/Microsoft.ReactNative/IReactModuleBuilder.idl
@@ -7,26 +7,26 @@ import "IReactContext.idl";
 
 #include "DocString.h"
 
-namespace Microsoft.ReactNative {
-
-  // A delegate that will set reactContext for a module.
-  // We use it for a stand-alone initialize method, strongly typed JS events and functions.
-  DOC_STRING("A delegate that will set `reactContext` for a module. We use it for a stand-alone initialize method, strongly typed JS events and functions.")
+namespace Microsoft.ReactNative
+{
+  DOC_STRING(
+    "A delegate that sets `reactContext` for a module.\n"
+    "We use it for a stand-alone initialize method, strongly typed JS events and functions.\n"
+    "Experimental code uses it to initialize TurboModule `CallInvoker`.")
   delegate void InitializerDelegate(IReactContext reactContext);
 
   // Native method return type.
-  enum MethodReturnType {
+  enum MethodReturnType
+  {
     Void,
     Callback,
     TwoCallbacks,
     Promise,
   };
 
-  // A callback to call JS code with results.
   DOC_STRING("A callback to call JS code with results.")
   delegate void MethodResultCallback(IJSValueWriter outputWriter);
 
-  // A delegate to call native asynchronous method.
   DOC_STRING("A delegate to call native asynchronous method.")
   delegate void MethodDelegate(
       IJSValueReader inputReader,
@@ -34,25 +34,28 @@ namespace Microsoft.ReactNative {
       MethodResultCallback resolve,
       MethodResultCallback reject);
 
-  // A delegate to call native synchronous method.
   DOC_STRING("A delegate to call native synchronous method.")
   delegate void SyncMethodDelegate(IJSValueReader inputReader, IJSValueWriter outputWriter);
 
-  // A delegate to gather constants from native modules.
-  DOC_STRING("A delegate to gather constants from native modules.")
+  DOC_STRING("A delegate to gather constants for the native module.")
   delegate void ConstantProviderDelegate(IJSValueWriter constantWriter);
 
-  // Builds native modules inside of React Native code based on the provided meta-data.
   [webhosthidden]
-  DOC_STRING("Builds native modules inside of React native code based on the provided meta-data. \
-See [Native Modules](native-modules) for more usage information.")
-  interface IReactModuleBuilder {
-
-    DOC_STRING("An initializer is a method that will be called when the react instance starts.  It provides the native module with the @IReactContext for the running instance. See @InitializerDelegate.")
+  DOC_STRING(
+    "Builds native module inside of ReactNative code based on the provided meta-data.\n"
+    "See [Native Modules](native-modules) for more usage information.")
+  interface IReactModuleBuilder
+  {
+    DOC_STRING(
+      "Adds an initializer method called on the native module initialization.\n"
+      "It provides the native module with the @IReactContext for the running ReactNative instance.  See @InitializerDelegate.\n"
+      "There can be multiple initializer methods which are called in the order they were registered.")
     void AddInitializer(InitializerDelegate initializer);
 
-    DOC_STRING("The JavaScript values written by the @ConstantProviderDelegate will be available as constants on the native module is JavaScript.")
+    DOC_STRING("Adds a constant provider method to define constants for the native module.  See @ConstantProviderDelegate.")
     void AddConstantProvider(ConstantProviderDelegate constantProvider);
+
+    DOC_STRING("Adds an asynchronous method to the native module.  See @MethodDelegate.")
     void AddMethod(String name, MethodReturnType returnType, MethodDelegate method);
 
     DOC_STRING("Adds a synchronous method to the native module.  See @SyncMethodDelegate.")
diff --git a/vnext/Microsoft.ReactNative/IReactPackageBuilder.idl b/vnext/Microsoft.ReactNative/IReactPackageBuilder.idl
index bf59373d94c..3a09663f771 100644
--- a/vnext/Microsoft.ReactNative/IReactPackageBuilder.idl
+++ b/vnext/Microsoft.ReactNative/IReactPackageBuilder.idl
@@ -9,8 +9,8 @@ import "IViewManager.idl";
 
 #include "DocString.h"
 
-namespace Microsoft.ReactNative {
-
+namespace Microsoft.ReactNative
+{
   DOC_STRING("Provides information about a custom native module.  See @IReactModuleBuilder.")
   delegate Object ReactModuleProvider(IReactModuleBuilder moduleBuilder);
 
@@ -20,20 +20,24 @@ namespace Microsoft.ReactNative {
 #endif
 
   [webhosthidden]
-  DOC_STRING("A `ReactPackageBuilder` provides the react instance with all the native modules and view managers that will be available in the React instance.")
-  interface IReactPackageBuilder {
-    DOC_STRING("Adds a custom native module. See @ReactModuleProvider.")
+  DOC_STRING("Builds ReactNative package with the set of native modules and view managers.")
+  interface IReactPackageBuilder
+  {
+    DOC_STRING("Adds a custom native module.  See @ReactModuleProvider.")
     void AddModule(String moduleName, ReactModuleProvider moduleProvider);
 
 #ifndef CORE_ABI
-    DOC_STRING("Adds a custom native module. See @ReactViewManagerProvider.")
+    DOC_STRING("Adds a custom view manager.  See @ReactViewManagerProvider.")
     void AddViewManager(String viewManagerName, ReactViewManagerProvider viewManagerProvider);
 #endif
   }
 
   [webhosthidden]
-  interface IReactPackageBuilderExperimental
-    requires IReactPackageBuilder {
+  [experimental]
+  DOC_STRING("Experimental extensions to the @IReactPackageBuilder.")
+  interface IReactPackageBuilderExperimental requires IReactPackageBuilder
+  {
+    DOC_STRING("Adds a custom TurboModule that uses direct access to JS Engine API.")
     void AddTurboModule(String moduleName, ReactModuleProvider moduleProvider);
   }
 } // namespace Microsoft.ReactNative
diff --git a/vnext/Microsoft.ReactNative/JsiApi.cpp b/vnext/Microsoft.ReactNative/JsiApi.cpp
index 86a11691966..a1f8342d7e5 100644
--- a/vnext/Microsoft.ReactNative/JsiApi.cpp
+++ b/vnext/Microsoft.ReactNative/JsiApi.cpp
@@ -7,6 +7,7 @@
 #include "JsiRuntime.g.cpp"
 #include <Threading/MessageDispatchQueue.h>
 #include <crash/verifyElseCrash.h>
+#include <winrt/Windows.Foundation.Collections.h>
 #include "ReactHost/MsoUtils.h"
 
 namespace winrt::Microsoft::ReactNative::implementation {
@@ -23,22 +24,6 @@ struct JsiPreparedJavaScript : JsiPreparedJavaScriptT<JsiPreparedJavaScript> {
   std::shared_ptr<facebook::jsi::PreparedJavaScript const> m_js;
 };
 
-// Wraps up the IJsiHostObject
-struct HostObjectWrapper : facebook::jsi::HostObject {
-  HostObjectWrapper(Microsoft::ReactNative::IJsiHostObject const &hostObject) noexcept;
-
-  facebook::jsi::Value get(facebook::jsi::Runtime &runtime, const facebook::jsi::PropNameID &name) override;
-  void set(facebook::jsi::Runtime &, const facebook::jsi::PropNameID &name, const facebook::jsi::Value &value) override;
-  std::vector<facebook::jsi::PropNameID> getPropertyNames(facebook::jsi::Runtime &runtime) override;
-
-  Microsoft::ReactNative::IJsiHostObject const &Get() const noexcept {
-    return m_hostObject;
-  }
-
- private:
-  Microsoft::ReactNative::IJsiHostObject m_hostObject;
-};
-
 struct JsiBufferWrapper : facebook::jsi::Buffer {
   JsiBufferWrapper(array_view<const uint8_t> bytes) : m_bytes{bytes} {}
 
@@ -58,137 +43,218 @@ struct JsiBufferWrapper : facebook::jsi::Buffer {
 // Helper methods
 //===========================================================================
 
-template <class TDataHolder>
-static facebook::jsi::Runtime::PointerValue const *AsPointerValue(TDataHolder const &dataHolder) noexcept {
-  return reinterpret_cast<facebook::jsi::Runtime::PointerValue const *>(dataHolder.Data);
-}
+// PointerAccessor allows accessing protected ptr_ value of facebook::jsi::Pointer.
+// It contains all functions that use the protected ptr_ value.
+struct PointerAccessor : facebook::jsi::Pointer {
+  static PointerAccessor const &AsPointerAccessor(facebook::jsi::Pointer const &pointer) noexcept {
+    return static_cast<PointerAccessor const &>(pointer);
+  }
 
-static facebook::jsi::PropNameID const &AsPropNameID(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
-  return *reinterpret_cast<facebook::jsi::PropNameID const *>(ptr);
-}
+  static PointerAccessor &&AsPointerAccessor(facebook::jsi::Pointer &&pointer) noexcept {
+    return static_cast<PointerAccessor &&>(pointer);
+  }
 
-static facebook::jsi::Symbol const &AsSymbol(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
-  return *reinterpret_cast<facebook::jsi::Symbol const *>(ptr);
-}
+  static JsiPropertyIdRef ToJsiPropertyNameIdData(facebook::jsi::PropNameID const &propNameId) noexcept {
+    return {reinterpret_cast<uint64_t>(AsPointerAccessor(propNameId).ptr_)};
+  }
 
-static facebook::jsi::String const &AsString(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
-  return *reinterpret_cast<facebook::jsi::String const *>(ptr);
-}
+  static JsiSymbolRef MakeJsiSymbolData(facebook::jsi::Symbol &&symbol) {
+    auto &&accessor = AsPointerAccessor(std::move(symbol));
+    return {reinterpret_cast<uint64_t>(std::exchange(accessor.ptr_, nullptr))};
+  }
 
-static facebook::jsi::Object const &AsObject(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
-  return *reinterpret_cast<facebook::jsi::Object const *>(ptr);
-}
+  static JsiStringRef MakeJsiStringData(facebook::jsi::String &&str) {
+    auto &&accessor = AsPointerAccessor(std::move(str));
+    return {reinterpret_cast<uint64_t>(std::exchange(accessor.ptr_, nullptr))};
+  }
 
-static facebook::jsi::Function const &AsFunction(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
-  return *reinterpret_cast<facebook::jsi::Function const *>(ptr);
-}
+  static JsiObjectRef MakeJsiObjectData(facebook::jsi::Object &&obj) {
+    auto &&accessor = AsPointerAccessor(std::move(obj));
+    return {reinterpret_cast<uint64_t>(std::exchange(accessor.ptr_, nullptr))};
+  }
 
-static facebook::jsi::WeakObject &AsWeakObject(facebook::jsi::Runtime::PointerValue **ptr) noexcept {
-  return *reinterpret_cast<facebook::jsi::WeakObject *>(ptr);
-}
+  static JsiPropertyIdRef MakeJsiPropertyNameIdData(facebook::jsi::PropNameID &&propertyNameId) {
+    auto &&accessor = AsPointerAccessor(std::move(propertyNameId));
+    return {reinterpret_cast<uint64_t>(std::exchange(accessor.ptr_, nullptr))};
+  }
 
-static facebook::jsi::Array const &AsArray(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
-  return *reinterpret_cast<facebook::jsi::Array const *>(ptr);
-}
+  static JsiObjectRef MakeJsiArrayData(facebook::jsi::Array &&arr) {
+    auto &&accessor = AsPointerAccessor(std::move(arr));
+    return {reinterpret_cast<uint64_t>(std::exchange(accessor.ptr_, nullptr))};
+  }
 
-static facebook::jsi::ArrayBuffer const &AsArrayBuffer(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
-  return *reinterpret_cast<facebook::jsi::ArrayBuffer const *>(ptr);
-}
+  static JsiWeakObjectRef MakeJsiWeakObjectData(facebook::jsi::WeakObject &&weakObj) {
+    auto &&accessor = AsPointerAccessor(std::move(weakObj));
+    return {reinterpret_cast<uint64_t>(std::exchange(accessor.ptr_, nullptr))};
+  }
 
-static facebook::jsi::Value const *AsValue(JsiValueRef const &data) noexcept {
-  return reinterpret_cast<facebook::jsi::Value const *>(&data);
-}
+  static JsiObjectRef MakeJsiFunctionData(facebook::jsi::Function &&func) {
+    auto &&accessor = AsPointerAccessor(std::move(func));
+    return {reinterpret_cast<uint64_t>(std::exchange(accessor.ptr_, nullptr))};
+  }
+};
 
-static JsiValueRef ToJsiValueData(facebook::jsi::Value const &value) noexcept {
-  return {static_cast<JsiValueKind>(static_cast<int32_t>(value.kind_)),
-          *reinterpret_cast<uint64_t const *>(&value.data_.number)};
-}
+// ValueAccessor provides access to private fields of facebook::jsi::Value.
+// We expect it to have the same layout as facebook::jsi::Value.
+// It is ugly, but faster than using the facebook::jsi::Value public API when we
+// convert between JsiValueRef and facebook::jsi::Value.
+struct ValueAccessor {
+  static JsiValueRef ToJsiValueData(facebook::jsi::Value const &value) noexcept {
+    ValueAccessor const &accessor = reinterpret_cast<ValueAccessor const &>(value);
+    return {static_cast<JsiValueKind>(static_cast<int32_t>(accessor.m_kind)),
+            *reinterpret_cast<uint64_t const *>(&accessor.m_data)};
+  }
 
-static JsiPropertyIdRef ToJsiPropertyNameIdData(facebook::jsi::PropNameID const &propNameId) noexcept {
-  return {reinterpret_cast<uint64_t>(propNameId.ptr_)};
-}
+  static JsiValueRef MakeJsiValueData(facebook::jsi::Value &&value) {
+    ValueAccessor &&accessor = reinterpret_cast<ValueAccessor &&>(value);
+    return {static_cast<JsiValueKind>(static_cast<int32_t>(std::exchange(accessor.m_kind, UndefinedKind))),
+            *reinterpret_cast<uint64_t *>(&accessor.m_data)};
+  }
 
-static JsiSymbolRef MakeJsiSymbolData(facebook::jsi::Runtime::PointerValue *pointerValue) {
-  return {reinterpret_cast<uint64_t>(pointerValue)};
-}
+ private:
+  enum ValueKind { UndefinedKind } m_kind;
+  double m_data;
 
-static JsiStringRef MakeJsiStringData(facebook::jsi::Runtime::PointerValue *pointerValue) {
-  return {reinterpret_cast<uint64_t>(pointerValue)};
-}
+  static_assert(sizeof(m_data) == sizeof(uint64_t), "Value data should fit in a 64-bit register");
+};
 
-static JsiObjectRef MakeJsiObjectData(facebook::jsi::Runtime::PointerValue *pointerValue) {
-  return {reinterpret_cast<uint64_t>(pointerValue)};
-}
+static_assert(
+    sizeof(ValueAccessor) == sizeof(facebook::jsi::Value),
+    "ValueAccessor must have the same size as facebook::jsi::Value");
+
+// RuntimeAccessor is used to access facebook::jsi::Runtime protected members.
+struct RuntimeAccessor : facebook::jsi::Runtime {
+  using facebook::jsi::Runtime::PointerValue;
+  using facebook::jsi::Runtime::ScopeState;
+
+  using facebook::jsi::Runtime::make;
+
+  using facebook::jsi::Runtime::call;
+  using facebook::jsi::Runtime::callAsConstructor;
+  using facebook::jsi::Runtime::cloneObject;
+  using facebook::jsi::Runtime::clonePropNameID;
+  using facebook::jsi::Runtime::cloneString;
+  using facebook::jsi::Runtime::cloneSymbol;
+  using facebook::jsi::Runtime::compare;
+  using facebook::jsi::Runtime::createArray;
+  using facebook::jsi::Runtime::createFunctionFromHostFunction;
+  using facebook::jsi::Runtime::createObject;
+  using facebook::jsi::Runtime::createPropNameIDFromAscii;
+  using facebook::jsi::Runtime::createPropNameIDFromString;
+  using facebook::jsi::Runtime::createPropNameIDFromUtf8;
+  using facebook::jsi::Runtime::createStringFromUtf8;
+  using facebook::jsi::Runtime::createValueFromJsonUtf8;
+  using facebook::jsi::Runtime::createWeakObject;
+  using facebook::jsi::Runtime::data;
+  using facebook::jsi::Runtime::getHostObject;
+  using facebook::jsi::Runtime::getProperty;
+  using facebook::jsi::Runtime::getPropertyNames;
+  using facebook::jsi::Runtime::hasProperty;
+  using facebook::jsi::Runtime::instanceOf;
+  using facebook::jsi::Runtime::isArray;
+  using facebook::jsi::Runtime::isArrayBuffer;
+  using facebook::jsi::Runtime::isFunction;
+  using facebook::jsi::Runtime::isHostFunction;
+  using facebook::jsi::Runtime::isHostObject;
+  using facebook::jsi::Runtime::lockWeakObject;
+  using facebook::jsi::Runtime::popScope;
+  using facebook::jsi::Runtime::pushScope;
+  using facebook::jsi::Runtime::setPropertyValue;
+  using facebook::jsi::Runtime::setValueAtIndexImpl;
+  using facebook::jsi::Runtime::size;
+  using facebook::jsi::Runtime::strictEquals;
+  using facebook::jsi::Runtime::symbolToString;
+  using facebook::jsi::Runtime::utf8;
+
+  template <class TDataHolder>
+  static facebook::jsi::Runtime::PointerValue const *AsPointerValue(TDataHolder const &dataHolder) noexcept {
+    return reinterpret_cast<facebook::jsi::Runtime::PointerValue const *>(dataHolder.Data);
+  }
 
-static JsiPropertyIdRef MakeJsiPropertyNameIdData(facebook::jsi::Runtime::PointerValue *pointerValue) {
-  return {reinterpret_cast<uint64_t>(pointerValue)};
-}
+  static facebook::jsi::PropNameID const &AsPropNameID(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
+    return *reinterpret_cast<facebook::jsi::PropNameID const *>(ptr);
+  }
 
-static JsiSymbolRef MakeJsiSymbolData(facebook::jsi::Symbol &&obj) {
-  return {reinterpret_cast<uint64_t>(std::exchange(obj.ptr_, nullptr))};
-}
+  static facebook::jsi::Symbol const &AsSymbol(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
+    return *reinterpret_cast<facebook::jsi::Symbol const *>(ptr);
+  }
 
-static JsiStringRef MakeJsiStringData(facebook::jsi::String &&obj) {
-  return {reinterpret_cast<uint64_t>(std::exchange(obj.ptr_, nullptr))};
-}
+  static facebook::jsi::String const &AsString(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
+    return *reinterpret_cast<facebook::jsi::String const *>(ptr);
+  }
 
-static JsiObjectRef MakeJsiObjectData(facebook::jsi::Object &&obj) {
-  return {reinterpret_cast<uint64_t>(std::exchange(obj.ptr_, nullptr))};
-}
+  static facebook::jsi::Object const &AsObject(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
+    return *reinterpret_cast<facebook::jsi::Object const *>(ptr);
+  }
 
-static JsiPropertyIdRef MakeJsiPropertyNameIdData(facebook::jsi::PropNameID &&propertyNameId) {
-  return {reinterpret_cast<uint64_t>(std::exchange(propertyNameId.ptr_, nullptr))};
-}
+  static facebook::jsi::Function const &AsFunction(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
+    return *reinterpret_cast<facebook::jsi::Function const *>(ptr);
+  }
 
-static JsiObjectRef MakeJsiArrayData(facebook::jsi::Array &&arr) {
-  return {reinterpret_cast<uint64_t>(std::exchange(arr.ptr_, nullptr))};
-}
+  static facebook::jsi::WeakObject &AsWeakObject(facebook::jsi::Runtime::PointerValue **ptr) noexcept {
+    return *reinterpret_cast<facebook::jsi::WeakObject *>(ptr);
+  }
 
-static JsiWeakObjectRef MakeJsiWeakObjectData(facebook::jsi::WeakObject &&weakObj) {
-  return {reinterpret_cast<uint64_t>(std::exchange(weakObj.ptr_, nullptr))};
-}
+  static facebook::jsi::Array const &AsArray(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
+    return *reinterpret_cast<facebook::jsi::Array const *>(ptr);
+  }
 
-static JsiObjectRef MakeJsiFunctionData(facebook::jsi::Function &&func) {
-  return {reinterpret_cast<uint64_t>(std::exchange(func.ptr_, nullptr))};
-}
+  static facebook::jsi::ArrayBuffer const &AsArrayBuffer(facebook::jsi::Runtime::PointerValue const **ptr) noexcept {
+    return *reinterpret_cast<facebook::jsi::ArrayBuffer const *>(ptr);
+  }
 
-static JsiValueRef MakeJsiValueData(facebook::jsi::Value &&value) {
-  return {
-      static_cast<JsiValueKind>(static_cast<int32_t>(std::exchange(value.kind_, facebook::jsi::Value::UndefinedKind))),
-      *reinterpret_cast<uint64_t *>(&value.data_.number)};
-}
+  static facebook::jsi::Value const *AsValue(JsiValueRef const &data) noexcept {
+    return reinterpret_cast<facebook::jsi::Value const *>(&data);
+  }
 
-static std::shared_ptr<facebook::jsi::HostObject> MakeHostObject(
-    Microsoft::ReactNative::IJsiHostObject const &hostObject) {
-  return std::make_shared<HostObjectWrapper>(hostObject);
-}
+  static JsiSymbolRef MakeJsiSymbolData(facebook::jsi::Runtime::PointerValue *pointerValue) {
+    return {reinterpret_cast<uint64_t>(pointerValue)};
+  }
 
-static JsiValueRef const *AsJsiValueData(facebook::jsi::Value const *value) noexcept {
-  return reinterpret_cast<Microsoft::ReactNative::JsiValueRef const *>(value);
-}
+  static JsiStringRef MakeJsiStringData(facebook::jsi::Runtime::PointerValue *pointerValue) {
+    return {reinterpret_cast<uint64_t>(pointerValue)};
+  }
 
-static facebook::jsi::Value &&ToValue(JsiValueRef &&value) noexcept {
-  return reinterpret_cast<facebook::jsi::Value &&>(value);
-}
+  static JsiObjectRef MakeJsiObjectData(facebook::jsi::Runtime::PointerValue *pointerValue) {
+    return {reinterpret_cast<uint64_t>(pointerValue)};
+  }
 
-static facebook::jsi::HostFunctionType MakeHostFunction(
-    Microsoft::ReactNative::JsiHostFunction const &hostFunc,
-    std::shared_ptr<JsiRuntime::HostFunctionCleaner> &&cleaner) {
-  return [hostFunc, cleaner](
-             facebook::jsi::Runtime &runtime,
-             facebook::jsi::Value const &thisVal,
-             facebook::jsi::Value const *args,
-             size_t count) -> facebook::jsi::Value {
-    try {
-      ReactNative::JsiRuntime jsiRuntime = JsiRuntime::FromRuntime(runtime);
-      auto argsData = reinterpret_cast<JsiValueRef const *>(args);
-      return ToValue(hostFunc(jsiRuntime, ToJsiValueData(thisVal), {argsData, argsData + count}));
-    } catch (hresult_error const &) {
-      JsiRuntime::RethrowJsiError(runtime);
-      throw;
-    }
-  };
-}
+  static JsiPropertyIdRef MakeJsiPropertyNameIdData(facebook::jsi::Runtime::PointerValue *pointerValue) {
+    return {reinterpret_cast<uint64_t>(pointerValue)};
+  }
+
+  static std::shared_ptr<facebook::jsi::HostObject> MakeHostObject(
+      Microsoft::ReactNative::IJsiHostObject const &hostObject) {
+    return std::make_shared<HostObjectWrapper>(hostObject);
+  }
+
+  static JsiValueRef const *AsJsiValueData(facebook::jsi::Value const *value) noexcept {
+    return reinterpret_cast<Microsoft::ReactNative::JsiValueRef const *>(value);
+  }
+
+  static facebook::jsi::Value &&ToValue(JsiValueRef &&value) noexcept {
+    return reinterpret_cast<facebook::jsi::Value &&>(value);
+  }
+
+  static facebook::jsi::HostFunctionType MakeHostFunction(
+      Microsoft::ReactNative::JsiHostFunction const &hostFunc,
+      std::shared_ptr<JsiRuntime::HostFunctionCleaner> &&cleaner) {
+    return [hostFunc, cleaner](
+               facebook::jsi::Runtime &runtime,
+               facebook::jsi::Value const &thisVal,
+               facebook::jsi::Value const *args,
+               size_t count) -> facebook::jsi::Value {
+      try {
+        ReactNative::JsiRuntime jsiRuntime = JsiRuntime::FromRuntime(runtime);
+        auto argsData = reinterpret_cast<JsiValueRef const *>(args);
+        return ToValue(hostFunc(jsiRuntime, ValueAccessor::ToJsiValueData(thisVal), {argsData, argsData + count}));
+      } catch (hresult_error const &) {
+        JsiRuntime::RethrowJsiError(runtime);
+        throw;
+      }
+    };
+  }
+};
 
 //===========================================================================
 // HostObjectWrapper implementation
@@ -201,7 +267,7 @@ facebook::jsi::Value HostObjectWrapper::get(
     facebook::jsi::Runtime &runtime,
     facebook::jsi::PropNameID const &name) try {
   ReactNative::JsiRuntime jsiRuntime = JsiRuntime::FromRuntime(runtime);
-  return ToValue(m_hostObject.GetProperty(jsiRuntime, ToJsiPropertyNameIdData(name)));
+  return RuntimeAccessor::ToValue(m_hostObject.GetProperty(jsiRuntime, PointerAccessor::ToJsiPropertyNameIdData(name)));
 } catch (hresult_error const &) {
   JsiRuntime::RethrowJsiError(runtime);
   throw;
@@ -212,7 +278,8 @@ void HostObjectWrapper::set(
     facebook::jsi::PropNameID const &name,
     facebook::jsi::Value const &value) try {
   ReactNative::JsiRuntime jsiRuntime = JsiRuntime::FromRuntime(runtime);
-  m_hostObject.SetProperty(jsiRuntime, ToJsiPropertyNameIdData(name), ToJsiValueData(value));
+  m_hostObject.SetProperty(
+      jsiRuntime, PointerAccessor::ToJsiPropertyNameIdData(name), ValueAccessor::ToJsiValueData(value));
 } catch (hresult_error const &) {
   JsiRuntime::RethrowJsiError(runtime);
   throw;
@@ -229,7 +296,7 @@ std::vector<facebook::jsi::PropNameID> HostObjectWrapper::getPropertyNames(faceb
     names.GetMany(startIndex, nameBuffer);
     size_t bufferLength = std::min(bufferSize, names.Size() - startIndex);
     for (size_t i = 0; i < bufferLength; ++i) {
-      auto ptr = reinterpret_cast<facebook::jsi::Runtime::PointerValue *>(nameBuffer[i].Data);
+      auto ptr = reinterpret_cast<RuntimeAccessor::PointerValue *>(nameBuffer[i].Data);
       result.emplace_back(std::move(*reinterpret_cast<facebook::jsi::PropNameID *>(&ptr)));
     }
   }
@@ -280,7 +347,7 @@ hstring JsiError::Stack() noexcept {
 
 JsiValueRef JsiError::Value() noexcept {
   if (m_errorType == JsiErrorType::JSError) {
-    return ToJsiValueData(m_jsError->value());
+    return ValueAccessor::ToJsiValueData(m_jsError->value());
   } else {
     return {JsiValueKind::Undefined, 0};
   }
@@ -378,7 +445,9 @@ ReactNative::JsiRuntime JsiRuntime::MakeChakraRuntime() {
 JsiRuntime::JsiRuntime(
     std::shared_ptr<facebook::jsi::RuntimeHolderLazyInit> &&runtimeHolder,
     std::shared_ptr<facebook::jsi::Runtime> &&runtime) noexcept
-    : m_runtimeHolder{std::move(runtimeHolder)}, m_runtime{std::move(runtime)} {}
+    : m_runtimeHolder{std::move(runtimeHolder)},
+      m_runtime{std::move(runtime)},
+      m_runtimeAccessor{static_cast<RuntimeAccessor *>(m_runtime.get())} {}
 
 JsiRuntime::~JsiRuntime() noexcept {
   std::scoped_lock lock{s_mutex};
@@ -389,12 +458,12 @@ JsiValueRef JsiRuntime::EvaluateJavaScript(IJsiByteBuffer const &buffer, hstring
   facebook::jsi::Value result;
   buffer.GetData([this, &result, &sourceUrl](array_view<uint8_t const> bytes) {
     try {
-      result = m_runtime->evaluateJavaScript(std::make_shared<JsiBufferWrapper>(bytes), to_string(sourceUrl));
+      result = m_runtimeAccessor->evaluateJavaScript(std::make_shared<JsiBufferWrapper>(bytes), to_string(sourceUrl));
     } catch (JSI_SET_ERROR) {
       throw;
     }
   });
-  return MakeJsiValueData(std::move(result));
+  return ValueAccessor::MakeJsiValueData(std::move(result));
 } catch (JSI_SET_ERROR) {
   throw;
 }
@@ -406,7 +475,7 @@ ReactNative::JsiPreparedJavaScript JsiRuntime::PrepareJavaScript(
   buffer.GetData([this, &result, &sourceUrl](array_view<uint8_t const> bytes) {
     try {
       result = make<JsiPreparedJavaScript>(
-          m_runtime->prepareJavaScript(std::make_shared<JsiBufferWrapper>(bytes), to_string(sourceUrl)));
+          m_runtimeAccessor->prepareJavaScript(std::make_shared<JsiBufferWrapper>(bytes), to_string(sourceUrl)));
     } catch (JSI_SET_ERROR) {
       throw;
     }
@@ -417,86 +486,95 @@ ReactNative::JsiPreparedJavaScript JsiRuntime::PrepareJavaScript(
 }
 
 JsiValueRef JsiRuntime::EvaluatePreparedJavaScript(ReactNative::JsiPreparedJavaScript const &js) try {
-  return MakeJsiValueData(m_runtime->evaluatePreparedJavaScript(get_self<JsiPreparedJavaScript>(js)->Get()));
+  return ValueAccessor::MakeJsiValueData(
+      m_runtimeAccessor->evaluatePreparedJavaScript(get_self<JsiPreparedJavaScript>(js)->Get()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
-JsiObjectRef JsiRuntime::Global() try { return MakeJsiObjectData(m_runtime->global()); } catch (JSI_SET_ERROR) {
+JsiObjectRef JsiRuntime::Global() try {
+  return PointerAccessor::MakeJsiObjectData(m_runtimeAccessor->global());
+} catch (JSI_SET_ERROR) {
   throw;
 }
 
-hstring JsiRuntime::Description() try { return to_hstring(m_runtime->description()); } catch (JSI_SET_ERROR) {
+hstring JsiRuntime::Description() try { return to_hstring(m_runtimeAccessor->description()); } catch (JSI_SET_ERROR) {
   throw;
 }
 
-bool JsiRuntime::IsInspectable() try { return m_runtime->isInspectable(); } catch (JSI_SET_ERROR) {
+bool JsiRuntime::IsInspectable() try { return m_runtimeAccessor->isInspectable(); } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiSymbolRef JsiRuntime::CloneSymbol(JsiSymbolRef symbol) try {
-  return MakeJsiSymbolData(m_runtime->cloneSymbol(AsPointerValue(symbol)));
+  return PointerAccessor::MakeJsiSymbolData(m_runtimeAccessor->make<facebook::jsi::Symbol>(
+      m_runtimeAccessor->cloneSymbol(RuntimeAccessor::AsPointerValue(symbol))));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiStringRef JsiRuntime::CloneString(JsiStringRef str) try {
-  return MakeJsiStringData(m_runtime->cloneString(AsPointerValue(str)));
+  return PointerAccessor::MakeJsiStringData(m_runtimeAccessor->make<facebook::jsi::String>(
+      m_runtimeAccessor->cloneString(RuntimeAccessor::AsPointerValue(str))));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiObjectRef JsiRuntime::CloneObject(JsiObjectRef obj) try {
-  return MakeJsiObjectData(m_runtime->cloneObject(AsPointerValue(obj)));
+  return PointerAccessor::MakeJsiObjectData(m_runtimeAccessor->make<facebook::jsi::Object>(
+      m_runtimeAccessor->cloneObject(RuntimeAccessor::AsPointerValue(obj))));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiPropertyIdRef JsiRuntime::ClonePropertyId(JsiPropertyIdRef propertyNameId) try {
-  return MakeJsiPropertyNameIdData(m_runtime->clonePropNameID(AsPointerValue(propertyNameId)));
+  return PointerAccessor::MakeJsiPropertyNameIdData(m_runtimeAccessor->make<facebook::jsi::PropNameID>(
+      m_runtimeAccessor->clonePropNameID(RuntimeAccessor::AsPointerValue(propertyNameId))));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiPropertyIdRef JsiRuntime::CreatePropertyId(hstring const &name) try {
   std::string utf8Name = to_string(name);
-  return MakeJsiPropertyNameIdData(
-      m_runtime->createPropNameIDFromUtf8(reinterpret_cast<uint8_t const *>(utf8Name.data()), utf8Name.size()));
+  return PointerAccessor::MakeJsiPropertyNameIdData(
+      m_runtimeAccessor->createPropNameIDFromUtf8(reinterpret_cast<uint8_t const *>(utf8Name.data()), utf8Name.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiPropertyIdRef JsiRuntime::CreatePropertyIdFromAscii(array_view<uint8_t const> ascii) try {
-  return MakeJsiPropertyNameIdData(
-      m_runtime->createPropNameIDFromAscii(reinterpret_cast<char const *>(ascii.data()), ascii.size()));
+  return PointerAccessor::MakeJsiPropertyNameIdData(
+      m_runtimeAccessor->createPropNameIDFromAscii(reinterpret_cast<char const *>(ascii.data()), ascii.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiPropertyIdRef JsiRuntime::CreatePropertyIdFromUtf8(array_view<uint8_t const> utf8) try {
-  return MakeJsiPropertyNameIdData(m_runtime->createPropNameIDFromUtf8(utf8.data(), utf8.size()));
+  return PointerAccessor::MakeJsiPropertyNameIdData(
+      m_runtimeAccessor->createPropNameIDFromUtf8(utf8.data(), utf8.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiPropertyIdRef JsiRuntime::CreatePropertyIdFromString(JsiStringRef str) try {
-  auto strPtr = AsPointerValue(str);
-  return MakeJsiPropertyNameIdData(m_runtime->createPropNameIDFromString(AsString(&strPtr)));
+  auto strPtr = RuntimeAccessor::AsPointerValue(str);
+  return PointerAccessor::MakeJsiPropertyNameIdData(
+      m_runtimeAccessor->createPropNameIDFromString(RuntimeAccessor::AsString(&strPtr)));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 hstring JsiRuntime::PropertyIdToString(JsiPropertyIdRef propertyId) try {
-  auto ptr = AsPointerValue(propertyId);
-  std::string utf8 = m_runtime->utf8(AsPropNameID(&ptr));
+  auto ptr = RuntimeAccessor::AsPointerValue(propertyId);
+  std::string utf8 = m_runtimeAccessor->utf8(RuntimeAccessor::AsPropNameID(&ptr));
   return to_hstring(utf8);
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 void JsiRuntime::PropertyIdToUtf8(JsiPropertyIdRef propertyNameId, JsiByteArrayUser const &useUtf8String) try {
-  auto ptr = AsPointerValue(propertyNameId);
-  std::string utf8 = m_runtime->utf8(AsPropNameID(&ptr));
+  auto ptr = RuntimeAccessor::AsPointerValue(propertyNameId);
+  std::string utf8 = m_runtimeAccessor->utf8(RuntimeAccessor::AsPropNameID(&ptr));
   uint8_t const *data = reinterpret_cast<uint8_t const *>(utf8.data());
   useUtf8String({data, data + utf8.size()});
 } catch (JSI_SET_ERROR) {
@@ -504,24 +582,24 @@ void JsiRuntime::PropertyIdToUtf8(JsiPropertyIdRef propertyNameId, JsiByteArrayU
 }
 
 bool JsiRuntime::PropertyIdEquals(JsiPropertyIdRef left, JsiPropertyIdRef right) try {
-  auto leftPtr = AsPointerValue(left);
-  auto rightPtr = AsPointerValue(right);
-  return m_runtime->compare(AsPropNameID(&leftPtr), AsPropNameID(&rightPtr));
+  auto leftPtr = RuntimeAccessor::AsPointerValue(left);
+  auto rightPtr = RuntimeAccessor::AsPointerValue(right);
+  return m_runtimeAccessor->compare(RuntimeAccessor::AsPropNameID(&leftPtr), RuntimeAccessor::AsPropNameID(&rightPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 hstring JsiRuntime::SymbolToString(JsiSymbolRef symbol) try {
-  auto symbolPtr = AsPointerValue(symbol);
-  std::string utf8 = m_runtime->symbolToString(AsSymbol(&symbolPtr));
+  auto symbolPtr = RuntimeAccessor::AsPointerValue(symbol);
+  std::string utf8 = m_runtimeAccessor->symbolToString(RuntimeAccessor::AsSymbol(&symbolPtr));
   return to_hstring(utf8);
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 void JsiRuntime::SymbolToUtf8(JsiSymbolRef symbol, JsiByteArrayUser const &useUtf8String) try {
-  auto symbolPtr = AsPointerValue(symbol);
-  std::string utf8 = m_runtime->symbolToString(AsSymbol(&symbolPtr));
+  auto symbolPtr = RuntimeAccessor::AsPointerValue(symbol);
+  std::string utf8 = m_runtimeAccessor->symbolToString(RuntimeAccessor::AsSymbol(&symbolPtr));
   uint8_t const *data = reinterpret_cast<uint8_t const *>(utf8.data());
   useUtf8String({data, data + utf8.size()});
 } catch (JSI_SET_ERROR) {
@@ -530,35 +608,35 @@ void JsiRuntime::SymbolToUtf8(JsiSymbolRef symbol, JsiByteArrayUser const &useUt
 
 JsiStringRef JsiRuntime::CreateString(hstring const &value) try {
   std::string utf8Value = to_string(value);
-  return MakeJsiStringData(
-      m_runtime->createStringFromUtf8(reinterpret_cast<uint8_t const *>(utf8Value.data()), utf8Value.size()));
+  return PointerAccessor::MakeJsiStringData(
+      m_runtimeAccessor->createStringFromUtf8(reinterpret_cast<uint8_t const *>(utf8Value.data()), utf8Value.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiStringRef JsiRuntime::CreateStringFromAscii(array_view<uint8_t const> ascii) try {
-  return MakeJsiStringData(m_runtime->createStringFromUtf8(ascii.data(), ascii.size()));
+  return PointerAccessor::MakeJsiStringData(m_runtimeAccessor->createStringFromUtf8(ascii.data(), ascii.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiStringRef JsiRuntime::CreateStringFromUtf8(array_view<uint8_t const> utf8) try {
-  return MakeJsiStringData(m_runtime->createStringFromUtf8(utf8.data(), utf8.size()));
+  return PointerAccessor::MakeJsiStringData(m_runtimeAccessor->createStringFromUtf8(utf8.data(), utf8.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 hstring JsiRuntime::StringToString(JsiStringRef str) try {
-  auto strPtr = AsPointerValue(str);
-  std::string utf8 = m_runtime->utf8(AsString(&strPtr));
+  auto strPtr = RuntimeAccessor::AsPointerValue(str);
+  std::string utf8 = m_runtimeAccessor->utf8(RuntimeAccessor::AsString(&strPtr));
   return to_hstring(utf8);
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 void JsiRuntime::StringToUtf8(JsiStringRef str, JsiByteArrayUser const &useUtf8String) try {
-  auto strPtr = AsPointerValue(str);
-  std::string utf8 = m_runtime->utf8(AsString(&strPtr));
+  auto strPtr = RuntimeAccessor::AsPointerValue(str);
+  std::string utf8 = m_runtimeAccessor->utf8(RuntimeAccessor::AsString(&strPtr));
   uint8_t const *data = reinterpret_cast<uint8_t const *>(utf8.data());
   useUtf8String({data, data + utf8.size()});
 } catch (JSI_SET_ERROR) {
@@ -567,33 +645,34 @@ void JsiRuntime::StringToUtf8(JsiStringRef str, JsiByteArrayUser const &useUtf8S
 
 JsiValueRef JsiRuntime::CreateValueFromJson(hstring const &json) try {
   std::string utf8Json = to_string(json);
-  return MakeJsiValueData(
-      m_runtime->createValueFromJsonUtf8(reinterpret_cast<uint8_t const *>(utf8Json.data()), utf8Json.size()));
+  return ValueAccessor::MakeJsiValueData(
+      m_runtimeAccessor->createValueFromJsonUtf8(reinterpret_cast<uint8_t const *>(utf8Json.data()), utf8Json.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiValueRef JsiRuntime::CreateValueFromJsonUtf8(array_view<uint8_t const> json) try {
-  return MakeJsiValueData(m_runtime->createValueFromJsonUtf8(json.data(), json.size()));
+  return ValueAccessor::MakeJsiValueData(m_runtimeAccessor->createValueFromJsonUtf8(json.data(), json.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiObjectRef JsiRuntime::CreateObject() try {
-  return MakeJsiObjectData(m_runtime->createObject());
+  return PointerAccessor::MakeJsiObjectData(m_runtimeAccessor->createObject());
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiObjectRef JsiRuntime::CreateObjectWithHostObject(IJsiHostObject const &hostObject) try {
-  return MakeJsiObjectData(m_runtime->createObject(MakeHostObject(hostObject)));
+  return PointerAccessor::MakeJsiObjectData(
+      m_runtimeAccessor->createObject(RuntimeAccessor::MakeHostObject(hostObject)));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 IJsiHostObject JsiRuntime::GetHostObject(JsiObjectRef obj) try {
-  auto objPtr = AsPointerValue(obj);
-  auto hostObject = m_runtime->getHostObject(AsObject(&objPtr));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  auto hostObject = m_runtimeAccessor->getHostObject(RuntimeAccessor::AsObject(&objPtr));
   // TODO: using static_pointer_cast is unsafe here. How to use shared_ptr without RTTI?
   auto wrapper = std::static_pointer_cast<HostObjectWrapper>(hostObject);
   return wrapper ? wrapper->Get() : nullptr;
@@ -602,8 +681,8 @@ IJsiHostObject JsiRuntime::GetHostObject(JsiObjectRef obj) try {
 }
 
 JsiHostFunction JsiRuntime::GetHostFunction(JsiObjectRef func) try {
-  auto funcPtr = AsPointerValue(func);
-  auto const &jsiFunc = AsFunction(&funcPtr);
+  auto funcPtr = RuntimeAccessor::AsPointerValue(func);
+  auto const &jsiFunc = RuntimeAccessor::AsFunction(&funcPtr);
   auto &rt = *m_runtime;
   int64_t hostFunctionId = static_cast<int64_t>(rt.global()
                                                     .getPropertyAsObject(rt, "__jsi_pal")
@@ -623,124 +702,131 @@ JsiHostFunction JsiRuntime::GetHostFunction(JsiObjectRef func) try {
 }
 
 JsiValueRef JsiRuntime::GetProperty(JsiObjectRef obj, JsiPropertyIdRef propertyId) try {
-  auto objPtr = AsPointerValue(obj);
-  auto propertyIdPtr = AsPointerValue(propertyId);
-  return MakeJsiValueData(m_runtime->getProperty(AsObject(&objPtr), AsPropNameID(&propertyIdPtr)));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  auto propertyIdPtr = RuntimeAccessor::AsPointerValue(propertyId);
+  return ValueAccessor::MakeJsiValueData(m_runtimeAccessor->getProperty(
+      RuntimeAccessor::AsObject(&objPtr), RuntimeAccessor::AsPropNameID(&propertyIdPtr)));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::HasProperty(JsiObjectRef obj, JsiPropertyIdRef propertyId) try {
-  auto objPtr = AsPointerValue(obj);
-  auto propertyIdPtr = AsPointerValue(propertyId);
-  return m_runtime->hasProperty(AsObject(&objPtr), AsPropNameID(&propertyIdPtr));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  auto propertyIdPtr = RuntimeAccessor::AsPointerValue(propertyId);
+  return m_runtimeAccessor->hasProperty(
+      RuntimeAccessor::AsObject(&objPtr), RuntimeAccessor::AsPropNameID(&propertyIdPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 void JsiRuntime::SetProperty(JsiObjectRef obj, JsiPropertyIdRef propertyId, JsiValueRef const &value) try {
-  auto objPtr = AsPointerValue(obj);
-  auto propertyIdPtr = AsPointerValue(propertyId);
-  m_runtime->setPropertyValue(
-      const_cast<facebook::jsi::Object &>(AsObject(&objPtr)), AsPropNameID(&propertyIdPtr), *AsValue(value));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  auto propertyIdPtr = RuntimeAccessor::AsPointerValue(propertyId);
+  m_runtimeAccessor->setPropertyValue(
+      const_cast<facebook::jsi::Object &>(RuntimeAccessor::AsObject(&objPtr)),
+      RuntimeAccessor::AsPropNameID(&propertyIdPtr),
+      *RuntimeAccessor::AsValue(value));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiObjectRef JsiRuntime::GetPropertyIdArray(JsiObjectRef obj) try {
-  auto objPtr = AsPointerValue(obj);
-  return MakeJsiArrayData(m_runtime->getPropertyNames(AsObject(&objPtr)));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  return PointerAccessor::MakeJsiArrayData(m_runtimeAccessor->getPropertyNames(RuntimeAccessor::AsObject(&objPtr)));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::IsArray(JsiObjectRef obj) try {
-  auto objPtr = AsPointerValue(obj);
-  return m_runtime->isArray(AsObject(&objPtr));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  return m_runtimeAccessor->isArray(RuntimeAccessor::AsObject(&objPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::IsArrayBuffer(JsiObjectRef obj) try {
-  auto objPtr = AsPointerValue(obj);
-  return m_runtime->isArrayBuffer(AsObject(&objPtr));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  return m_runtimeAccessor->isArrayBuffer(RuntimeAccessor::AsObject(&objPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::IsFunction(JsiObjectRef obj) try {
-  auto objPtr = AsPointerValue(obj);
-  return m_runtime->isFunction(AsObject(&objPtr));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  return m_runtimeAccessor->isFunction(RuntimeAccessor::AsObject(&objPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::IsHostObject(JsiObjectRef obj) try {
-  auto objPtr = AsPointerValue(obj);
-  return m_runtime->isHostObject(AsObject(&objPtr));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  return m_runtimeAccessor->isHostObject(RuntimeAccessor::AsObject(&objPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::IsHostFunction(JsiObjectRef func) try {
-  auto funcPtr = AsPointerValue(func);
-  return m_runtime->isHostFunction(AsFunction(&funcPtr));
+  auto funcPtr = RuntimeAccessor::AsPointerValue(func);
+  return m_runtimeAccessor->isHostFunction(RuntimeAccessor::AsFunction(&funcPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiWeakObjectRef JsiRuntime::CreateWeakObject(JsiObjectRef obj) try {
-  auto objPtr = AsPointerValue(obj);
-  return MakeJsiWeakObjectData(m_runtime->createWeakObject(AsObject(&objPtr)));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  return PointerAccessor::MakeJsiWeakObjectData(
+      m_runtimeAccessor->createWeakObject(RuntimeAccessor::AsObject(&objPtr)));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiValueRef JsiRuntime::LockWeakObject(JsiWeakObjectRef weakObject) try {
-  auto weakObjectPtr = reinterpret_cast<facebook::jsi::Runtime::PointerValue *>(weakObject.Data);
-  return MakeJsiValueData(m_runtime->lockWeakObject(AsWeakObject(&weakObjectPtr)));
+  auto weakObjectPtr = const_cast<RuntimeAccessor::PointerValue *>(RuntimeAccessor::AsPointerValue(weakObject));
+  return ValueAccessor::MakeJsiValueData(
+      m_runtimeAccessor->lockWeakObject(RuntimeAccessor::AsWeakObject(&weakObjectPtr)));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiObjectRef JsiRuntime::CreateArray(uint32_t size) try {
-  return MakeJsiArrayData(m_runtime->createArray(size));
+  return PointerAccessor::MakeJsiArrayData(m_runtimeAccessor->createArray(size));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 uint32_t JsiRuntime::GetArraySize(JsiObjectRef arr) try {
-  auto arrPtr = AsPointerValue(arr);
-  return static_cast<uint32_t>(m_runtime->size(AsArray(&arrPtr)));
+  auto arrPtr = RuntimeAccessor::AsPointerValue(arr);
+  return static_cast<uint32_t>(m_runtimeAccessor->size(RuntimeAccessor::AsArray(&arrPtr)));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 uint32_t JsiRuntime::GetArrayBufferSize(JsiObjectRef arrayBuffer) try {
-  auto arrayBufferPtr = AsPointerValue(arrayBuffer);
-  return static_cast<uint32_t>(m_runtime->size(AsArrayBuffer(&arrayBufferPtr)));
+  auto arrayBufferPtr = RuntimeAccessor::AsPointerValue(arrayBuffer);
+  return static_cast<uint32_t>(m_runtimeAccessor->size(RuntimeAccessor::AsArrayBuffer(&arrayBufferPtr)));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 void JsiRuntime::GetArrayBufferData(JsiObjectRef arrayBuffer, JsiByteArrayUser const &useArrayBytes) try {
-  auto arrayBufferPtr = AsPointerValue(arrayBuffer);
-  auto data = m_runtime->data(AsArrayBuffer(&arrayBufferPtr));
-  return useArrayBytes({data, data + m_runtime->size(AsArrayBuffer(&arrayBufferPtr))});
+  auto arrayBufferPtr = RuntimeAccessor::AsPointerValue(arrayBuffer);
+  auto data = m_runtimeAccessor->data(RuntimeAccessor::AsArrayBuffer(&arrayBufferPtr));
+  return useArrayBytes({data, data + m_runtimeAccessor->size(RuntimeAccessor::AsArrayBuffer(&arrayBufferPtr))});
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiValueRef JsiRuntime::GetValueAtIndex(JsiObjectRef arr, uint32_t index) try {
-  auto arrPtr = AsPointerValue(arr);
-  return MakeJsiValueData(AsArray(&arrPtr).getValueAtIndex(*m_runtime, index));
+  auto arrPtr = RuntimeAccessor::AsPointerValue(arr);
+  return ValueAccessor::MakeJsiValueData(RuntimeAccessor::AsArray(&arrPtr).getValueAtIndex(*m_runtime, index));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 void JsiRuntime::SetValueAtIndex(JsiObjectRef arr, uint32_t index, JsiValueRef const &value) try {
-  auto arrPtr = AsPointerValue(arr);
-  m_runtime->setValueAtIndexImpl(const_cast<facebook::jsi::Array &>(AsArray(&arrPtr)), index, *AsValue(value));
+  auto arrPtr = RuntimeAccessor::AsPointerValue(arr);
+  m_runtimeAccessor->setValueAtIndexImpl(
+      const_cast<facebook::jsi::Array &>(RuntimeAccessor::AsArray(&arrPtr)), index, *RuntimeAccessor::AsValue(value));
 } catch (JSI_SET_ERROR) {
   throw;
 }
@@ -763,24 +849,26 @@ JsiObjectRef JsiRuntime::CreateFunctionFromHostFunction(
     s_jsiHostFunctionMap[hostFunctionId] = hostFunc;
   }
   auto cleaner = std::make_shared<HostFunctionCleaner>(hostFunctionId);
-  auto &rt = *m_runtime;
-  auto propertyIdPtr = AsPointerValue(propNameId);
+  auto &rt = *m_runtimeAccessor;
+  auto propertyIdPtr = RuntimeAccessor::AsPointerValue(propNameId);
   auto func = rt.createFunctionFromHostFunction(
-      AsPropNameID(&propertyIdPtr), paramCount, MakeHostFunction(hostFunc, std::move(cleaner)));
+      RuntimeAccessor::AsPropNameID(&propertyIdPtr),
+      paramCount,
+      RuntimeAccessor::MakeHostFunction(hostFunc, std::move(cleaner)));
   rt.global()
       .getPropertyAsObject(rt, "__jsi_pal")
       .getPropertyAsFunction(rt, "setHostFunctionId")
       .call(rt, func, static_cast<double>(hostFunctionId));
-  return MakeJsiFunctionData(std::move(func));
+  return PointerAccessor::MakeJsiFunctionData(std::move(func));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiValueRef JsiRuntime::Call(JsiObjectRef func, JsiValueRef const &thisArg, array_view<JsiValueRef const> args) try {
-  auto funcPtr = AsPointerValue(func);
-  return MakeJsiValueData(m_runtime->call(
-      AsFunction(&funcPtr),
-      *AsValue(thisArg),
+  auto funcPtr = RuntimeAccessor::AsPointerValue(func);
+  return ValueAccessor::MakeJsiValueData(m_runtimeAccessor->call(
+      RuntimeAccessor::AsFunction(&funcPtr),
+      *RuntimeAccessor::AsValue(thisArg),
       reinterpret_cast<facebook::jsi::Value const *>(args.data()),
       args.size()));
 } catch (JSI_SET_ERROR) {
@@ -788,71 +876,75 @@ JsiValueRef JsiRuntime::Call(JsiObjectRef func, JsiValueRef const &thisArg, arra
 }
 
 JsiValueRef JsiRuntime::CallAsConstructor(JsiObjectRef func, array_view<JsiValueRef const> args) try {
-  auto funcPtr = AsPointerValue(func);
-  return MakeJsiValueData(m_runtime->callAsConstructor(
-      AsFunction(&funcPtr), reinterpret_cast<facebook::jsi::Value const *>(args.data()), args.size()));
+  auto funcPtr = RuntimeAccessor::AsPointerValue(func);
+  return ValueAccessor::MakeJsiValueData(m_runtimeAccessor->callAsConstructor(
+      RuntimeAccessor::AsFunction(&funcPtr), reinterpret_cast<facebook::jsi::Value const *>(args.data()), args.size()));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 JsiScopeState JsiRuntime::PushScope() try {
-  return {reinterpret_cast<uint64_t>(m_runtime->pushScope())};
+  return {reinterpret_cast<uint64_t>(m_runtimeAccessor->pushScope())};
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 void JsiRuntime::PopScope(JsiScopeState scopeState) try {
-  m_runtime->popScope(reinterpret_cast<facebook::jsi::Runtime::ScopeState *>(scopeState.Data));
+  m_runtimeAccessor->popScope(reinterpret_cast<RuntimeAccessor::ScopeState *>(scopeState.Data));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::SymbolStrictEquals(JsiSymbolRef left, JsiSymbolRef right) try {
-  auto leftPtr = AsPointerValue(left);
-  auto rightPtr = AsPointerValue(right);
-  return m_runtime->strictEquals(AsSymbol(&leftPtr), AsSymbol(&rightPtr));
+  auto leftPtr = RuntimeAccessor::AsPointerValue(left);
+  auto rightPtr = RuntimeAccessor::AsPointerValue(right);
+  return m_runtimeAccessor->strictEquals(RuntimeAccessor::AsSymbol(&leftPtr), RuntimeAccessor::AsSymbol(&rightPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::StringStrictEquals(JsiStringRef left, JsiStringRef right) try {
-  auto leftPtr = AsPointerValue(left);
-  auto rightPtr = AsPointerValue(right);
-  return m_runtime->strictEquals(AsString(&leftPtr), AsString(&rightPtr));
+  auto leftPtr = RuntimeAccessor::AsPointerValue(left);
+  auto rightPtr = RuntimeAccessor::AsPointerValue(right);
+  return m_runtimeAccessor->strictEquals(RuntimeAccessor::AsString(&leftPtr), RuntimeAccessor::AsString(&rightPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::ObjectStrictEquals(JsiObjectRef left, JsiObjectRef right) try {
-  auto leftPtr = AsPointerValue(left);
-  auto rightPtr = AsPointerValue(right);
-  return m_runtime->strictEquals(AsObject(&leftPtr), AsObject(&rightPtr));
+  auto leftPtr = RuntimeAccessor::AsPointerValue(left);
+  auto rightPtr = RuntimeAccessor::AsPointerValue(right);
+  return m_runtimeAccessor->strictEquals(RuntimeAccessor::AsObject(&leftPtr), RuntimeAccessor::AsObject(&rightPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 bool JsiRuntime::InstanceOf(JsiObjectRef obj, JsiObjectRef constructor) try {
-  auto objPtr = AsPointerValue(obj);
-  auto ctorPtr = AsPointerValue(constructor);
-  return m_runtime->instanceOf(AsObject(&objPtr), AsFunction(&ctorPtr));
+  auto objPtr = RuntimeAccessor::AsPointerValue(obj);
+  auto ctorPtr = RuntimeAccessor::AsPointerValue(constructor);
+  return m_runtimeAccessor->instanceOf(RuntimeAccessor::AsObject(&objPtr), RuntimeAccessor::AsFunction(&ctorPtr));
 } catch (JSI_SET_ERROR) {
   throw;
 }
 
 void JsiRuntime::ReleaseSymbol(JsiSymbolRef const &symbolData) {
-  facebook::jsi::Symbol symbol{reinterpret_cast<facebook::jsi::Runtime::PointerValue *>(symbolData.Data)};
+  auto symbol =
+      RuntimeAccessor::make<facebook::jsi::Symbol>(reinterpret_cast<RuntimeAccessor::PointerValue *>(symbolData.Data));
 }
 
 void JsiRuntime::ReleaseString(JsiStringRef const &stringData) {
-  facebook::jsi::String str{reinterpret_cast<facebook::jsi::Runtime::PointerValue *>(stringData.Data)};
+  auto str =
+      RuntimeAccessor::make<facebook::jsi::String>(reinterpret_cast<RuntimeAccessor::PointerValue *>(stringData.Data));
 }
 
 void JsiRuntime::ReleaseObject(JsiObjectRef const &objectData) {
-  facebook::jsi::Object obj{reinterpret_cast<facebook::jsi::Runtime::PointerValue *>(objectData.Data)};
+  auto obj =
+      RuntimeAccessor::make<facebook::jsi::Object>(reinterpret_cast<RuntimeAccessor::PointerValue *>(objectData.Data));
 }
 
 void JsiRuntime::ReleasePropertyId(JsiPropertyIdRef const &propertyNameIdData) {
-  facebook::jsi::PropNameID prop{reinterpret_cast<facebook::jsi::Runtime::PointerValue *>(propertyNameIdData.Data)};
+  auto prop = RuntimeAccessor::make<facebook::jsi::PropNameID>(
+      reinterpret_cast<RuntimeAccessor::PointerValue *>(propertyNameIdData.Data));
 }
 
 ReactNative::JsiError JsiRuntime::GetAndClearError() noexcept {
@@ -864,8 +956,8 @@ ReactNative::JsiError JsiRuntime::GetAndClearError() noexcept {
 void JsiRuntime::SetError(JsiErrorType errorType, hstring const &errorDetails, JsiValueRef const &value) noexcept {
   std::scoped_lock lock{m_mutex};
   if (errorType == JsiErrorType::JSError) {
-    m_error = make<JsiError>(
-        facebook::jsi::JSError{to_string(errorDetails), *m_runtime, facebook::jsi::Value{*m_runtime, *AsValue(value)}});
+    m_error = make<JsiError>(facebook::jsi::JSError{
+        to_string(errorDetails), *m_runtime, facebook::jsi::Value{*m_runtime, *RuntimeAccessor::AsValue(value)}});
   } else {
     m_error = make<JsiError>(facebook::jsi::JSINativeException{to_string(errorDetails)});
   }
diff --git a/vnext/Microsoft.ReactNative/JsiApi.h b/vnext/Microsoft.ReactNative/JsiApi.h
index 4a34a000403..fe90ef6d33e 100644
--- a/vnext/Microsoft.ReactNative/JsiApi.h
+++ b/vnext/Microsoft.ReactNative/JsiApi.h
@@ -9,18 +9,6 @@
 #include <unordered_map>
 #include "winrt/Microsoft.ReactNative.h"
 
-// facebook::jsi::Runtime hides all methods that we need to call.
-// We "open" them up here by redeclaring the private and protected keywords.
-#pragma push_macro("private")
-#pragma push_macro("protected")
-#define private public
-#define protected public
-#include "jsi/jsi.h"
-#undef protected
-#undef private
-#pragma pop_macro("protected")
-#pragma pop_macro("private")
-
 #include "ChakraRuntimeHolder.h"
 
 namespace facebook::jsi {
@@ -50,6 +38,24 @@ struct JsiError : JsiErrorT<JsiError> {
   std::optional<facebook::jsi::JSINativeException> const m_nativeException;
 };
 
+// Wraps up the IJsiHostObject
+struct HostObjectWrapper final : facebook::jsi::HostObject {
+  HostObjectWrapper(Microsoft::ReactNative::IJsiHostObject const &hostObject) noexcept;
+
+  facebook::jsi::Value get(facebook::jsi::Runtime &runtime, const facebook::jsi::PropNameID &name) override;
+  void set(facebook::jsi::Runtime &, const facebook::jsi::PropNameID &name, const facebook::jsi::Value &value) override;
+  std::vector<facebook::jsi::PropNameID> getPropertyNames(facebook::jsi::Runtime &runtime) override;
+
+  Microsoft::ReactNative::IJsiHostObject const &Get() const noexcept {
+    return m_hostObject;
+  }
+
+ private:
+  Microsoft::ReactNative::IJsiHostObject m_hostObject;
+};
+
+struct RuntimeAccessor;
+
 struct JsiRuntime : JsiRuntimeT<JsiRuntime> {
   JsiRuntime(
       std::shared_ptr<facebook::jsi::RuntimeHolderLazyInit> &&runtimeHolder,
@@ -163,6 +169,7 @@ struct JsiRuntime : JsiRuntimeT<JsiRuntime> {
  private:
   std::shared_ptr<facebook::jsi::RuntimeHolderLazyInit> m_runtimeHolder;
   std::shared_ptr<facebook::jsi::Runtime> m_runtime;
+  RuntimeAccessor *m_runtimeAccessor{};
   std::mutex m_mutex;
   ReactNative::JsiError m_error{nullptr};
 
diff --git a/vnext/Microsoft.ReactNative/ReactHost/React.h b/vnext/Microsoft.ReactNative/ReactHost/React.h
index 25c81398a3a..33358b0c03b 100644
--- a/vnext/Microsoft.ReactNative/ReactHost/React.h
+++ b/vnext/Microsoft.ReactNative/ReactHost/React.h
@@ -22,7 +22,12 @@
 #undef GetCurrentTime
 #endif
 
+#ifndef CORE_ABI
+// The IReactInstance.h brings dependency on XAML. Exclude it for the UI technology independent code.
 #include <IReactInstance.h>
+#endif
+
+#include <Shared/IReactRootView.h>
 
 #include <ViewManagerProvider.h>
 #include <winrt/Microsoft.ReactNative.h>
diff --git a/vnext/Microsoft.ReactNative/TurboModulesProvider.cpp b/vnext/Microsoft.ReactNative/TurboModulesProvider.cpp
index 1fcfcda679d..7264457db46 100644
--- a/vnext/Microsoft.ReactNative/TurboModulesProvider.cpp
+++ b/vnext/Microsoft.ReactNative/TurboModulesProvider.cpp
@@ -7,6 +7,7 @@
 #include "pch.h"
 #include "TurboModulesProvider.h"
 #include <ReactCommon/TurboModuleUtils.h>
+#include "JsiApi.h"
 #include "JsiReader.h"
 #include "JsiWriter.h"
 #ifdef __APPLE__
@@ -85,9 +86,16 @@ class TurboModuleImpl : public facebook::react::TurboModule {
       ReactModuleProvider reactModuleProvider)
       : facebook::react::TurboModule(name, jsInvoker), m_moduleBuilder(winrt::make<TurboModuleBuilder>(reactContext)) {
     providedModule = reactModuleProvider(m_moduleBuilder);
+    if (auto hostObject = providedModule.try_as<IJsiHostObject>()) {
+      m_hostObjectWrapper = std::make_shared<implementation::HostObjectWrapper>(hostObject);
+    }
   }
 
   std::vector<facebook::jsi::PropNameID> getPropertyNames(facebook::jsi::Runtime &rt) override {
+    if (m_hostObjectWrapper) {
+      return m_hostObjectWrapper->getPropertyNames(rt);
+    }
+
     std::vector<facebook::jsi::PropNameID> props;
     auto tmb = m_moduleBuilder.as<TurboModuleBuilder>();
     for (auto &it : tmb->m_methods) {
@@ -97,6 +105,10 @@ class TurboModuleImpl : public facebook::react::TurboModule {
   };
 
   facebook::jsi::Value get(facebook::jsi::Runtime &runtime, const facebook::jsi::PropNameID &propName) override {
+    if (m_hostObjectWrapper) {
+      return m_hostObjectWrapper->get(runtime, propName);
+    }
+
     // it is not safe to assume that "runtime" never changes, so members are not cached here
     auto tmb = m_moduleBuilder.as<TurboModuleBuilder>();
     auto key = propName.utf8(runtime);
@@ -277,9 +289,19 @@ class TurboModuleImpl : public facebook::react::TurboModule {
     return facebook::jsi::Value::undefined();
   }
 
+  void set(facebook::jsi::Runtime &rt, const facebook::jsi::PropNameID &name, const facebook::jsi::Value &value)
+      override {
+    if (m_hostObjectWrapper) {
+      return m_hostObjectWrapper->set(rt, name, value);
+    }
+
+    facebook::react::TurboModule::set(rt, name, value);
+  }
+
  private:
   IReactModuleBuilder m_moduleBuilder;
   IInspectable providedModule;
+  std::shared_ptr<implementation::HostObjectWrapper> m_hostObjectWrapper;
 };
 
 /*-------------------------------------------------------------------------------
diff --git a/vnext/Scripts/Tfs/Layout-Headers.ps1 b/vnext/Scripts/Tfs/Layout-Headers.ps1
index 0674e131dc9..5092a9adca4 100644
--- a/vnext/Scripts/Tfs/Layout-Headers.ps1
+++ b/vnext/Scripts/Tfs/Layout-Headers.ps1
@@ -98,6 +98,13 @@ Copy-Item -Force -Path $ReactNativeRoot\ReactCommon\jsi\jsi\jsi.h -Destination $
 Copy-Item -Force -Path $ReactNativeRoot\ReactCommon\jsi\jsi\jsi-inl.h -Destination $TargetRoot\Microsoft.ReactNative.Cxx\jsi\
 Copy-Item -Force -Path $ReactNativeRoot\ReactCommon\jsi\jsi\threadsafe.h -Destination $TargetRoot\Microsoft.ReactNative.Cxx\jsi\
 
+# Microsoft.ReactNative.CXX project TurboModule files
+Copy-Item -Force -Path $ReactNativeRoot\ReactCommon\callinvoker\ReactCommon\CallInvoker.h -Destination $TargetRoot\Microsoft.ReactNative.Cxx\ReactCommon\
+Copy-Item -Force -Path $ReactNativeRoot\ReactCommon\react\nativemodule\core\ReactCommon\LongLivedObject.cpp -Destination $TargetRoot\Microsoft.ReactNative.Cxx\ReactCommon\
+Copy-Item -Force -Path $ReactNativeRoot\ReactCommon\react\nativemodule\core\ReactCommon\LongLivedObject.h -Destination $TargetRoot\Microsoft.ReactNative.Cxx\ReactCommon\
+Copy-Item -Force -Path $ReactNativeRoot\ReactCommon\react\nativemodule\core\ReactCommon\TurboModule.cpp -Destination $TargetRoot\Microsoft.ReactNative.Cxx\ReactCommon\
+Copy-Item -Force -Path $ReactNativeRoot\ReactCommon\react\nativemodule\core\ReactCommon\TurboModule.h -Destination $TargetRoot\Microsoft.ReactNative.Cxx\ReactCommon\
+
 # NUSPEC
 Copy-Item -Force -Path $ReactWindowsRoot\Scripts\*.nuspec -Destination $TargetRoot
 
