Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Support PreparedScriptStore for V8 Node-API.",
"packageName": "react-native-windows",
"email": "vmorozov@microsoft.com",
"dependentChangeType": "patch"
}
20 changes: 17 additions & 3 deletions vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const facebook::jsi::Buffer> &buffer, const char *sourceUrl);
std::vector<uint8_t> SerializeScript(napi_value script, const char *sourceUrl);
napi_value RunSerializedScript(span<const uint8_t> serialized, napi_value source, const char *sourceUrl);
napi_ext_ref CreateReference(napi_value value) const;
Expand Down Expand Up @@ -620,9 +621,7 @@ NapiJsiRuntime::NapiJsiRuntime(napi_env env) noexcept : m_env{env} {

Value NapiJsiRuntime::evaluateJavaScript(const shared_ptr<const Buffer> &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);
}

Expand Down Expand Up @@ -1406,6 +1405,21 @@ napi_value NapiJsiRuntime::RunScript(napi_value script, const char *sourceUrl) {
return result;
}

napi_value NapiJsiRuntime::RunScriptBuffer(
const std::shared_ptr<const facebook::jsi::Buffer> &buffer,
const char *sourceUrl) {
napi_ext_buffer napiBuffer{};
napiBuffer.buffer_object = NativeObjectWrapper<std::shared_ptr<const facebook::jsi::Buffer>>::Wrap(
std::shared_ptr<const facebook::jsi::Buffer>{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<uint8_t> NapiJsiRuntime::SerializeScript(napi_value script, const char *sourceUrl) {
vector<uint8_t> result;
Expand Down
48 changes: 48 additions & 0 deletions vnext/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,54 @@ namespace Microsoft::JSI {
///
std::unique_ptr<facebook::jsi::Runtime> __cdecl MakeNodeApiJsiRuntime(napi_env env) noexcept;

template <typename T>
struct NativeObjectWrapper;

template <typename T>
struct NativeObjectWrapper<std::unique_ptr<T>> {
static napi_ext_native_data Wrap(std::unique_ptr<T> &&obj) noexcept {
napi_ext_native_data nativeData{};
nativeData.data = obj.release();
nativeData.finalize_cb = [](napi_env /*env*/, void *data, void * /*finalizeHint*/) {
std::unique_ptr<T> obj{reinterpret_cast<T *>(data)};
};
return nativeData;
}

static T *Unwrap(napi_ext_native_data &nativeData) noexcept {
return reinterpret_cast<T *>(nativeData.data);
}
};

template <typename T>
struct NativeObjectWrapper<std::shared_ptr<T>> {
static napi_ext_native_data Wrap(std::shared_ptr<T> &&obj) noexcept {
static_assert(
sizeof(SharedPtrHolder) == sizeof(std::shared_ptr<T>), "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<T> obj(std::move(*reinterpret_cast<std::shared_ptr<T> *>(std::addressof(ptrHolder))));
};
return nativeData;
}

static std::shared_ptr<T> Unwrap(napi_ext_native_data &nativeData) noexcept {
SharedPtrHolder ptrHolder{nativeData.data, nativeData.finalize_hint};
return *reinterpret_cast<std::shared_ptr<T> *>(std::addressof(ptrHolder));
}

private:
struct SharedPtrHolder {
void *ptr1;
void *ptr2;
};
};

} // namespace Microsoft::JSI

#endif // MICROSOFT_REACTNATIVE_JSI_NODEAPIJSIRUNTIME
2 changes: 1 addition & 1 deletion vnext/PropertySheets/JSEngine.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<EnableDevServerHBCBundles Condition="'$(EnableDevServerHBCBundles)' == ''">false</EnableDevServerHBCBundles>

<UseV8 Condition="'$(UseV8)' == ''">false</UseV8>
<V8Version Condition="'$(V8Version)' == ''">0.65.11</V8Version>
<V8Version Condition="'$(V8Version)' == ''">0.65.15</V8Version>
<V8PackageName>ReactNative.V8Jsi.Windows</V8PackageName>
<V8PackageName Condition="'$(V8AppPlatform)' != 'win32'">$(V8PackageName).UWP</V8PackageName>
<V8Package>$(NuGetPackageRoot)\$(V8PackageName).$(V8Version)</V8Package>
Expand Down
74 changes: 72 additions & 2 deletions vnext/Shared/JSI/NapiJsiV8RuntimeHolder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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*/);
Expand All @@ -96,6 +99,73 @@ void NapiJsiV8RuntimeHolder::InitRuntime() noexcept {
m_ownThreadId = std::this_thread::get_id();
}

struct NodeApiJsiBuffer : facebook::jsi::Buffer {
static std::shared_ptr<const facebook::jsi::Buffer> CreateJsiBuffer(const napi_ext_buffer *buffer) {
if (buffer && buffer->data) {
return std::shared_ptr<const facebook::jsi::Buffer>(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> &&preparedScriptStore) noexcept {
napi_ext_script_cache scriptCache{};
scriptCache.cache_object = NativeObjectWrapper<unique_ptr<PreparedScriptStore>>::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<PreparedScriptStore *>(script_cache->cache_object.data);
std::shared_ptr<const facebook::jsi::Buffer> 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<std::shared_ptr<const facebook::jsi::Buffer>>::Wrap(
std::shared_ptr<const facebook::jsi::Buffer>{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<PreparedScriptStore *>(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 {
Expand Down
2 changes: 2 additions & 0 deletions vnext/Shared/JSI/NapiJsiV8RuntimeHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class NapiJsiV8RuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit {
void *finalizeHint);

void InitRuntime() noexcept;
napi_ext_script_cache InitScriptCache(
std::unique_ptr<facebook::jsi::PreparedScriptStore> &&preparedScriptStore) noexcept;

std::shared_ptr<facebook::jsi::Runtime> m_runtime;
std::shared_ptr<facebook::react::MessageQueueThread> m_jsQueue;
Expand Down