src: add snapshot support for embedder API
Add experimental support for loading snapshots in the embedder API by adding a public opaque wrapper for our `SnapshotData` struct and allowing embedders to pass it to the relevant setup functions. Where applicable, use these helpers to deduplicate existing code in Node.js’s startup path. This has shown a 40 % startup performance increase for a real-world application, even with the somewhat limited current support for built-in modules. The documentation includes a note about no guarantees for API or ABI stability for this feature while it is experimental. PR-URL: https://github.com/nodejs/node/pull/45888 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
parent
d523bfe2c4
commit
06bb6b42b3
@ -1,6 +1,7 @@
|
|||||||
#include "node.h"
|
|
||||||
#include "env-inl.h"
|
|
||||||
#include "debug_utils-inl.h"
|
#include "debug_utils-inl.h"
|
||||||
|
#include "env-inl.h"
|
||||||
|
#include "node.h"
|
||||||
|
#include "node_snapshot_builder.h"
|
||||||
|
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
using v8::Function;
|
using v8::Function;
|
||||||
@ -86,8 +87,9 @@ struct CommonEnvironmentSetup::Impl {
|
|||||||
CommonEnvironmentSetup::CommonEnvironmentSetup(
|
CommonEnvironmentSetup::CommonEnvironmentSetup(
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
std::vector<std::string>* errors,
|
std::vector<std::string>* errors,
|
||||||
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
|
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
|
||||||
: impl_(new Impl()) {
|
: impl_(new Impl()) {
|
||||||
CHECK_NOT_NULL(platform);
|
CHECK_NOT_NULL(platform);
|
||||||
CHECK_NOT_NULL(errors);
|
CHECK_NOT_NULL(errors);
|
||||||
|
|
||||||
@ -104,16 +106,25 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
|
|||||||
loop->data = this;
|
loop->data = this;
|
||||||
|
|
||||||
impl_->allocator = ArrayBufferAllocator::Create();
|
impl_->allocator = ArrayBufferAllocator::Create();
|
||||||
impl_->isolate = NewIsolate(impl_->allocator, &impl_->loop, platform);
|
impl_->isolate =
|
||||||
|
NewIsolate(impl_->allocator, &impl_->loop, platform, snapshot_data);
|
||||||
Isolate* isolate = impl_->isolate;
|
Isolate* isolate = impl_->isolate;
|
||||||
|
|
||||||
{
|
{
|
||||||
Locker locker(isolate);
|
Locker locker(isolate);
|
||||||
Isolate::Scope isolate_scope(isolate);
|
Isolate::Scope isolate_scope(isolate);
|
||||||
impl_->isolate_data.reset(CreateIsolateData(
|
impl_->isolate_data.reset(CreateIsolateData(
|
||||||
isolate, loop, platform, impl_->allocator.get()));
|
isolate, loop, platform, impl_->allocator.get(), snapshot_data));
|
||||||
|
|
||||||
HandleScope handle_scope(isolate);
|
HandleScope handle_scope(isolate);
|
||||||
|
if (snapshot_data) {
|
||||||
|
impl_->env.reset(make_env(this));
|
||||||
|
if (impl_->env) {
|
||||||
|
impl_->context.Reset(isolate, impl_->env->context());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Local<Context> context = NewContext(isolate);
|
Local<Context> context = NewContext(isolate);
|
||||||
impl_->context.Reset(isolate, context);
|
impl_->context.Reset(isolate, context);
|
||||||
if (context.IsEmpty()) {
|
if (context.IsEmpty()) {
|
||||||
@ -126,6 +137,12 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommonEnvironmentSetup::CommonEnvironmentSetup(
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
std::vector<std::string>* errors,
|
||||||
|
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
|
||||||
|
: CommonEnvironmentSetup(platform, errors, nullptr, make_env) {}
|
||||||
|
|
||||||
CommonEnvironmentSetup::~CommonEnvironmentSetup() {
|
CommonEnvironmentSetup::~CommonEnvironmentSetup() {
|
||||||
if (impl_->isolate != nullptr) {
|
if (impl_->isolate != nullptr) {
|
||||||
Isolate* isolate = impl_->isolate;
|
Isolate* isolate = impl_->isolate;
|
||||||
@ -189,4 +206,42 @@ v8::Local<v8::Context> CommonEnvironmentSetup::context() const {
|
|||||||
return impl_->context.Get(impl_->isolate);
|
return impl_->context.Get(impl_->isolate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmbedderSnapshotData::DeleteSnapshotData::operator()(
|
||||||
|
const EmbedderSnapshotData* data) const {
|
||||||
|
CHECK_IMPLIES(data->owns_impl_, data->impl_);
|
||||||
|
if (data->owns_impl_ &&
|
||||||
|
data->impl_->data_ownership == SnapshotData::DataOwnership::kOwned) {
|
||||||
|
delete data->impl_;
|
||||||
|
}
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbedderSnapshotData::Pointer EmbedderSnapshotData::BuiltinSnapshotData() {
|
||||||
|
return EmbedderSnapshotData::Pointer{new EmbedderSnapshotData(
|
||||||
|
SnapshotBuilder::GetEmbeddedSnapshotData(), false)};
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) {
|
||||||
|
SnapshotData* snapshot_data = new SnapshotData();
|
||||||
|
CHECK_EQ(snapshot_data->data_ownership, SnapshotData::DataOwnership::kOwned);
|
||||||
|
EmbedderSnapshotData::Pointer result{
|
||||||
|
new EmbedderSnapshotData(snapshot_data, true)};
|
||||||
|
if (!SnapshotData::FromBlob(snapshot_data, in)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbedderSnapshotData::EmbedderSnapshotData(const SnapshotData* impl,
|
||||||
|
bool owns_impl)
|
||||||
|
: impl_(impl), owns_impl_(owns_impl) {}
|
||||||
|
|
||||||
|
bool EmbedderSnapshotData::CanUseCustomSnapshotPerIsolate() {
|
||||||
|
#ifdef NODE_V8_SHARED_RO_HEAP
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "node_platform.h"
|
#include "node_platform.h"
|
||||||
#include "node_realm-inl.h"
|
#include "node_realm-inl.h"
|
||||||
#include "node_shadow_realm.h"
|
#include "node_shadow_realm.h"
|
||||||
|
#include "node_snapshot_builder.h"
|
||||||
#include "node_v8_platform-inl.h"
|
#include "node_v8_platform-inl.h"
|
||||||
#include "node_wasm_web_api.h"
|
#include "node_wasm_web_api.h"
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
@ -315,9 +316,15 @@ void SetIsolateUpForNode(v8::Isolate* isolate) {
|
|||||||
Isolate* NewIsolate(Isolate::CreateParams* params,
|
Isolate* NewIsolate(Isolate::CreateParams* params,
|
||||||
uv_loop_t* event_loop,
|
uv_loop_t* event_loop,
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
bool has_snapshot_data) {
|
const SnapshotData* snapshot_data,
|
||||||
|
const IsolateSettings& settings) {
|
||||||
Isolate* isolate = Isolate::Allocate();
|
Isolate* isolate = Isolate::Allocate();
|
||||||
if (isolate == nullptr) return nullptr;
|
if (isolate == nullptr) return nullptr;
|
||||||
|
|
||||||
|
if (snapshot_data != nullptr) {
|
||||||
|
SnapshotBuilder::InitializeIsolateParams(snapshot_data, params);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef NODE_V8_SHARED_RO_HEAP
|
#ifdef NODE_V8_SHARED_RO_HEAP
|
||||||
{
|
{
|
||||||
// In shared-readonly-heap mode, V8 requires all snapshots used for
|
// In shared-readonly-heap mode, V8 requires all snapshots used for
|
||||||
@ -336,12 +343,12 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
|
|||||||
|
|
||||||
SetIsolateCreateParamsForNode(params);
|
SetIsolateCreateParamsForNode(params);
|
||||||
Isolate::Initialize(isolate, *params);
|
Isolate::Initialize(isolate, *params);
|
||||||
if (!has_snapshot_data) {
|
if (snapshot_data == nullptr) {
|
||||||
// If in deserialize mode, delay until after the deserialization is
|
// If in deserialize mode, delay until after the deserialization is
|
||||||
// complete.
|
// complete.
|
||||||
SetIsolateUpForNode(isolate);
|
SetIsolateUpForNode(isolate, settings);
|
||||||
} else {
|
} else {
|
||||||
SetIsolateMiscHandlers(isolate, {});
|
SetIsolateMiscHandlers(isolate, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
return isolate;
|
return isolate;
|
||||||
@ -349,25 +356,60 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
|
|||||||
|
|
||||||
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||||
uv_loop_t* event_loop,
|
uv_loop_t* event_loop,
|
||||||
MultiIsolatePlatform* platform) {
|
MultiIsolatePlatform* platform,
|
||||||
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
|
const IsolateSettings& settings) {
|
||||||
Isolate::CreateParams params;
|
Isolate::CreateParams params;
|
||||||
if (allocator != nullptr) params.array_buffer_allocator = allocator;
|
if (allocator != nullptr) params.array_buffer_allocator = allocator;
|
||||||
return NewIsolate(¶ms, event_loop, platform);
|
return NewIsolate(¶ms,
|
||||||
|
event_loop,
|
||||||
|
platform,
|
||||||
|
SnapshotData::FromEmbedderWrapper(snapshot_data),
|
||||||
|
settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
|
||||||
|
uv_loop_t* event_loop,
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
|
const IsolateSettings& settings) {
|
||||||
|
Isolate::CreateParams params;
|
||||||
|
if (allocator) params.array_buffer_allocator_shared = allocator;
|
||||||
|
return NewIsolate(¶ms,
|
||||||
|
event_loop,
|
||||||
|
platform,
|
||||||
|
SnapshotData::FromEmbedderWrapper(snapshot_data),
|
||||||
|
settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||||
|
uv_loop_t* event_loop,
|
||||||
|
MultiIsolatePlatform* platform) {
|
||||||
|
return NewIsolate(allocator, event_loop, platform, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
|
Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
|
||||||
uv_loop_t* event_loop,
|
uv_loop_t* event_loop,
|
||||||
MultiIsolatePlatform* platform) {
|
MultiIsolatePlatform* platform) {
|
||||||
Isolate::CreateParams params;
|
return NewIsolate(allocator, event_loop, platform, nullptr);
|
||||||
if (allocator) params.array_buffer_allocator_shared = allocator;
|
}
|
||||||
return NewIsolate(¶ms, event_loop, platform);
|
|
||||||
|
IsolateData* CreateIsolateData(
|
||||||
|
Isolate* isolate,
|
||||||
|
uv_loop_t* loop,
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
ArrayBufferAllocator* allocator,
|
||||||
|
const EmbedderSnapshotData* embedder_snapshot_data) {
|
||||||
|
const SnapshotData* snapshot_data =
|
||||||
|
SnapshotData::FromEmbedderWrapper(embedder_snapshot_data);
|
||||||
|
return new IsolateData(isolate, loop, platform, allocator, snapshot_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
IsolateData* CreateIsolateData(Isolate* isolate,
|
IsolateData* CreateIsolateData(Isolate* isolate,
|
||||||
uv_loop_t* loop,
|
uv_loop_t* loop,
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
ArrayBufferAllocator* allocator) {
|
ArrayBufferAllocator* allocator) {
|
||||||
return new IsolateData(isolate, loop, platform, allocator);
|
return CreateIsolateData(isolate, loop, platform, allocator, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeIsolateData(IsolateData* isolate_data) {
|
void FreeIsolateData(IsolateData* isolate_data) {
|
||||||
@ -395,13 +437,45 @@ Environment* CreateEnvironment(
|
|||||||
EnvironmentFlags::Flags flags,
|
EnvironmentFlags::Flags flags,
|
||||||
ThreadId thread_id,
|
ThreadId thread_id,
|
||||||
std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
|
std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
|
||||||
Isolate* isolate = context->GetIsolate();
|
Isolate* isolate = isolate_data->isolate();
|
||||||
HandleScope handle_scope(isolate);
|
HandleScope handle_scope(isolate);
|
||||||
Context::Scope context_scope(context);
|
|
||||||
|
const bool use_snapshot = context.IsEmpty();
|
||||||
|
const EnvSerializeInfo* env_snapshot_info = nullptr;
|
||||||
|
if (use_snapshot) {
|
||||||
|
CHECK_NOT_NULL(isolate_data->snapshot_data());
|
||||||
|
env_snapshot_info = &isolate_data->snapshot_data()->env_info;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(addaleax): This is a much better place for parsing per-Environment
|
// TODO(addaleax): This is a much better place for parsing per-Environment
|
||||||
// options than the global parse call.
|
// options than the global parse call.
|
||||||
Environment* env = new Environment(
|
Environment* env = new Environment(isolate_data,
|
||||||
isolate_data, context, args, exec_args, nullptr, flags, thread_id);
|
isolate,
|
||||||
|
args,
|
||||||
|
exec_args,
|
||||||
|
env_snapshot_info,
|
||||||
|
flags,
|
||||||
|
thread_id);
|
||||||
|
CHECK_NOT_NULL(env);
|
||||||
|
|
||||||
|
if (use_snapshot) {
|
||||||
|
context = Context::FromSnapshot(isolate,
|
||||||
|
SnapshotData::kNodeMainContextIndex,
|
||||||
|
{DeserializeNodeInternalFields, env})
|
||||||
|
.ToLocalChecked();
|
||||||
|
|
||||||
|
CHECK(!context.IsEmpty());
|
||||||
|
Context::Scope context_scope(context);
|
||||||
|
|
||||||
|
if (InitializeContextRuntime(context).IsNothing()) {
|
||||||
|
FreeEnvironment(env);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
SetIsolateErrorHandlers(isolate, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Context::Scope context_scope(context);
|
||||||
|
env->InitializeMainContext(context, env_snapshot_info);
|
||||||
|
|
||||||
#if HAVE_INSPECTOR
|
#if HAVE_INSPECTOR
|
||||||
if (env->should_create_inspector()) {
|
if (env->should_create_inspector()) {
|
||||||
@ -415,7 +489,7 @@ Environment* CreateEnvironment(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (env->principal_realm()->RunBootstrapping().IsEmpty()) {
|
if (!use_snapshot && env->principal_realm()->RunBootstrapping().IsEmpty()) {
|
||||||
FreeEnvironment(env);
|
FreeEnvironment(env);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -500,6 +574,10 @@ ArrayBufferAllocator* GetArrayBufferAllocator(IsolateData* isolate_data) {
|
|||||||
return isolate_data->node_allocator();
|
return isolate_data->node_allocator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Local<Context> GetMainContext(Environment* env) {
|
||||||
|
return env->context();
|
||||||
|
}
|
||||||
|
|
||||||
MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env) {
|
MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env) {
|
||||||
return GetMultiIsolatePlatform(env->isolate_data());
|
return GetMultiIsolatePlatform(env->isolate_data());
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,10 @@ inline MultiIsolatePlatform* IsolateData::platform() const {
|
|||||||
return platform_;
|
return platform_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const SnapshotData* IsolateData::snapshot_data() const {
|
||||||
|
return snapshot_data_;
|
||||||
|
}
|
||||||
|
|
||||||
inline void IsolateData::set_worker_context(worker::Worker* context) {
|
inline void IsolateData::set_worker_context(worker::Worker* context) {
|
||||||
CHECK_NULL(worker_context_); // Should be set only once.
|
CHECK_NULL(worker_context_); // Should be set only once.
|
||||||
worker_context_ = context;
|
worker_context_ = context;
|
||||||
|
41
src/env.cc
41
src/env.cc
@ -472,19 +472,20 @@ IsolateData::IsolateData(Isolate* isolate,
|
|||||||
uv_loop_t* event_loop,
|
uv_loop_t* event_loop,
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
ArrayBufferAllocator* node_allocator,
|
ArrayBufferAllocator* node_allocator,
|
||||||
const IsolateDataSerializeInfo* isolate_data_info)
|
const SnapshotData* snapshot_data)
|
||||||
: isolate_(isolate),
|
: isolate_(isolate),
|
||||||
event_loop_(event_loop),
|
event_loop_(event_loop),
|
||||||
node_allocator_(node_allocator == nullptr ? nullptr
|
node_allocator_(node_allocator == nullptr ? nullptr
|
||||||
: node_allocator->GetImpl()),
|
: node_allocator->GetImpl()),
|
||||||
platform_(platform) {
|
platform_(platform),
|
||||||
|
snapshot_data_(snapshot_data) {
|
||||||
options_.reset(
|
options_.reset(
|
||||||
new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
|
new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
|
||||||
|
|
||||||
if (isolate_data_info == nullptr) {
|
if (snapshot_data == nullptr) {
|
||||||
CreateProperties();
|
CreateProperties();
|
||||||
} else {
|
} else {
|
||||||
DeserializeProperties(isolate_data_info);
|
DeserializeProperties(&snapshot_data->isolate_data_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,14 +676,23 @@ Environment::Environment(IsolateData* isolate_data,
|
|||||||
thread_id_(thread_id.id == static_cast<uint64_t>(-1)
|
thread_id_(thread_id.id == static_cast<uint64_t>(-1)
|
||||||
? AllocateEnvironmentThreadId().id
|
? AllocateEnvironmentThreadId().id
|
||||||
: thread_id.id) {
|
: thread_id.id) {
|
||||||
|
constexpr bool is_shared_ro_heap =
|
||||||
#ifdef NODE_V8_SHARED_RO_HEAP
|
#ifdef NODE_V8_SHARED_RO_HEAP
|
||||||
if (!is_main_thread()) {
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
if (is_shared_ro_heap && !is_main_thread()) {
|
||||||
|
// If this is a Worker thread and we are in shared-readonly-heap mode,
|
||||||
|
// we can always safely use the parent's Isolate's code cache.
|
||||||
CHECK_NOT_NULL(isolate_data->worker_context());
|
CHECK_NOT_NULL(isolate_data->worker_context());
|
||||||
// TODO(addaleax): Adjust for the embedder API snapshot support changes
|
|
||||||
builtin_loader()->CopySourceAndCodeCacheReferenceFrom(
|
builtin_loader()->CopySourceAndCodeCacheReferenceFrom(
|
||||||
isolate_data->worker_context()->env()->builtin_loader());
|
isolate_data->worker_context()->env()->builtin_loader());
|
||||||
|
} else if (isolate_data->snapshot_data() != nullptr) {
|
||||||
|
// ... otherwise, if a snapshot was provided, use its code cache.
|
||||||
|
builtin_loader()->RefreshCodeCache(
|
||||||
|
isolate_data->snapshot_data()->code_cache);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// We'll be creating new objects so make sure we've entered the context.
|
// We'll be creating new objects so make sure we've entered the context.
|
||||||
HandleScope handle_scope(isolate);
|
HandleScope handle_scope(isolate);
|
||||||
@ -747,23 +757,6 @@ Environment::Environment(IsolateData* isolate_data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment::Environment(IsolateData* isolate_data,
|
|
||||||
Local<Context> context,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::vector<std::string>& exec_args,
|
|
||||||
const EnvSerializeInfo* env_info,
|
|
||||||
EnvironmentFlags::Flags flags,
|
|
||||||
ThreadId thread_id)
|
|
||||||
: Environment(isolate_data,
|
|
||||||
context->GetIsolate(),
|
|
||||||
args,
|
|
||||||
exec_args,
|
|
||||||
env_info,
|
|
||||||
flags,
|
|
||||||
thread_id) {
|
|
||||||
InitializeMainContext(context, env_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Environment::InitializeMainContext(Local<Context> context,
|
void Environment::InitializeMainContext(Local<Context> context,
|
||||||
const EnvSerializeInfo* env_info) {
|
const EnvSerializeInfo* env_info) {
|
||||||
principal_realm_ = std::make_unique<Realm>(
|
principal_realm_ = std::make_unique<Realm>(
|
||||||
|
15
src/env.h
15
src/env.h
@ -126,7 +126,7 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
|
|||||||
uv_loop_t* event_loop,
|
uv_loop_t* event_loop,
|
||||||
MultiIsolatePlatform* platform = nullptr,
|
MultiIsolatePlatform* platform = nullptr,
|
||||||
ArrayBufferAllocator* node_allocator = nullptr,
|
ArrayBufferAllocator* node_allocator = nullptr,
|
||||||
const IsolateDataSerializeInfo* isolate_data_info = nullptr);
|
const SnapshotData* snapshot_data = nullptr);
|
||||||
SET_MEMORY_INFO_NAME(IsolateData)
|
SET_MEMORY_INFO_NAME(IsolateData)
|
||||||
SET_SELF_SIZE(IsolateData)
|
SET_SELF_SIZE(IsolateData)
|
||||||
void MemoryInfo(MemoryTracker* tracker) const override;
|
void MemoryInfo(MemoryTracker* tracker) const override;
|
||||||
@ -134,6 +134,7 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
|
|||||||
|
|
||||||
inline uv_loop_t* event_loop() const;
|
inline uv_loop_t* event_loop() const;
|
||||||
inline MultiIsolatePlatform* platform() const;
|
inline MultiIsolatePlatform* platform() const;
|
||||||
|
inline const SnapshotData* snapshot_data() const;
|
||||||
inline std::shared_ptr<PerIsolateOptions> options();
|
inline std::shared_ptr<PerIsolateOptions> options();
|
||||||
inline void set_options(std::shared_ptr<PerIsolateOptions> options);
|
inline void set_options(std::shared_ptr<PerIsolateOptions> options);
|
||||||
|
|
||||||
@ -205,6 +206,7 @@ class NODE_EXTERN_PRIVATE IsolateData : public MemoryRetainer {
|
|||||||
uv_loop_t* const event_loop_;
|
uv_loop_t* const event_loop_;
|
||||||
NodeArrayBufferAllocator* const node_allocator_;
|
NodeArrayBufferAllocator* const node_allocator_;
|
||||||
MultiIsolatePlatform* platform_;
|
MultiIsolatePlatform* platform_;
|
||||||
|
const SnapshotData* snapshot_data_;
|
||||||
std::shared_ptr<PerIsolateOptions> options_;
|
std::shared_ptr<PerIsolateOptions> options_;
|
||||||
worker::Worker* worker_context_ = nullptr;
|
worker::Worker* worker_context_ = nullptr;
|
||||||
};
|
};
|
||||||
@ -520,6 +522,9 @@ struct SnapshotData {
|
|||||||
// and the caller should not consume the snapshot data.
|
// and the caller should not consume the snapshot data.
|
||||||
bool Check() const;
|
bool Check() const;
|
||||||
static bool FromBlob(SnapshotData* out, FILE* in);
|
static bool FromBlob(SnapshotData* out, FILE* in);
|
||||||
|
static const SnapshotData* FromEmbedderWrapper(
|
||||||
|
const EmbedderSnapshotData* data);
|
||||||
|
EmbedderSnapshotData::Pointer AsEmbedderWrapper() const;
|
||||||
|
|
||||||
~SnapshotData();
|
~SnapshotData();
|
||||||
};
|
};
|
||||||
@ -610,14 +615,6 @@ class Environment : public MemoryRetainer {
|
|||||||
ThreadId thread_id);
|
ThreadId thread_id);
|
||||||
void InitializeMainContext(v8::Local<v8::Context> context,
|
void InitializeMainContext(v8::Local<v8::Context> context,
|
||||||
const EnvSerializeInfo* env_info);
|
const EnvSerializeInfo* env_info);
|
||||||
// Create an Environment and initialize the provided principal context for it.
|
|
||||||
Environment(IsolateData* isolate_data,
|
|
||||||
v8::Local<v8::Context> context,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::vector<std::string>& exec_args,
|
|
||||||
const EnvSerializeInfo* env_info,
|
|
||||||
EnvironmentFlags::Flags flags,
|
|
||||||
ThreadId thread_id);
|
|
||||||
~Environment() override;
|
~Environment() override;
|
||||||
|
|
||||||
void InitializeLibuv();
|
void InitializeLibuv();
|
||||||
|
118
src/node.h
118
src/node.h
@ -127,6 +127,8 @@ struct napi_module;
|
|||||||
// terminally confused when it's done in node_internals.h
|
// terminally confused when it's done in node_internals.h
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
|
struct SnapshotData;
|
||||||
|
|
||||||
namespace tracing {
|
namespace tracing {
|
||||||
|
|
||||||
class TracingController;
|
class TracingController;
|
||||||
@ -473,6 +475,65 @@ struct IsolateSettings {
|
|||||||
modify_code_generation_from_strings_callback = nullptr;
|
modify_code_generation_from_strings_callback = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Represents a startup snapshot blob, e.g. created by passing
|
||||||
|
// --node-snapshot-main=entry.js to the configure script at build time,
|
||||||
|
// or by running Node.js with the --build-snapshot option.
|
||||||
|
//
|
||||||
|
// If used, the snapshot *must* have been built with the same Node.js
|
||||||
|
// version and V8 flags as the version that is currently running, and will
|
||||||
|
// be rejected otherwise.
|
||||||
|
// The same EmbedderSnapshotData instance *must* be passed to both
|
||||||
|
// `NewIsolate()` and `CreateIsolateData()`. The first `Environment` instance
|
||||||
|
// should be created with an empty `context` argument and will then
|
||||||
|
// use the main context included in the snapshot blob. It can be retrieved
|
||||||
|
// using `GetMainContext()`. `LoadEnvironment` can receive an empty
|
||||||
|
// `StartExecutionCallback` in this case.
|
||||||
|
// If V8 was configured with the shared-readonly-heap option, it requires
|
||||||
|
// all snapshots used to create `Isolate` instances to be identical.
|
||||||
|
// This option *must* be unset by embedders who wish to use the startup
|
||||||
|
// feature during the build step by passing the --disable-shared-readonly-heap
|
||||||
|
// flag to the configure script.
|
||||||
|
//
|
||||||
|
// Snapshots are an *experimental* feature. In particular, the embedder API
|
||||||
|
// exposed through this class is subject to change or removal between Node.js
|
||||||
|
// versions, including possible API and ABI breakage.
|
||||||
|
class EmbedderSnapshotData {
|
||||||
|
public:
|
||||||
|
struct DeleteSnapshotData {
|
||||||
|
void operator()(const EmbedderSnapshotData*) const;
|
||||||
|
};
|
||||||
|
using Pointer =
|
||||||
|
std::unique_ptr<const EmbedderSnapshotData, DeleteSnapshotData>;
|
||||||
|
|
||||||
|
// Return an EmbedderSnapshotData object that refers to the built-in
|
||||||
|
// snapshot of Node.js. This can have been configured through e.g.
|
||||||
|
// --node-snapshot-main=entry.js.
|
||||||
|
static Pointer BuiltinSnapshotData();
|
||||||
|
|
||||||
|
// Return an EmbedderSnapshotData object that is based on an input file.
|
||||||
|
// Calling this method will not consume but not close the FILE* handle.
|
||||||
|
// The FILE* handle can be closed immediately following this call.
|
||||||
|
// If the snapshot is invalid, this returns an empty pointer.
|
||||||
|
static Pointer FromFile(FILE* in);
|
||||||
|
|
||||||
|
// Returns whether custom snapshots can be used. Currently, this means
|
||||||
|
// that V8 was configured without the shared-readonly-heap feature.
|
||||||
|
static bool CanUseCustomSnapshotPerIsolate();
|
||||||
|
|
||||||
|
EmbedderSnapshotData(const EmbedderSnapshotData&) = delete;
|
||||||
|
EmbedderSnapshotData& operator=(const EmbedderSnapshotData&) = delete;
|
||||||
|
EmbedderSnapshotData(EmbedderSnapshotData&&) = delete;
|
||||||
|
EmbedderSnapshotData& operator=(EmbedderSnapshotData&&) = delete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EmbedderSnapshotData(const SnapshotData* impl, bool owns_impl);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SnapshotData* impl_;
|
||||||
|
bool owns_impl_;
|
||||||
|
friend struct SnapshotData;
|
||||||
|
};
|
||||||
|
|
||||||
// Overriding IsolateSettings may produce unexpected behavior
|
// Overriding IsolateSettings may produce unexpected behavior
|
||||||
// in Node.js core functionality, so proceed at your own risk.
|
// in Node.js core functionality, so proceed at your own risk.
|
||||||
NODE_EXTERN void SetIsolateUpForNode(v8::Isolate* isolate,
|
NODE_EXTERN void SetIsolateUpForNode(v8::Isolate* isolate,
|
||||||
@ -489,10 +550,23 @@ NODE_EXTERN void SetIsolateUpForNode(v8::Isolate* isolate);
|
|||||||
NODE_EXTERN v8::Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
NODE_EXTERN v8::Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||||
struct uv_loop_s* event_loop,
|
struct uv_loop_s* event_loop,
|
||||||
MultiIsolatePlatform* platform = nullptr);
|
MultiIsolatePlatform* platform = nullptr);
|
||||||
|
// TODO(addaleax): Merge with the function definition above.
|
||||||
|
NODE_EXTERN v8::Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||||
|
struct uv_loop_s* event_loop,
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
|
const IsolateSettings& settings = {});
|
||||||
NODE_EXTERN v8::Isolate* NewIsolate(
|
NODE_EXTERN v8::Isolate* NewIsolate(
|
||||||
std::shared_ptr<ArrayBufferAllocator> allocator,
|
std::shared_ptr<ArrayBufferAllocator> allocator,
|
||||||
struct uv_loop_s* event_loop,
|
struct uv_loop_s* event_loop,
|
||||||
MultiIsolatePlatform* platform);
|
MultiIsolatePlatform* platform);
|
||||||
|
// TODO(addaleax): Merge with the function definition above.
|
||||||
|
NODE_EXTERN v8::Isolate* NewIsolate(
|
||||||
|
std::shared_ptr<ArrayBufferAllocator> allocator,
|
||||||
|
struct uv_loop_s* event_loop,
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
|
const IsolateSettings& settings = {});
|
||||||
|
|
||||||
// Creates a new context with Node.js-specific tweaks.
|
// Creates a new context with Node.js-specific tweaks.
|
||||||
NODE_EXTERN v8::Local<v8::Context> NewContext(
|
NODE_EXTERN v8::Local<v8::Context> NewContext(
|
||||||
@ -512,6 +586,13 @@ NODE_EXTERN IsolateData* CreateIsolateData(
|
|||||||
struct uv_loop_s* loop,
|
struct uv_loop_s* loop,
|
||||||
MultiIsolatePlatform* platform = nullptr,
|
MultiIsolatePlatform* platform = nullptr,
|
||||||
ArrayBufferAllocator* allocator = nullptr);
|
ArrayBufferAllocator* allocator = nullptr);
|
||||||
|
// TODO(addaleax): Merge with the function definition above.
|
||||||
|
NODE_EXTERN IsolateData* CreateIsolateData(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
struct uv_loop_s* loop,
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
ArrayBufferAllocator* allocator,
|
||||||
|
const EmbedderSnapshotData* snapshot_data);
|
||||||
NODE_EXTERN void FreeIsolateData(IsolateData* isolate_data);
|
NODE_EXTERN void FreeIsolateData(IsolateData* isolate_data);
|
||||||
|
|
||||||
struct ThreadId {
|
struct ThreadId {
|
||||||
@ -571,6 +652,8 @@ struct InspectorParentHandle {
|
|||||||
// TODO(addaleax): Maybe move per-Environment options parsing here.
|
// TODO(addaleax): Maybe move per-Environment options parsing here.
|
||||||
// Returns nullptr when the Environment cannot be created e.g. there are
|
// Returns nullptr when the Environment cannot be created e.g. there are
|
||||||
// pending JavaScript exceptions.
|
// pending JavaScript exceptions.
|
||||||
|
// `context` may be empty if an `EmbedderSnapshotData` instance was provided
|
||||||
|
// to `NewIsolate()` and `CreateIsolateData()`.
|
||||||
NODE_EXTERN Environment* CreateEnvironment(
|
NODE_EXTERN Environment* CreateEnvironment(
|
||||||
IsolateData* isolate_data,
|
IsolateData* isolate_data,
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
@ -624,6 +707,9 @@ NODE_EXTERN void DefaultProcessExitHandler(Environment* env, int exit_code);
|
|||||||
NODE_EXTERN Environment* GetCurrentEnvironment(v8::Local<v8::Context> context);
|
NODE_EXTERN Environment* GetCurrentEnvironment(v8::Local<v8::Context> context);
|
||||||
NODE_EXTERN IsolateData* GetEnvironmentIsolateData(Environment* env);
|
NODE_EXTERN IsolateData* GetEnvironmentIsolateData(Environment* env);
|
||||||
NODE_EXTERN ArrayBufferAllocator* GetArrayBufferAllocator(IsolateData* data);
|
NODE_EXTERN ArrayBufferAllocator* GetArrayBufferAllocator(IsolateData* data);
|
||||||
|
// This is mostly useful for Environment* instances that were created through
|
||||||
|
// a snapshot and have a main context that was read from that snapshot.
|
||||||
|
NODE_EXTERN v8::Local<v8::Context> GetMainContext(Environment* env);
|
||||||
|
|
||||||
NODE_EXTERN void OnFatalError(const char* location, const char* message);
|
NODE_EXTERN void OnFatalError(const char* location, const char* message);
|
||||||
NODE_EXTERN void PromiseRejectCallback(v8::PromiseRejectMessage message);
|
NODE_EXTERN void PromiseRejectCallback(v8::PromiseRejectMessage message);
|
||||||
@ -730,6 +816,12 @@ class NODE_EXTERN CommonEnvironmentSetup {
|
|||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
std::vector<std::string>* errors,
|
std::vector<std::string>* errors,
|
||||||
EnvironmentArgs&&... env_args);
|
EnvironmentArgs&&... env_args);
|
||||||
|
template <typename... EnvironmentArgs>
|
||||||
|
static std::unique_ptr<CommonEnvironmentSetup> CreateWithSnapshot(
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
std::vector<std::string>* errors,
|
||||||
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
|
EnvironmentArgs&&... env_args);
|
||||||
|
|
||||||
struct uv_loop_s* event_loop() const;
|
struct uv_loop_s* event_loop() const;
|
||||||
std::shared_ptr<ArrayBufferAllocator> array_buffer_allocator() const;
|
std::shared_ptr<ArrayBufferAllocator> array_buffer_allocator() const;
|
||||||
@ -750,6 +842,11 @@ class NODE_EXTERN CommonEnvironmentSetup {
|
|||||||
MultiIsolatePlatform*,
|
MultiIsolatePlatform*,
|
||||||
std::vector<std::string>*,
|
std::vector<std::string>*,
|
||||||
std::function<Environment*(const CommonEnvironmentSetup*)>);
|
std::function<Environment*(const CommonEnvironmentSetup*)>);
|
||||||
|
CommonEnvironmentSetup(
|
||||||
|
MultiIsolatePlatform*,
|
||||||
|
std::vector<std::string>*,
|
||||||
|
const EmbedderSnapshotData*,
|
||||||
|
std::function<Environment*(const CommonEnvironmentSetup*)>);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Implementation for CommonEnvironmentSetup::Create
|
// Implementation for CommonEnvironmentSetup::Create
|
||||||
@ -768,6 +865,27 @@ std::unique_ptr<CommonEnvironmentSetup> CommonEnvironmentSetup::Create(
|
|||||||
if (!errors->empty()) ret.reset();
|
if (!errors->empty()) ret.reset();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
// Implementation for ::CreateWithSnapshot -- the ::Create() method
|
||||||
|
// could call this with a nullptr snapshot_data in a major version.
|
||||||
|
template <typename... EnvironmentArgs>
|
||||||
|
std::unique_ptr<CommonEnvironmentSetup>
|
||||||
|
CommonEnvironmentSetup::CreateWithSnapshot(
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
std::vector<std::string>* errors,
|
||||||
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
|
EnvironmentArgs&&... env_args) {
|
||||||
|
auto ret = std::unique_ptr<CommonEnvironmentSetup>(new CommonEnvironmentSetup(
|
||||||
|
platform,
|
||||||
|
errors,
|
||||||
|
snapshot_data,
|
||||||
|
[&](const CommonEnvironmentSetup* setup) -> Environment* {
|
||||||
|
return CreateEnvironment(setup->isolate_data(),
|
||||||
|
setup->context(),
|
||||||
|
std::forward<EnvironmentArgs>(env_args)...);
|
||||||
|
}));
|
||||||
|
if (!errors->empty()) ret.reset();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Converts a unixtime to V8 Date */
|
/* Converts a unixtime to V8 Date */
|
||||||
NODE_DEPRECATED("Use v8::Date::New() directly",
|
NODE_DEPRECATED("Use v8::Date::New() directly",
|
||||||
|
@ -117,9 +117,7 @@ BaseObjectPtr<ContextifyContext> ContextifyContext::New(
|
|||||||
InitializeGlobalTemplates(env->isolate_data());
|
InitializeGlobalTemplates(env->isolate_data());
|
||||||
Local<ObjectTemplate> object_template = env->contextify_global_template();
|
Local<ObjectTemplate> object_template = env->contextify_global_template();
|
||||||
DCHECK(!object_template.IsEmpty());
|
DCHECK(!object_template.IsEmpty());
|
||||||
bool use_node_snapshot = per_process::cli_options->node_snapshot;
|
const SnapshotData* snapshot_data = env->isolate_data()->snapshot_data();
|
||||||
const SnapshotData* snapshot_data =
|
|
||||||
use_node_snapshot ? SnapshotBuilder::GetEmbeddedSnapshotData() : nullptr;
|
|
||||||
|
|
||||||
MicrotaskQueue* queue =
|
MicrotaskQueue* queue =
|
||||||
options.microtask_queue_wrap
|
options.microtask_queue_wrap
|
||||||
|
@ -304,7 +304,8 @@ void DefineZlibConstants(v8::Local<v8::Object> target);
|
|||||||
v8::Isolate* NewIsolate(v8::Isolate::CreateParams* params,
|
v8::Isolate* NewIsolate(v8::Isolate::CreateParams* params,
|
||||||
uv_loop_t* event_loop,
|
uv_loop_t* event_loop,
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
bool has_snapshot_data = false);
|
const SnapshotData* snapshot_data = nullptr,
|
||||||
|
const IsolateSettings& settings = {});
|
||||||
// This overload automatically picks the right 'main_script_id' if no callback
|
// This overload automatically picks the right 'main_script_id' if no callback
|
||||||
// was provided by the embedder.
|
// was provided by the embedder.
|
||||||
v8::MaybeLocal<v8::Value> StartExecution(Environment* env,
|
v8::MaybeLocal<v8::Value> StartExecution(Environment* env,
|
||||||
|
@ -71,22 +71,18 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data,
|
|||||||
isolate_params_(std::make_unique<Isolate::CreateParams>()),
|
isolate_params_(std::make_unique<Isolate::CreateParams>()),
|
||||||
snapshot_data_(snapshot_data) {
|
snapshot_data_(snapshot_data) {
|
||||||
isolate_params_->array_buffer_allocator = array_buffer_allocator_.get();
|
isolate_params_->array_buffer_allocator = array_buffer_allocator_.get();
|
||||||
if (snapshot_data != nullptr) {
|
|
||||||
SnapshotBuilder::InitializeIsolateParams(snapshot_data,
|
|
||||||
isolate_params_.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
isolate_ = NewIsolate(
|
isolate_ =
|
||||||
isolate_params_.get(), event_loop, platform, snapshot_data != nullptr);
|
NewIsolate(isolate_params_.get(), event_loop, platform, snapshot_data);
|
||||||
CHECK_NOT_NULL(isolate_);
|
CHECK_NOT_NULL(isolate_);
|
||||||
|
|
||||||
// If the indexes are not nullptr, we are not deserializing
|
// If the indexes are not nullptr, we are not deserializing
|
||||||
isolate_data_ = std::make_unique<IsolateData>(
|
isolate_data_.reset(
|
||||||
isolate_,
|
CreateIsolateData(isolate_,
|
||||||
event_loop,
|
event_loop,
|
||||||
platform,
|
platform,
|
||||||
array_buffer_allocator_.get(),
|
array_buffer_allocator_.get(),
|
||||||
snapshot_data == nullptr ? nullptr : &(snapshot_data->isolate_data_info));
|
snapshot_data->AsEmbedderWrapper().get()));
|
||||||
|
|
||||||
isolate_data_->max_young_gen_size =
|
isolate_data_->max_young_gen_size =
|
||||||
isolate_params_->constraints.max_young_generation_size_in_bytes();
|
isolate_params_->constraints.max_young_generation_size_in_bytes();
|
||||||
@ -152,33 +148,10 @@ NodeMainInstance::CreateMainEnvironment(ExitCode* exit_code) {
|
|||||||
DeleteFnPtr<Environment, FreeEnvironment> env;
|
DeleteFnPtr<Environment, FreeEnvironment> env;
|
||||||
|
|
||||||
if (snapshot_data_ != nullptr) {
|
if (snapshot_data_ != nullptr) {
|
||||||
env.reset(new Environment(isolate_data_.get(),
|
env.reset(CreateEnvironment(isolate_data_.get(),
|
||||||
isolate_,
|
Local<Context>(), // read from snapshot
|
||||||
args_,
|
args_,
|
||||||
exec_args_,
|
exec_args_));
|
||||||
&(snapshot_data_->env_info),
|
|
||||||
EnvironmentFlags::kDefaultFlags,
|
|
||||||
{}));
|
|
||||||
#ifdef NODE_V8_SHARED_RO_HEAP
|
|
||||||
// TODO(addaleax): Do this as part of creating the Environment
|
|
||||||
// once we store the SnapshotData* itself on IsolateData.
|
|
||||||
env->builtin_loader()->RefreshCodeCache(snapshot_data_->code_cache);
|
|
||||||
#endif
|
|
||||||
context = Context::FromSnapshot(isolate_,
|
|
||||||
SnapshotData::kNodeMainContextIndex,
|
|
||||||
{DeserializeNodeInternalFields, env.get()})
|
|
||||||
.ToLocalChecked();
|
|
||||||
|
|
||||||
CHECK(!context.IsEmpty());
|
|
||||||
Context::Scope context_scope(context);
|
|
||||||
|
|
||||||
CHECK(InitializeContextRuntime(context).IsJust());
|
|
||||||
SetIsolateErrorHandlers(isolate_, {});
|
|
||||||
env->InitializeMainContext(context, &(snapshot_data_->env_info));
|
|
||||||
#if HAVE_INSPECTOR
|
|
||||||
env->InitializeInspector({});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_OPENSSL
|
#if HAVE_OPENSSL
|
||||||
crypto::InitCryptoOnce(isolate_);
|
crypto::InitCryptoOnce(isolate_);
|
||||||
#endif // HAVE_OPENSSL
|
#endif // HAVE_OPENSSL
|
||||||
|
@ -862,6 +862,15 @@ void SnapshotData::ToBlob(FILE* out) const {
|
|||||||
w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
|
w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SnapshotData* SnapshotData::FromEmbedderWrapper(
|
||||||
|
const EmbedderSnapshotData* data) {
|
||||||
|
return data != nullptr ? data->impl_ : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbedderSnapshotData::Pointer SnapshotData::AsEmbedderWrapper() const {
|
||||||
|
return EmbedderSnapshotData::Pointer{new EmbedderSnapshotData(this, false)};
|
||||||
|
}
|
||||||
|
|
||||||
bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
|
bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
|
||||||
CHECK_EQ(ftell(in), 0);
|
CHECK_EQ(ftell(in), 0);
|
||||||
int err = fseek(in, 0, SEEK_END);
|
int err = fseek(in, 0, SEEK_END);
|
||||||
@ -1082,6 +1091,8 @@ const std::vector<intptr_t>& SnapshotBuilder::CollectExternalReferences() {
|
|||||||
|
|
||||||
void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
|
void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
|
||||||
Isolate::CreateParams* params) {
|
Isolate::CreateParams* params) {
|
||||||
|
CHECK_NULL(params->external_references);
|
||||||
|
CHECK_NULL(params->snapshot_blob);
|
||||||
params->external_references = CollectExternalReferences().data();
|
params->external_references = CollectExternalReferences().data();
|
||||||
params->snapshot_blob =
|
params->snapshot_blob =
|
||||||
const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
|
const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
|
||||||
|
@ -584,9 +584,7 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
|
|||||||
exec_argv_out = env->exec_argv();
|
exec_argv_out = env->exec_argv();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool use_node_snapshot = per_process::cli_options->node_snapshot;
|
const SnapshotData* snapshot_data = env->isolate_data()->snapshot_data();
|
||||||
const SnapshotData* snapshot_data =
|
|
||||||
use_node_snapshot ? SnapshotBuilder::GetEmbeddedSnapshotData() : nullptr;
|
|
||||||
|
|
||||||
Worker* worker = new Worker(env,
|
Worker* worker = new Worker(env,
|
||||||
args.This(),
|
args.This(),
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
// Note: This file is being referred to from doc/api/embedding.md, and excerpts
|
// Note: This file is being referred to from doc/api/embedding.md, and excerpts
|
||||||
// from it are included in the documentation. Try to keep these in sync.
|
// from it are included in the documentation. Try to keep these in sync.
|
||||||
|
// Snapshot support is not part of the embedder API docs yet due to its
|
||||||
|
// experimental nature, although it is of course documented in node.h.
|
||||||
|
|
||||||
using node::CommonEnvironmentSetup;
|
using node::CommonEnvironmentSetup;
|
||||||
using node::Environment;
|
using node::Environment;
|
||||||
@ -55,9 +57,22 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
|
|||||||
const std::vector<std::string>& exec_args) {
|
const std::vector<std::string>& exec_args) {
|
||||||
int exit_code = 0;
|
int exit_code = 0;
|
||||||
|
|
||||||
|
node::EmbedderSnapshotData::Pointer snapshot;
|
||||||
|
auto snapshot_arg_it =
|
||||||
|
std::find(args.begin(), args.end(), "--embedder-snapshot-blob");
|
||||||
|
if (snapshot_arg_it < args.end() - 1) {
|
||||||
|
FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "r");
|
||||||
|
assert(fp != nullptr);
|
||||||
|
snapshot = node::EmbedderSnapshotData::FromFile(fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> errors;
|
std::vector<std::string> errors;
|
||||||
std::unique_ptr<CommonEnvironmentSetup> setup =
|
std::unique_ptr<CommonEnvironmentSetup> setup =
|
||||||
CommonEnvironmentSetup::Create(platform, &errors, args, exec_args);
|
snapshot
|
||||||
|
? CommonEnvironmentSetup::CreateWithSnapshot(
|
||||||
|
platform, &errors, snapshot.get(), args, exec_args)
|
||||||
|
: CommonEnvironmentSetup::Create(platform, &errors, args, exec_args);
|
||||||
if (!setup) {
|
if (!setup) {
|
||||||
for (const std::string& err : errors)
|
for (const std::string& err : errors)
|
||||||
fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
|
fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
|
||||||
@ -73,13 +88,18 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
|
|||||||
HandleScope handle_scope(isolate);
|
HandleScope handle_scope(isolate);
|
||||||
Context::Scope context_scope(setup->context());
|
Context::Scope context_scope(setup->context());
|
||||||
|
|
||||||
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
|
MaybeLocal<Value> loadenv_ret;
|
||||||
env,
|
if (snapshot) {
|
||||||
"const publicRequire ="
|
loadenv_ret = node::LoadEnvironment(env, node::StartExecutionCallback{});
|
||||||
" require('module').createRequire(process.cwd() + '/');"
|
} else {
|
||||||
"globalThis.require = publicRequire;"
|
loadenv_ret = node::LoadEnvironment(
|
||||||
"globalThis.embedVars = { nön_ascıı: '🏳️🌈' };"
|
env,
|
||||||
"require('vm').runInThisContext(process.argv[1]);");
|
"const publicRequire ="
|
||||||
|
" require('module').createRequire(process.cwd() + '/');"
|
||||||
|
"globalThis.require = publicRequire;"
|
||||||
|
"globalThis.embedVars = { nön_ascıı: '🏳️🌈' };"
|
||||||
|
"require('vm').runInThisContext(process.argv[1]);");
|
||||||
|
}
|
||||||
|
|
||||||
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
|
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1,17 +1,26 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
const fixtures = require('../common/fixtures');
|
const fixtures = require('../common/fixtures');
|
||||||
|
const tmpdir = require('../common/tmpdir');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const child_process = require('child_process');
|
const child_process = require('child_process');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
tmpdir.refresh();
|
||||||
common.allowGlobals(global.require);
|
common.allowGlobals(global.require);
|
||||||
common.allowGlobals(global.embedVars);
|
common.allowGlobals(global.embedVars);
|
||||||
let binary = `out/${common.buildType}/embedtest`;
|
|
||||||
if (common.isWindows) {
|
function resolveBuiltBinary(bin) {
|
||||||
binary += '.exe';
|
let binary = `out/${common.buildType}/${bin}`;
|
||||||
|
if (common.isWindows) {
|
||||||
|
binary += '.exe';
|
||||||
|
}
|
||||||
|
return path.resolve(__dirname, '..', '..', binary);
|
||||||
}
|
}
|
||||||
binary = path.resolve(__dirname, '..', '..', binary);
|
|
||||||
|
const binary = resolveBuiltBinary('embedtest');
|
||||||
|
const standaloneNodeBinary = resolveBuiltBinary('node');
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
child_process.spawnSync(binary, ['console.log(42)'])
|
child_process.spawnSync(binary, ['console.log(42)'])
|
||||||
@ -41,3 +50,39 @@ const fixturePath = JSON.stringify(fixtures.path('exit.js'));
|
|||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
child_process.spawnSync(binary, [`require(${fixturePath})`, 92]).status,
|
child_process.spawnSync(binary, [`require(${fixturePath})`, 92]).status,
|
||||||
92);
|
92);
|
||||||
|
|
||||||
|
// Basic snapshot support
|
||||||
|
{
|
||||||
|
const snapshotFixture = fixtures.path('snapshot', 'echo-args.js');
|
||||||
|
const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob');
|
||||||
|
const buildSnapshotArgs = [snapshotFixture, 'arg1', 'arg2'];
|
||||||
|
const runEmbeddedArgs = ['--embedder-snapshot-blob', blobPath, 'arg3', 'arg4'];
|
||||||
|
|
||||||
|
fs.rmSync(blobPath, { force: true });
|
||||||
|
assert.strictEqual(child_process.spawnSync(standaloneNodeBinary, [
|
||||||
|
'--snapshot-blob', blobPath, '--build-snapshot', ...buildSnapshotArgs,
|
||||||
|
], {
|
||||||
|
cwd: tmpdir.path,
|
||||||
|
}).status, 0);
|
||||||
|
const spawnResult = child_process.spawnSync(binary, ['--', ...runEmbeddedArgs]);
|
||||||
|
assert.deepStrictEqual(JSON.parse(spawnResult.stdout), {
|
||||||
|
originalArgv: [standaloneNodeBinary, ...buildSnapshotArgs],
|
||||||
|
currentArgv: [binary, ...runEmbeddedArgs],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create workers and vm contexts after deserialization
|
||||||
|
{
|
||||||
|
const snapshotFixture = fixtures.path('snapshot', 'create-worker-and-vm.js');
|
||||||
|
const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob');
|
||||||
|
|
||||||
|
fs.rmSync(blobPath, { force: true });
|
||||||
|
assert.strictEqual(child_process.spawnSync(standaloneNodeBinary, [
|
||||||
|
'--snapshot-blob', blobPath, '--build-snapshot', snapshotFixture,
|
||||||
|
], {
|
||||||
|
cwd: tmpdir.path,
|
||||||
|
}).status, 0);
|
||||||
|
assert.strictEqual(
|
||||||
|
child_process.spawnSync(binary, ['--', '--embedder-snapshot-blob', blobPath]).status,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
20
test/fixtures/snapshot/create-worker-and-vm.js
vendored
Normal file
20
test/fixtures/snapshot/create-worker-and-vm.js
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const {
|
||||||
|
setDeserializeMainFunction,
|
||||||
|
} = require('v8').startupSnapshot;
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
setDeserializeMainFunction(() => {
|
||||||
|
const vm = require('vm');
|
||||||
|
const { Worker } = require('worker_threads');
|
||||||
|
assert.strictEqual(vm.runInNewContext('21+21'), 42);
|
||||||
|
const worker = new Worker(
|
||||||
|
'require("worker_threads").parentPort.postMessage({value: 21 + 21})',
|
||||||
|
{ eval: true });
|
||||||
|
|
||||||
|
const messages = [];
|
||||||
|
worker.on('message', message => messages.push(message));
|
||||||
|
|
||||||
|
process.on('beforeExit', () => {
|
||||||
|
assert.deepStrictEqual(messages, [{value:42}]);
|
||||||
|
})
|
||||||
|
});
|
12
test/fixtures/snapshot/echo-args.js
vendored
Normal file
12
test/fixtures/snapshot/echo-args.js
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const {
|
||||||
|
setDeserializeMainFunction,
|
||||||
|
} = require('v8').startupSnapshot;
|
||||||
|
|
||||||
|
const originalArgv = [...process.argv];
|
||||||
|
|
||||||
|
setDeserializeMainFunction(() => {
|
||||||
|
console.log(JSON.stringify({
|
||||||
|
currentArgv: process.argv,
|
||||||
|
originalArgv
|
||||||
|
}));
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user