diff --git a/change/react-native-windows-8f4cd7f3-9cd6-4681-8d63-b378c912d67d.json b/change/react-native-windows-8f4cd7f3-9cd6-4681-8d63-b378c912d67d.json
new file mode 100644
index 00000000000..e3a57944058
--- /dev/null
+++ b/change/react-native-windows-8f4cd7f3-9cd6-4681-8d63-b378c912d67d.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Support PreparedScriptStore for V8 Node-API.",
+ "packageName": "react-native-windows",
+ "email": "vmorozov@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/vnext/Desktop.DLL/packages.config b/vnext/Desktop.DLL/packages.config
index f79693a48f3..6d348202643 100644
--- a/vnext/Desktop.DLL/packages.config
+++ b/vnext/Desktop.DLL/packages.config
@@ -4,6 +4,6 @@
-
+
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative.Cxx.UnitTests/packages.config b/vnext/Microsoft.ReactNative.Cxx.UnitTests/packages.config
index 8863241a0b0..08540709f33 100644
--- a/vnext/Microsoft.ReactNative.Cxx.UnitTests/packages.config
+++ b/vnext/Microsoft.ReactNative.Cxx.UnitTests/packages.config
@@ -2,5 +2,5 @@
-
+
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp b/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp
index 610e22536ee..e30162d9a06 100644
--- a/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp
+++ b/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp
@@ -440,6 +440,7 @@ struct NapiJsiRuntime : facebook::jsi::Runtime {
private: // Shared NAPI call helpers
napi_value RunScript(napi_value script, const char *sourceUrl);
+ napi_value RunScriptBuffer(const std::shared_ptr &buffer, const char *sourceUrl);
std::vector SerializeScript(napi_value script, const char *sourceUrl);
napi_value RunSerializedScript(span serialized, napi_value source, const char *sourceUrl);
napi_ext_ref CreateReference(napi_value value) const;
@@ -620,9 +621,7 @@ NapiJsiRuntime::NapiJsiRuntime(napi_env env) noexcept : m_env{env} {
Value NapiJsiRuntime::evaluateJavaScript(const shared_ptr &buffer, const string &sourceUrl) {
EnvScope envScope{m_env};
- napi_value script = CreateStringUtf8(buffer->data(), buffer->size());
- napi_value result = RunScript(script, sourceUrl.c_str());
-
+ napi_value result = RunScriptBuffer(buffer, sourceUrl.c_str());
return ToJsiValue(result);
}
@@ -1406,6 +1405,21 @@ napi_value NapiJsiRuntime::RunScript(napi_value script, const char *sourceUrl) {
return result;
}
+napi_value NapiJsiRuntime::RunScriptBuffer(
+ const std::shared_ptr &buffer,
+ const char *sourceUrl) {
+ napi_ext_buffer napiBuffer{};
+ napiBuffer.buffer_object = NativeObjectWrapper>::Wrap(
+ std::shared_ptr{buffer});
+ napiBuffer.data = buffer->data();
+ napiBuffer.byte_size = buffer->size();
+
+ napi_value result{};
+ CHECK_NAPI(napi_ext_run_script_buffer(m_env, &napiBuffer, sourceUrl, &result));
+
+ return result;
+}
+
// Serializes script with the sourceUrl origin.
vector NapiJsiRuntime::SerializeScript(napi_value script, const char *sourceUrl) {
vector result;
diff --git a/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.h b/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.h
index b2ea468b7ac..f7c48de83ea 100644
--- a/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.h
+++ b/vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.h
@@ -20,6 +20,54 @@ namespace Microsoft::JSI {
///
std::unique_ptr __cdecl MakeNodeApiJsiRuntime(napi_env env) noexcept;
+template
+struct NativeObjectWrapper;
+
+template
+struct NativeObjectWrapper> {
+ static napi_ext_native_data Wrap(std::unique_ptr &&obj) noexcept {
+ napi_ext_native_data nativeData{};
+ nativeData.data = obj.release();
+ nativeData.finalize_cb = [](napi_env /*env*/, void *data, void * /*finalizeHint*/) {
+ std::unique_ptr obj{reinterpret_cast(data)};
+ };
+ return nativeData;
+ }
+
+ static T *Unwrap(napi_ext_native_data &nativeData) noexcept {
+ return reinterpret_cast(nativeData.data);
+ }
+};
+
+template
+struct NativeObjectWrapper> {
+ static napi_ext_native_data Wrap(std::shared_ptr &&obj) noexcept {
+ static_assert(
+ sizeof(SharedPtrHolder) == sizeof(std::shared_ptr), "std::shared_ptr expected to have size of two pointers");
+ SharedPtrHolder ptrHolder;
+ new (std::addressof(ptrHolder)) std::shared_ptr(std::move(obj));
+ napi_ext_native_data nativeData{};
+ nativeData.data = ptrHolder.ptr1;
+ nativeData.finalize_hint = ptrHolder.ptr2;
+ nativeData.finalize_cb = [](napi_env /*env*/, void *data, void *finalizeHint) {
+ SharedPtrHolder ptrHolder{data, finalizeHint};
+ std::shared_ptr obj(std::move(*reinterpret_cast *>(std::addressof(ptrHolder))));
+ };
+ return nativeData;
+ }
+
+ static std::shared_ptr Unwrap(napi_ext_native_data &nativeData) noexcept {
+ SharedPtrHolder ptrHolder{nativeData.data, nativeData.finalize_hint};
+ return *reinterpret_cast *>(std::addressof(ptrHolder));
+ }
+
+ private:
+ struct SharedPtrHolder {
+ void *ptr1;
+ void *ptr2;
+ };
+};
+
} // namespace Microsoft::JSI
#endif // MICROSOFT_REACTNATIVE_JSI_NODEAPIJSIRUNTIME
diff --git a/vnext/Microsoft.ReactNative/packages.config b/vnext/Microsoft.ReactNative/packages.config
index 51ac12946f8..36af6649038 100644
--- a/vnext/Microsoft.ReactNative/packages.config
+++ b/vnext/Microsoft.ReactNative/packages.config
@@ -8,5 +8,5 @@
-
+
\ No newline at end of file
diff --git a/vnext/PropertySheets/JSEngine.props b/vnext/PropertySheets/JSEngine.props
index 6f56cef839c..3e5f9e8c812 100644
--- a/vnext/PropertySheets/JSEngine.props
+++ b/vnext/PropertySheets/JSEngine.props
@@ -16,7 +16,7 @@
false
false
- 0.65.11
+ 0.65.15
ReactNative.V8Jsi.Windows
$(V8PackageName).UWP
$(SolutionDir)packages\$(V8PackageName).$(V8Version)
diff --git a/vnext/ReactCommon.UnitTests/packages.config b/vnext/ReactCommon.UnitTests/packages.config
index 31f8193b1e4..3d3af1eb666 100644
--- a/vnext/ReactCommon.UnitTests/packages.config
+++ b/vnext/ReactCommon.UnitTests/packages.config
@@ -2,5 +2,5 @@
-
+
\ No newline at end of file
diff --git a/vnext/Shared/JSI/NapiJsiV8RuntimeHolder.cpp b/vnext/Shared/JSI/NapiJsiV8RuntimeHolder.cpp
index 3f4a2cf6066..8c0255aa326 100644
--- a/vnext/Shared/JSI/NapiJsiV8RuntimeHolder.cpp
+++ b/vnext/Shared/JSI/NapiJsiV8RuntimeHolder.cpp
@@ -77,17 +77,20 @@ NapiJsiV8RuntimeHolder::NapiJsiV8RuntimeHolder(
m_preparedScriptStore{std::move(preparedScriptStore)} {}
void NapiJsiV8RuntimeHolder::InitRuntime() noexcept {
- napi_env env{};
napi_ext_env_settings settings{};
settings.this_size = sizeof(settings);
- settings.flags.enable_gc_api = true;
if (m_debuggerPort > 0)
settings.inspector_port = m_debuggerPort;
settings.flags.enable_inspector = m_useDirectDebugger;
settings.flags.wait_for_debugger = m_debuggerBreakOnNextLine;
+ // TODO: args.debuggerRuntimeName = debuggerRuntimeName_;
settings.foreground_scheduler = &NapiJsiV8RuntimeHolder::ScheduleTaskCallback;
+ napi_ext_script_cache scriptCache = InitScriptCache(std::move(m_preparedScriptStore));
+ settings.script_cache = &scriptCache;
+
+ napi_env env{};
napi_ext_create_env(&settings, &env);
// Associate environment to holder.
napi_set_instance_data(env, this, nullptr /*finalize_cb*/, nullptr /*finalize_hint*/);
@@ -96,6 +99,73 @@ void NapiJsiV8RuntimeHolder::InitRuntime() noexcept {
m_ownThreadId = std::this_thread::get_id();
}
+struct NodeApiJsiBuffer : facebook::jsi::Buffer {
+ static std::shared_ptr CreateJsiBuffer(const napi_ext_buffer *buffer) {
+ if (buffer && buffer->data) {
+ return std::shared_ptr(new NodeApiJsiBuffer(buffer));
+ } else {
+ return {};
+ }
+ }
+
+ NodeApiJsiBuffer(const napi_ext_buffer *buffer) noexcept : buffer_(*buffer) {}
+
+ ~NodeApiJsiBuffer() override {
+ if (buffer_.buffer_object.finalize_cb) {
+ buffer_.buffer_object.finalize_cb(nullptr, buffer_.buffer_object.data, buffer_.buffer_object.finalize_hint);
+ }
+ }
+
+ const uint8_t *data() const override {
+ return buffer_.data;
+ }
+
+ size_t size() const override {
+ return buffer_.byte_size;
+ }
+
+ private:
+ napi_ext_buffer buffer_;
+};
+
+napi_ext_script_cache NapiJsiV8RuntimeHolder::InitScriptCache(
+ unique_ptr &&preparedScriptStore) noexcept {
+ napi_ext_script_cache scriptCache{};
+ scriptCache.cache_object = NativeObjectWrapper>::Wrap(std::move(preparedScriptStore));
+ scriptCache.load_cached_script = [](napi_env env,
+ napi_ext_script_cache *script_cache,
+ napi_ext_cached_script_metadata *script_metadata,
+ napi_ext_buffer *result) -> napi_status {
+ PreparedScriptStore *scriptStore = reinterpret_cast(script_cache->cache_object.data);
+ std::shared_ptr buffer = scriptStore->tryGetPreparedScript(
+ ScriptSignature{script_metadata->source_url, script_metadata->source_hash},
+ JSRuntimeSignature{script_metadata->runtime_name, script_metadata->runtime_version},
+ script_metadata->tag);
+ if (buffer) {
+ result->buffer_object = NativeObjectWrapper>::Wrap(
+ std::shared_ptr{buffer});
+ result->data = buffer->data();
+ result->byte_size = buffer->size();
+ } else {
+ *result = napi_ext_buffer{};
+ }
+ return napi_ok;
+ };
+ scriptCache.store_cached_script = [](napi_env env,
+ napi_ext_script_cache *script_cache,
+ napi_ext_cached_script_metadata *script_metadata,
+ const napi_ext_buffer *buffer) -> napi_status {
+ PreparedScriptStore *scriptStore = reinterpret_cast(script_cache->cache_object.data);
+ scriptStore->persistPreparedScript(
+ NodeApiJsiBuffer::CreateJsiBuffer(buffer),
+ ScriptSignature{script_metadata->source_url, script_metadata->source_hash},
+ JSRuntimeSignature{script_metadata->runtime_name, script_metadata->runtime_version},
+ script_metadata->tag);
+ return napi_ok;
+ };
+ return scriptCache;
+}
+
#pragma region Microsoft::JSI::RuntimeHolderLazyInit
facebook::react::JSIEngineOverride NapiJsiV8RuntimeHolder::getRuntimeType() noexcept {
diff --git a/vnext/Shared/JSI/NapiJsiV8RuntimeHolder.h b/vnext/Shared/JSI/NapiJsiV8RuntimeHolder.h
index eb4c0b3fb83..dfdd433a2dd 100644
--- a/vnext/Shared/JSI/NapiJsiV8RuntimeHolder.h
+++ b/vnext/Shared/JSI/NapiJsiV8RuntimeHolder.h
@@ -31,6 +31,8 @@ class NapiJsiV8RuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit {
void *finalizeHint);
void InitRuntime() noexcept;
+ napi_ext_script_cache InitScriptCache(
+ std::unique_ptr &&preparedScriptStore) noexcept;
std::shared_ptr m_runtime;
std::shared_ptr m_jsQueue;