src: allow snapshotting from the embedder API
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
02fad4f40a
commit
bfadee5e68
@ -12,6 +12,7 @@ const {
|
|||||||
const binding = internalBinding('mksnapshot');
|
const binding = internalBinding('mksnapshot');
|
||||||
const { BuiltinModule } = require('internal/bootstrap/loaders');
|
const { BuiltinModule } = require('internal/bootstrap/loaders');
|
||||||
const {
|
const {
|
||||||
|
getEmbedderEntryFunction,
|
||||||
compileSerializeMain,
|
compileSerializeMain,
|
||||||
} = binding;
|
} = binding;
|
||||||
|
|
||||||
@ -119,14 +120,21 @@ function main() {
|
|||||||
prepareMainThreadExecution
|
prepareMainThreadExecution
|
||||||
} = require('internal/process/pre_execution');
|
} = require('internal/process/pre_execution');
|
||||||
|
|
||||||
prepareMainThreadExecution(true, false);
|
let serializeMainFunction = getEmbedderEntryFunction();
|
||||||
|
const serializeMainArgs = [requireForUserSnapshot];
|
||||||
|
|
||||||
|
if (serializeMainFunction) { // embedded case
|
||||||
|
prepareMainThreadExecution(false, false);
|
||||||
|
} else {
|
||||||
|
prepareMainThreadExecution(true, false);
|
||||||
const file = process.argv[1];
|
const file = process.argv[1];
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const filename = path.resolve(file);
|
const filename = path.resolve(file);
|
||||||
const dirname = path.dirname(filename);
|
const dirname = path.dirname(filename);
|
||||||
const source = readFileSync(file, 'utf-8');
|
const source = readFileSync(file, 'utf-8');
|
||||||
const serializeMainFunction = compileSerializeMain(filename, source);
|
serializeMainFunction = compileSerializeMain(filename, source);
|
||||||
|
serializeMainArgs.push(filename, dirname);
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
initializeCallbacks,
|
initializeCallbacks,
|
||||||
@ -146,10 +154,9 @@ function main() {
|
|||||||
|
|
||||||
if (getOptionValue('--inspect-brk')) {
|
if (getOptionValue('--inspect-brk')) {
|
||||||
internalBinding('inspector').callAndPauseOnStart(
|
internalBinding('inspector').callAndPauseOnStart(
|
||||||
serializeMainFunction, undefined,
|
serializeMainFunction, undefined, ...serializeMainArgs);
|
||||||
requireForUserSnapshot, filename, dirname);
|
|
||||||
} else {
|
} else {
|
||||||
serializeMainFunction(requireForUserSnapshot, filename, dirname);
|
serializeMainFunction(...serializeMainArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSerializeCallback(() => {
|
addSerializeCallback(() => {
|
||||||
|
@ -14,6 +14,7 @@ using v8::Locker;
|
|||||||
using v8::Maybe;
|
using v8::Maybe;
|
||||||
using v8::Nothing;
|
using v8::Nothing;
|
||||||
using v8::SealHandleScope;
|
using v8::SealHandleScope;
|
||||||
|
using v8::SnapshotCreator;
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
@ -78,16 +79,18 @@ struct CommonEnvironmentSetup::Impl {
|
|||||||
MultiIsolatePlatform* platform = nullptr;
|
MultiIsolatePlatform* platform = nullptr;
|
||||||
uv_loop_t loop;
|
uv_loop_t loop;
|
||||||
std::shared_ptr<ArrayBufferAllocator> allocator;
|
std::shared_ptr<ArrayBufferAllocator> allocator;
|
||||||
|
std::optional<SnapshotCreator> snapshot_creator;
|
||||||
Isolate* isolate = nullptr;
|
Isolate* isolate = nullptr;
|
||||||
DeleteFnPtr<IsolateData, FreeIsolateData> isolate_data;
|
DeleteFnPtr<IsolateData, FreeIsolateData> isolate_data;
|
||||||
DeleteFnPtr<Environment, FreeEnvironment> env;
|
DeleteFnPtr<Environment, FreeEnvironment> env;
|
||||||
Global<Context> context;
|
Global<Context> main_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
CommonEnvironmentSetup::CommonEnvironmentSetup(
|
CommonEnvironmentSetup::CommonEnvironmentSetup(
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
std::vector<std::string>* errors,
|
std::vector<std::string>* errors,
|
||||||
const EmbedderSnapshotData* snapshot_data,
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
|
uint32_t flags,
|
||||||
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);
|
||||||
@ -105,28 +108,43 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
|
|||||||
}
|
}
|
||||||
loop->data = this;
|
loop->data = this;
|
||||||
|
|
||||||
|
Isolate* isolate;
|
||||||
|
if (flags & Flags::kIsForSnapshotting) {
|
||||||
|
const std::vector<intptr_t>& external_references =
|
||||||
|
SnapshotBuilder::CollectExternalReferences();
|
||||||
|
isolate = impl_->isolate = Isolate::Allocate();
|
||||||
|
// Must be done before the SnapshotCreator creation so that the
|
||||||
|
// memory reducer can be initialized.
|
||||||
|
platform->RegisterIsolate(isolate, loop);
|
||||||
|
impl_->snapshot_creator.emplace(isolate, external_references.data());
|
||||||
|
isolate->SetCaptureStackTraceForUncaughtExceptions(
|
||||||
|
true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
|
||||||
|
SetIsolateMiscHandlers(isolate, {});
|
||||||
|
} else {
|
||||||
impl_->allocator = ArrayBufferAllocator::Create();
|
impl_->allocator = ArrayBufferAllocator::Create();
|
||||||
impl_->isolate =
|
isolate = impl_->isolate =
|
||||||
NewIsolate(impl_->allocator, &impl_->loop, platform, snapshot_data);
|
NewIsolate(impl_->allocator, &impl_->loop, platform, snapshot_data);
|
||||||
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(), snapshot_data));
|
isolate, loop, platform, impl_->allocator.get(), snapshot_data));
|
||||||
|
impl_->isolate_data->options()->build_snapshot =
|
||||||
|
impl_->snapshot_creator.has_value();
|
||||||
|
|
||||||
HandleScope handle_scope(isolate);
|
HandleScope handle_scope(isolate);
|
||||||
if (snapshot_data) {
|
if (snapshot_data) {
|
||||||
impl_->env.reset(make_env(this));
|
impl_->env.reset(make_env(this));
|
||||||
if (impl_->env) {
|
if (impl_->env) {
|
||||||
impl_->context.Reset(isolate, impl_->env->context());
|
impl_->main_context.Reset(isolate, impl_->env->context());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Local<Context> context = NewContext(isolate);
|
Local<Context> context = NewContext(isolate);
|
||||||
impl_->context.Reset(isolate, context);
|
impl_->main_context.Reset(isolate, context);
|
||||||
if (context.IsEmpty()) {
|
if (context.IsEmpty()) {
|
||||||
errors->push_back("Failed to initialize V8 Context");
|
errors->push_back("Failed to initialize V8 Context");
|
||||||
return;
|
return;
|
||||||
@ -141,7 +159,37 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
|
|||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
std::vector<std::string>* errors,
|
std::vector<std::string>* errors,
|
||||||
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
|
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
|
||||||
: CommonEnvironmentSetup(platform, errors, nullptr, make_env) {}
|
: CommonEnvironmentSetup(platform, errors, nullptr, false, make_env) {}
|
||||||
|
|
||||||
|
std::unique_ptr<CommonEnvironmentSetup>
|
||||||
|
CommonEnvironmentSetup::CreateForSnapshotting(
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
std::vector<std::string>* errors,
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
const std::vector<std::string>& exec_args) {
|
||||||
|
// It's not guaranteed that a context that goes through
|
||||||
|
// v8_inspector::V8Inspector::contextCreated() is runtime-independent,
|
||||||
|
// so do not start the inspector on the main context when building
|
||||||
|
// the default snapshot.
|
||||||
|
uint64_t env_flags =
|
||||||
|
EnvironmentFlags::kDefaultFlags | EnvironmentFlags::kNoCreateInspector;
|
||||||
|
|
||||||
|
auto ret = std::unique_ptr<CommonEnvironmentSetup>(new CommonEnvironmentSetup(
|
||||||
|
platform,
|
||||||
|
errors,
|
||||||
|
nullptr,
|
||||||
|
true,
|
||||||
|
[&](const CommonEnvironmentSetup* setup) -> Environment* {
|
||||||
|
return CreateEnvironment(
|
||||||
|
setup->isolate_data(),
|
||||||
|
setup->context(),
|
||||||
|
args,
|
||||||
|
exec_args,
|
||||||
|
static_cast<EnvironmentFlags::Flags>(env_flags));
|
||||||
|
}));
|
||||||
|
if (!errors->empty()) ret.reset();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
CommonEnvironmentSetup::~CommonEnvironmentSetup() {
|
CommonEnvironmentSetup::~CommonEnvironmentSetup() {
|
||||||
if (impl_->isolate != nullptr) {
|
if (impl_->isolate != nullptr) {
|
||||||
@ -150,7 +198,7 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() {
|
|||||||
Locker locker(isolate);
|
Locker locker(isolate);
|
||||||
Isolate::Scope isolate_scope(isolate);
|
Isolate::Scope isolate_scope(isolate);
|
||||||
|
|
||||||
impl_->context.Reset();
|
impl_->main_context.Reset();
|
||||||
impl_->env.reset();
|
impl_->env.reset();
|
||||||
impl_->isolate_data.reset();
|
impl_->isolate_data.reset();
|
||||||
}
|
}
|
||||||
@ -160,6 +208,9 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() {
|
|||||||
*static_cast<bool*>(data) = true;
|
*static_cast<bool*>(data) = true;
|
||||||
}, &platform_finished);
|
}, &platform_finished);
|
||||||
impl_->platform->UnregisterIsolate(isolate);
|
impl_->platform->UnregisterIsolate(isolate);
|
||||||
|
if (impl_->snapshot_creator.has_value())
|
||||||
|
impl_->snapshot_creator.reset();
|
||||||
|
else
|
||||||
isolate->Dispose();
|
isolate->Dispose();
|
||||||
|
|
||||||
// Wait until the platform has cleaned up all relevant resources.
|
// Wait until the platform has cleaned up all relevant resources.
|
||||||
@ -173,6 +224,21 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() {
|
|||||||
delete impl_;
|
delete impl_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EmbedderSnapshotData::Pointer CommonEnvironmentSetup::CreateSnapshot() {
|
||||||
|
CHECK_NOT_NULL(snapshot_creator());
|
||||||
|
SnapshotData* snapshot_data = new SnapshotData();
|
||||||
|
EmbedderSnapshotData::Pointer result{
|
||||||
|
new EmbedderSnapshotData(snapshot_data, true)};
|
||||||
|
|
||||||
|
auto exit_code = SnapshotBuilder::CreateSnapshot(
|
||||||
|
snapshot_data,
|
||||||
|
this,
|
||||||
|
static_cast<uint8_t>(SnapshotMetadata::Type::kFullyCustomized));
|
||||||
|
if (exit_code != ExitCode::kNoFailure) return {};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<int> SpinEventLoop(Environment* env) {
|
Maybe<int> SpinEventLoop(Environment* env) {
|
||||||
Maybe<ExitCode> result = SpinEventLoopInternal(env);
|
Maybe<ExitCode> result = SpinEventLoopInternal(env);
|
||||||
if (result.IsNothing()) {
|
if (result.IsNothing()) {
|
||||||
@ -203,7 +269,11 @@ Environment* CommonEnvironmentSetup::env() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v8::Local<v8::Context> CommonEnvironmentSetup::context() const {
|
v8::Local<v8::Context> CommonEnvironmentSetup::context() const {
|
||||||
return impl_->context.Get(impl_->isolate);
|
return impl_->main_context.Get(impl_->isolate);
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::SnapshotCreator* CommonEnvironmentSetup::snapshot_creator() {
|
||||||
|
return impl_->snapshot_creator ? &impl_->snapshot_creator.value() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmbedderSnapshotData::DeleteSnapshotData::operator()(
|
void EmbedderSnapshotData::DeleteSnapshotData::operator()(
|
||||||
@ -232,6 +302,10 @@ EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmbedderSnapshotData::ToFile(FILE* out) const {
|
||||||
|
impl_->ToBlob(out);
|
||||||
|
}
|
||||||
|
|
||||||
EmbedderSnapshotData::EmbedderSnapshotData(const SnapshotData* impl,
|
EmbedderSnapshotData::EmbedderSnapshotData(const SnapshotData* impl,
|
||||||
bool owns_impl)
|
bool owns_impl)
|
||||||
: impl_(impl), owns_impl_(owns_impl) {}
|
: impl_(impl), owns_impl_(owns_impl) {}
|
||||||
|
@ -444,6 +444,16 @@ inline builtins::BuiltinLoader* Environment::builtin_loader() {
|
|||||||
return &builtin_loader_;
|
return &builtin_loader_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const StartExecutionCallback&
|
||||||
|
Environment::embedder_mksnapshot_entry_point() const {
|
||||||
|
return embedder_mksnapshot_entry_point_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Environment::set_embedder_mksnapshot_entry_point(
|
||||||
|
StartExecutionCallback&& fn) {
|
||||||
|
embedder_mksnapshot_entry_point_ = std::move(fn);
|
||||||
|
}
|
||||||
|
|
||||||
inline double Environment::new_async_id() {
|
inline double Environment::new_async_id() {
|
||||||
async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter] += 1;
|
async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter] += 1;
|
||||||
return async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter];
|
return async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter];
|
||||||
|
@ -961,6 +961,9 @@ class Environment : public MemoryRetainer {
|
|||||||
|
|
||||||
#endif // HAVE_INSPECTOR
|
#endif // HAVE_INSPECTOR
|
||||||
|
|
||||||
|
inline const StartExecutionCallback& embedder_mksnapshot_entry_point() const;
|
||||||
|
inline void set_embedder_mksnapshot_entry_point(StartExecutionCallback&& fn);
|
||||||
|
|
||||||
inline void set_process_exit_handler(
|
inline void set_process_exit_handler(
|
||||||
std::function<void(Environment*, ExitCode)>&& handler);
|
std::function<void(Environment*, ExitCode)>&& handler);
|
||||||
|
|
||||||
@ -1144,6 +1147,7 @@ class Environment : public MemoryRetainer {
|
|||||||
std::unique_ptr<Realm> principal_realm_ = nullptr;
|
std::unique_ptr<Realm> principal_realm_ = nullptr;
|
||||||
|
|
||||||
builtins::BuiltinLoader builtin_loader_;
|
builtins::BuiltinLoader builtin_loader_;
|
||||||
|
StartExecutionCallback embedder_mksnapshot_entry_point_;
|
||||||
|
|
||||||
// Used by allocate_managed_buffer() and release_managed_buffer() to keep
|
// Used by allocate_managed_buffer() and release_managed_buffer() to keep
|
||||||
// track of the BackingStore for a given pointer.
|
// track of the BackingStore for a given pointer.
|
||||||
|
17
src/node.cc
17
src/node.cc
@ -276,14 +276,21 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
|
|||||||
if (cb != nullptr) {
|
if (cb != nullptr) {
|
||||||
EscapableHandleScope scope(env->isolate());
|
EscapableHandleScope scope(env->isolate());
|
||||||
|
|
||||||
if (StartExecution(env, "internal/main/environment").IsEmpty()) return {};
|
if (env->isolate_data()->options()->build_snapshot) {
|
||||||
|
// TODO(addaleax): pass the callback to the main script more directly,
|
||||||
|
// e.g. by making StartExecution(env, builtin) parametrizable
|
||||||
|
env->set_embedder_mksnapshot_entry_point(std::move(cb));
|
||||||
|
auto reset_entry_point =
|
||||||
|
OnScopeLeave([&]() { env->set_embedder_mksnapshot_entry_point({}); });
|
||||||
|
|
||||||
StartExecutionCallbackInfo info = {
|
return StartExecution(env, "internal/main/mksnapshot");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartExecution(env, "internal/main/environment").IsEmpty()) return {};
|
||||||
|
return scope.EscapeMaybe(cb({
|
||||||
env->process_object(),
|
env->process_object(),
|
||||||
env->builtin_module_require(),
|
env->builtin_module_require(),
|
||||||
};
|
}));
|
||||||
|
|
||||||
return scope.EscapeMaybe(cb(info));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(joyeecheung): move these conditions into JS land and let the
|
// TODO(joyeecheung): move these conditions into JS land and let the
|
||||||
|
44
src/node.h
44
src/node.h
@ -511,11 +511,16 @@ class EmbedderSnapshotData {
|
|||||||
static Pointer BuiltinSnapshotData();
|
static Pointer BuiltinSnapshotData();
|
||||||
|
|
||||||
// Return an EmbedderSnapshotData object that is based on an input file.
|
// Return an EmbedderSnapshotData object that is based on an input file.
|
||||||
// Calling this method will not consume but not close the FILE* handle.
|
// Calling this method will consume but not close the FILE* handle.
|
||||||
// The FILE* handle can be closed immediately following this call.
|
// The FILE* handle can be closed immediately following this call.
|
||||||
// If the snapshot is invalid, this returns an empty pointer.
|
// If the snapshot is invalid, this returns an empty pointer.
|
||||||
static Pointer FromFile(FILE* in);
|
static Pointer FromFile(FILE* in);
|
||||||
|
|
||||||
|
// Write this EmbedderSnapshotData object to an output file.
|
||||||
|
// Calling this method will not close the FILE* handle.
|
||||||
|
// The FILE* handle can be closed immediately following this call.
|
||||||
|
void ToFile(FILE* out) const;
|
||||||
|
|
||||||
// Returns whether custom snapshots can be used. Currently, this means
|
// Returns whether custom snapshots can be used. Currently, this means
|
||||||
// that V8 was configured without the shared-readonly-heap feature.
|
// that V8 was configured without the shared-readonly-heap feature.
|
||||||
static bool CanUseCustomSnapshotPerIsolate();
|
static bool CanUseCustomSnapshotPerIsolate();
|
||||||
@ -532,6 +537,7 @@ class EmbedderSnapshotData {
|
|||||||
const SnapshotData* impl_;
|
const SnapshotData* impl_;
|
||||||
bool owns_impl_;
|
bool owns_impl_;
|
||||||
friend struct SnapshotData;
|
friend struct SnapshotData;
|
||||||
|
friend class CommonEnvironmentSetup;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Overriding IsolateSettings may produce unexpected behavior
|
// Overriding IsolateSettings may produce unexpected behavior
|
||||||
@ -817,13 +823,35 @@ class NODE_EXTERN CommonEnvironmentSetup {
|
|||||||
std::vector<std::string>* errors,
|
std::vector<std::string>* errors,
|
||||||
EnvironmentArgs&&... env_args);
|
EnvironmentArgs&&... env_args);
|
||||||
template <typename... EnvironmentArgs>
|
template <typename... EnvironmentArgs>
|
||||||
static std::unique_ptr<CommonEnvironmentSetup> CreateWithSnapshot(
|
static std::unique_ptr<CommonEnvironmentSetup> CreateFromSnapshot(
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
std::vector<std::string>* errors,
|
std::vector<std::string>* errors,
|
||||||
const EmbedderSnapshotData* snapshot_data,
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
EnvironmentArgs&&... env_args);
|
EnvironmentArgs&&... env_args);
|
||||||
|
|
||||||
|
// Create an embedding setup which will be used for creating a snapshot
|
||||||
|
// using CreateSnapshot().
|
||||||
|
//
|
||||||
|
// This will create and attach a v8::SnapshotCreator to this instance,
|
||||||
|
// and the same restrictions apply to this instance that also apply to
|
||||||
|
// other V8 snapshotting environments.
|
||||||
|
// Not all Node.js APIs are supported in this case. Currently, there is
|
||||||
|
// no support for native/host objects other than Node.js builtins
|
||||||
|
// in the snapshot.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
static std::unique_ptr<CommonEnvironmentSetup> CreateForSnapshotting(
|
||||||
|
MultiIsolatePlatform* platform,
|
||||||
|
std::vector<std::string>* errors,
|
||||||
|
const std::vector<std::string>& args = {},
|
||||||
|
const std::vector<std::string>& exec_args = {});
|
||||||
|
EmbedderSnapshotData::Pointer CreateSnapshot();
|
||||||
|
|
||||||
struct uv_loop_s* event_loop() const;
|
struct uv_loop_s* event_loop() const;
|
||||||
|
v8::SnapshotCreator* snapshot_creator();
|
||||||
|
// Empty for snapshotting environments.
|
||||||
std::shared_ptr<ArrayBufferAllocator> array_buffer_allocator() const;
|
std::shared_ptr<ArrayBufferAllocator> array_buffer_allocator() const;
|
||||||
v8::Isolate* isolate() const;
|
v8::Isolate* isolate() const;
|
||||||
IsolateData* isolate_data() const;
|
IsolateData* isolate_data() const;
|
||||||
@ -836,8 +864,14 @@ class NODE_EXTERN CommonEnvironmentSetup {
|
|||||||
CommonEnvironmentSetup& operator=(CommonEnvironmentSetup&&) = delete;
|
CommonEnvironmentSetup& operator=(CommonEnvironmentSetup&&) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum Flags : uint32_t {
|
||||||
|
kNoFlags = 0,
|
||||||
|
kIsForSnapshotting = 1,
|
||||||
|
};
|
||||||
|
|
||||||
struct Impl;
|
struct Impl;
|
||||||
Impl* impl_;
|
Impl* impl_;
|
||||||
|
|
||||||
CommonEnvironmentSetup(
|
CommonEnvironmentSetup(
|
||||||
MultiIsolatePlatform*,
|
MultiIsolatePlatform*,
|
||||||
std::vector<std::string>*,
|
std::vector<std::string>*,
|
||||||
@ -846,6 +880,7 @@ class NODE_EXTERN CommonEnvironmentSetup {
|
|||||||
MultiIsolatePlatform*,
|
MultiIsolatePlatform*,
|
||||||
std::vector<std::string>*,
|
std::vector<std::string>*,
|
||||||
const EmbedderSnapshotData*,
|
const EmbedderSnapshotData*,
|
||||||
|
uint32_t flags,
|
||||||
std::function<Environment*(const CommonEnvironmentSetup*)>);
|
std::function<Environment*(const CommonEnvironmentSetup*)>);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -865,11 +900,11 @@ 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
|
// Implementation for ::CreateFromSnapshot -- the ::Create() method
|
||||||
// could call this with a nullptr snapshot_data in a major version.
|
// could call this with a nullptr snapshot_data in a major version.
|
||||||
template <typename... EnvironmentArgs>
|
template <typename... EnvironmentArgs>
|
||||||
std::unique_ptr<CommonEnvironmentSetup>
|
std::unique_ptr<CommonEnvironmentSetup>
|
||||||
CommonEnvironmentSetup::CreateWithSnapshot(
|
CommonEnvironmentSetup::CreateFromSnapshot(
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
std::vector<std::string>* errors,
|
std::vector<std::string>* errors,
|
||||||
const EmbedderSnapshotData* snapshot_data,
|
const EmbedderSnapshotData* snapshot_data,
|
||||||
@ -878,6 +913,7 @@ CommonEnvironmentSetup::CreateWithSnapshot(
|
|||||||
platform,
|
platform,
|
||||||
errors,
|
errors,
|
||||||
snapshot_data,
|
snapshot_data,
|
||||||
|
Flags::kNoFlags,
|
||||||
[&](const CommonEnvironmentSetup* setup) -> Environment* {
|
[&](const CommonEnvironmentSetup* setup) -> Environment* {
|
||||||
return CreateEnvironment(setup->isolate_data(),
|
return CreateEnvironment(setup->isolate_data(),
|
||||||
setup->context(),
|
setup->context(),
|
||||||
|
@ -29,34 +29,6 @@ using v8::Isolate;
|
|||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::Locker;
|
using v8::Locker;
|
||||||
|
|
||||||
NodeMainInstance::NodeMainInstance(Isolate* isolate,
|
|
||||||
uv_loop_t* event_loop,
|
|
||||||
MultiIsolatePlatform* platform,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::vector<std::string>& exec_args)
|
|
||||||
: args_(args),
|
|
||||||
exec_args_(exec_args),
|
|
||||||
array_buffer_allocator_(nullptr),
|
|
||||||
isolate_(isolate),
|
|
||||||
platform_(platform),
|
|
||||||
isolate_data_(nullptr),
|
|
||||||
snapshot_data_(nullptr) {
|
|
||||||
isolate_data_ =
|
|
||||||
std::make_unique<IsolateData>(isolate_, event_loop, platform, nullptr);
|
|
||||||
|
|
||||||
SetIsolateMiscHandlers(isolate_, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<NodeMainInstance> NodeMainInstance::Create(
|
|
||||||
Isolate* isolate,
|
|
||||||
uv_loop_t* event_loop,
|
|
||||||
MultiIsolatePlatform* platform,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::vector<std::string>& exec_args) {
|
|
||||||
return std::unique_ptr<NodeMainInstance>(
|
|
||||||
new NodeMainInstance(isolate, event_loop, platform, args, exec_args));
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data,
|
NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data,
|
||||||
uv_loop_t* event_loop,
|
uv_loop_t* event_loop,
|
||||||
MultiIsolatePlatform* platform,
|
MultiIsolatePlatform* platform,
|
||||||
@ -88,13 +60,6 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data,
|
|||||||
isolate_params_->constraints.max_young_generation_size_in_bytes();
|
isolate_params_->constraints.max_young_generation_size_in_bytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeMainInstance::Dispose() {
|
|
||||||
// This should only be called on a main instance that does not own its
|
|
||||||
// isolate.
|
|
||||||
CHECK_NULL(isolate_params_);
|
|
||||||
platform_->DrainTasks(isolate_);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeMainInstance::~NodeMainInstance() {
|
NodeMainInstance::~NodeMainInstance() {
|
||||||
if (isolate_params_ == nullptr) {
|
if (isolate_params_ == nullptr) {
|
||||||
return;
|
return;
|
||||||
|
@ -22,33 +22,6 @@ struct SnapshotData;
|
|||||||
// We may be able to create an abstract class to reuse some of the routines.
|
// We may be able to create an abstract class to reuse some of the routines.
|
||||||
class NodeMainInstance {
|
class NodeMainInstance {
|
||||||
public:
|
public:
|
||||||
// To create a main instance that does not own the isolate,
|
|
||||||
// The caller needs to do:
|
|
||||||
//
|
|
||||||
// Isolate* isolate = Isolate::Allocate();
|
|
||||||
// platform->RegisterIsolate(isolate, loop);
|
|
||||||
// isolate->Initialize(...);
|
|
||||||
// isolate->Enter();
|
|
||||||
// std::unique_ptr<NodeMainInstance> main_instance =
|
|
||||||
// NodeMainInstance::Create(isolate, loop, args, exec_args);
|
|
||||||
//
|
|
||||||
// When tearing it down:
|
|
||||||
//
|
|
||||||
// main_instance->Cleanup(); // While the isolate is entered
|
|
||||||
// isolate->Exit();
|
|
||||||
// isolate->Dispose();
|
|
||||||
// platform->UnregisterIsolate(isolate);
|
|
||||||
//
|
|
||||||
// After calling Dispose() the main_instance is no longer accessible.
|
|
||||||
static std::unique_ptr<NodeMainInstance> Create(
|
|
||||||
v8::Isolate* isolate,
|
|
||||||
uv_loop_t* event_loop,
|
|
||||||
MultiIsolatePlatform* platform,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::vector<std::string>& exec_args);
|
|
||||||
|
|
||||||
void Dispose();
|
|
||||||
|
|
||||||
// Create a main instance that owns the isolate
|
// Create a main instance that owns the isolate
|
||||||
NodeMainInstance(const SnapshotData* snapshot_data,
|
NodeMainInstance(const SnapshotData* snapshot_data,
|
||||||
uv_loop_t* event_loop,
|
uv_loop_t* event_loop,
|
||||||
|
@ -778,8 +778,7 @@ PerIsolateOptionsParser::PerIsolateOptionsParser(
|
|||||||
Implies("--harmony-shadow-realm", "--experimental-shadow-realm");
|
Implies("--harmony-shadow-realm", "--experimental-shadow-realm");
|
||||||
ImpliesNot("--no-harmony-shadow-realm", "--experimental-shadow-realm");
|
ImpliesNot("--no-harmony-shadow-realm", "--experimental-shadow-realm");
|
||||||
AddOption("--build-snapshot",
|
AddOption("--build-snapshot",
|
||||||
"Generate a snapshot blob when the process exits."
|
"Generate a snapshot blob when the process exits.",
|
||||||
" Currently only supported in the node_mksnapshot binary.",
|
|
||||||
&PerIsolateOptions::build_snapshot,
|
&PerIsolateOptions::build_snapshot,
|
||||||
kDisallowedInEnvvar);
|
kDisallowedInEnvvar);
|
||||||
|
|
||||||
|
@ -31,9 +31,14 @@ class NODE_EXTERN_PRIVATE SnapshotBuilder {
|
|||||||
static void InitializeIsolateParams(const SnapshotData* data,
|
static void InitializeIsolateParams(const SnapshotData* data,
|
||||||
v8::Isolate::CreateParams* params);
|
v8::Isolate::CreateParams* params);
|
||||||
|
|
||||||
private:
|
|
||||||
static const std::vector<intptr_t>& CollectExternalReferences();
|
static const std::vector<intptr_t>& CollectExternalReferences();
|
||||||
|
|
||||||
|
static ExitCode CreateSnapshot(
|
||||||
|
SnapshotData* out,
|
||||||
|
CommonEnvironmentSetup* setup,
|
||||||
|
/*SnapshotMetadata::Type*/ uint8_t snapshot_type);
|
||||||
|
|
||||||
|
private:
|
||||||
static std::unique_ptr<ExternalReferenceRegistry> registry_;
|
static std::unique_ptr<ExternalReferenceRegistry> registry_;
|
||||||
};
|
};
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@ -33,6 +33,7 @@ using v8::FunctionCallbackInfo;
|
|||||||
using v8::HandleScope;
|
using v8::HandleScope;
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
|
using v8::MaybeLocal;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::ObjectTemplate;
|
using v8::ObjectTemplate;
|
||||||
using v8::ScriptCompiler;
|
using v8::ScriptCompiler;
|
||||||
@ -1101,38 +1102,15 @@ void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
|
|||||||
ExitCode SnapshotBuilder::Generate(SnapshotData* out,
|
ExitCode SnapshotBuilder::Generate(SnapshotData* out,
|
||||||
const std::vector<std::string> args,
|
const std::vector<std::string> args,
|
||||||
const std::vector<std::string> exec_args) {
|
const std::vector<std::string> exec_args) {
|
||||||
const std::vector<intptr_t>& external_references =
|
std::vector<std::string> errors;
|
||||||
CollectExternalReferences();
|
auto setup = CommonEnvironmentSetup::CreateForSnapshotting(
|
||||||
Isolate* isolate = Isolate::Allocate();
|
per_process::v8_platform.Platform(), &errors, args, exec_args);
|
||||||
// Must be done before the SnapshotCreator creation so that the
|
if (!setup) {
|
||||||
// memory reducer can be initialized.
|
for (const std::string& err : errors)
|
||||||
per_process::v8_platform.Platform()->RegisterIsolate(isolate,
|
fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
|
||||||
uv_default_loop());
|
return ExitCode::kBootstrapFailure;
|
||||||
|
|
||||||
SnapshotCreator creator(isolate, external_references.data());
|
|
||||||
|
|
||||||
isolate->SetCaptureStackTraceForUncaughtExceptions(
|
|
||||||
true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
|
|
||||||
|
|
||||||
Environment* env = nullptr;
|
|
||||||
std::unique_ptr<NodeMainInstance> main_instance =
|
|
||||||
NodeMainInstance::Create(isolate,
|
|
||||||
uv_default_loop(),
|
|
||||||
per_process::v8_platform.Platform(),
|
|
||||||
args,
|
|
||||||
exec_args);
|
|
||||||
|
|
||||||
// The cleanups should be done in case of an early exit due to errors.
|
|
||||||
auto cleanup = OnScopeLeave([&]() {
|
|
||||||
// Must be done while the snapshot creator isolate is entered i.e. the
|
|
||||||
// creator is still alive. The snapshot creator destructor will destroy
|
|
||||||
// the isolate.
|
|
||||||
if (env != nullptr) {
|
|
||||||
FreeEnvironment(env);
|
|
||||||
}
|
}
|
||||||
main_instance->Dispose();
|
Isolate* isolate = setup->isolate();
|
||||||
per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
|
|
||||||
});
|
|
||||||
|
|
||||||
// It's only possible to be kDefault in node_mksnapshot.
|
// It's only possible to be kDefault in node_mksnapshot.
|
||||||
SnapshotMetadata::Type snapshot_type =
|
SnapshotMetadata::Type snapshot_type =
|
||||||
@ -1151,57 +1129,11 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// The default context with only things created by V8.
|
|
||||||
Local<Context> default_context = Context::New(isolate);
|
|
||||||
|
|
||||||
// The context used by the vm module.
|
|
||||||
Local<Context> vm_context;
|
|
||||||
{
|
|
||||||
Local<ObjectTemplate> global_template =
|
|
||||||
main_instance->isolate_data()->contextify_global_template();
|
|
||||||
CHECK(!global_template.IsEmpty());
|
|
||||||
if (!contextify::ContextifyContext::CreateV8Context(
|
|
||||||
isolate, global_template, nullptr, nullptr)
|
|
||||||
.ToLocal(&vm_context)) {
|
|
||||||
return ExitCode::kStartupSnapshotFailure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Node.js-specific context with primodials, can be used by workers
|
|
||||||
// TODO(joyeecheung): investigate if this can be used by vm contexts
|
|
||||||
// without breaking compatibility.
|
|
||||||
Local<Context> base_context = NewContext(isolate);
|
|
||||||
if (base_context.IsEmpty()) {
|
|
||||||
return ExitCode::kBootstrapFailure;
|
|
||||||
}
|
|
||||||
ResetContextSettingsBeforeSnapshot(base_context);
|
|
||||||
|
|
||||||
Local<Context> main_context = NewContext(isolate);
|
|
||||||
if (main_context.IsEmpty()) {
|
|
||||||
return ExitCode::kBootstrapFailure;
|
|
||||||
}
|
|
||||||
// Initialize the main instance context.
|
// Initialize the main instance context.
|
||||||
{
|
{
|
||||||
Context::Scope context_scope(main_context);
|
Context::Scope context_scope(setup->context());
|
||||||
|
Environment* env = setup->env();
|
||||||
|
|
||||||
// Create the environment.
|
|
||||||
// It's not guaranteed that a context that goes through
|
|
||||||
// v8_inspector::V8Inspector::contextCreated() is runtime-independent,
|
|
||||||
// so do not start the inspector on the main context when building
|
|
||||||
// the default snapshot.
|
|
||||||
uint64_t env_flags = EnvironmentFlags::kDefaultFlags |
|
|
||||||
EnvironmentFlags::kNoCreateInspector;
|
|
||||||
|
|
||||||
env = CreateEnvironment(main_instance->isolate_data(),
|
|
||||||
main_context,
|
|
||||||
args,
|
|
||||||
exec_args,
|
|
||||||
static_cast<EnvironmentFlags::Flags>(env_flags));
|
|
||||||
|
|
||||||
// This already ran scripts in lib/internal/bootstrap/, if it fails return
|
|
||||||
if (env == nullptr) {
|
|
||||||
return ExitCode::kBootstrapFailure;
|
|
||||||
}
|
|
||||||
// If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
|
// If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
|
||||||
// loaded via LoadEnvironment() to execute process.argv[1] as the entry
|
// loaded via LoadEnvironment() to execute process.argv[1] as the entry
|
||||||
// point (we currently only support this kind of entry point, but we
|
// point (we currently only support this kind of entry point, but we
|
||||||
@ -1223,6 +1155,52 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out,
|
|||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateSnapshot(out, setup.get(), static_cast<uint8_t>(snapshot_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitCode SnapshotBuilder::CreateSnapshot(SnapshotData* out,
|
||||||
|
CommonEnvironmentSetup* setup,
|
||||||
|
uint8_t snapshot_type_u8) {
|
||||||
|
SnapshotMetadata::Type snapshot_type =
|
||||||
|
static_cast<SnapshotMetadata::Type>(snapshot_type_u8);
|
||||||
|
Isolate* isolate = setup->isolate();
|
||||||
|
Environment* env = setup->env();
|
||||||
|
SnapshotCreator* creator = setup->snapshot_creator();
|
||||||
|
|
||||||
|
{
|
||||||
|
HandleScope scope(isolate);
|
||||||
|
Local<Context> main_context = setup->context();
|
||||||
|
|
||||||
|
// The default context with only things created by V8.
|
||||||
|
Local<Context> default_context = Context::New(isolate);
|
||||||
|
|
||||||
|
// The context used by the vm module.
|
||||||
|
Local<Context> vm_context;
|
||||||
|
{
|
||||||
|
Local<ObjectTemplate> global_template =
|
||||||
|
setup->isolate_data()->contextify_global_template();
|
||||||
|
CHECK(!global_template.IsEmpty());
|
||||||
|
if (!contextify::ContextifyContext::CreateV8Context(
|
||||||
|
isolate, global_template, nullptr, nullptr)
|
||||||
|
.ToLocal(&vm_context)) {
|
||||||
|
return ExitCode::kStartupSnapshotFailure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Node.js-specific context with primodials, can be used by workers
|
||||||
|
// TODO(joyeecheung): investigate if this can be used by vm contexts
|
||||||
|
// without breaking compatibility.
|
||||||
|
Local<Context> base_context = NewContext(isolate);
|
||||||
|
if (base_context.IsEmpty()) {
|
||||||
|
return ExitCode::kBootstrapFailure;
|
||||||
|
}
|
||||||
|
ResetContextSettingsBeforeSnapshot(base_context);
|
||||||
|
|
||||||
|
{
|
||||||
|
Context::Scope context_scope(main_context);
|
||||||
|
|
||||||
if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
|
if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
|
||||||
env->ForEachRealm([](Realm* realm) { realm->PrintInfoForSnapshot(); });
|
env->ForEachRealm([](Realm* realm) { realm->PrintInfoForSnapshot(); });
|
||||||
@ -1230,9 +1208,8 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the native states
|
// Serialize the native states
|
||||||
out->isolate_data_info =
|
out->isolate_data_info = setup->isolate_data()->Serialize(creator);
|
||||||
main_instance->isolate_data()->Serialize(&creator);
|
out->env_info = env->Serialize(creator);
|
||||||
out->env_info = env->Serialize(&creator);
|
|
||||||
|
|
||||||
#ifdef NODE_USE_NODE_CODE_CACHE
|
#ifdef NODE_USE_NODE_CODE_CACHE
|
||||||
// Regenerate all the code cache.
|
// Regenerate all the code cache.
|
||||||
@ -1255,19 +1232,19 @@ ExitCode SnapshotBuilder::Generate(SnapshotData* out,
|
|||||||
// Global handles to the contexts can't be disposed before the
|
// Global handles to the contexts can't be disposed before the
|
||||||
// blob is created. So initialize all the contexts before adding them.
|
// blob is created. So initialize all the contexts before adding them.
|
||||||
// TODO(joyeecheung): figure out how to remove this restriction.
|
// TODO(joyeecheung): figure out how to remove this restriction.
|
||||||
creator.SetDefaultContext(default_context);
|
creator->SetDefaultContext(default_context);
|
||||||
size_t index = creator.AddContext(vm_context);
|
size_t index = creator->AddContext(vm_context);
|
||||||
CHECK_EQ(index, SnapshotData::kNodeVMContextIndex);
|
CHECK_EQ(index, SnapshotData::kNodeVMContextIndex);
|
||||||
index = creator.AddContext(base_context);
|
index = creator->AddContext(base_context);
|
||||||
CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex);
|
CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex);
|
||||||
index = creator.AddContext(main_context,
|
index = creator->AddContext(main_context,
|
||||||
{SerializeNodeContextInternalFields, env});
|
{SerializeNodeContextInternalFields, env});
|
||||||
CHECK_EQ(index, SnapshotData::kNodeMainContextIndex);
|
CHECK_EQ(index, SnapshotData::kNodeMainContextIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be out of HandleScope
|
// Must be out of HandleScope
|
||||||
out->v8_snapshot_blob_data =
|
out->v8_snapshot_blob_data =
|
||||||
creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kKeep);
|
creator->CreateBlob(SnapshotCreator::FunctionCodeHandling::kKeep);
|
||||||
|
|
||||||
// We must be able to rehash the blob when we restore it or otherwise
|
// We must be able to rehash the blob when we restore it or otherwise
|
||||||
// the hash seed would be fixed by V8, introducing a vulnerability.
|
// the hash seed would be fixed by V8, introducing a vulnerability.
|
||||||
@ -1468,6 +1445,23 @@ void SerializeSnapshotableObjects(Realm* realm,
|
|||||||
|
|
||||||
namespace mksnapshot {
|
namespace mksnapshot {
|
||||||
|
|
||||||
|
void GetEmbedderEntryFunction(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
Isolate* isolate = env->isolate();
|
||||||
|
if (!env->embedder_mksnapshot_entry_point()) return;
|
||||||
|
MaybeLocal<Function> jsfn =
|
||||||
|
Function::New(isolate->GetCurrentContext(),
|
||||||
|
[](const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
Local<Value> require_fn = args[0];
|
||||||
|
CHECK(require_fn->IsFunction());
|
||||||
|
CHECK(env->embedder_mksnapshot_entry_point());
|
||||||
|
env->embedder_mksnapshot_entry_point()(
|
||||||
|
{env->process_object(), require_fn.As<Function>()});
|
||||||
|
});
|
||||||
|
if (!jsfn.IsEmpty()) args.GetReturnValue().Set(jsfn.ToLocalChecked());
|
||||||
|
}
|
||||||
|
|
||||||
void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
|
void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
|
||||||
CHECK(args[0]->IsString());
|
CHECK(args[0]->IsString());
|
||||||
Local<String> filename = args[0].As<String>();
|
Local<String> filename = args[0].As<String>();
|
||||||
@ -1521,6 +1515,8 @@ void Initialize(Local<Object> target,
|
|||||||
Local<Value> unused,
|
Local<Value> unused,
|
||||||
Local<Context> context,
|
Local<Context> context,
|
||||||
void* priv) {
|
void* priv) {
|
||||||
|
SetMethod(
|
||||||
|
context, target, "getEmbedderEntryFunction", GetEmbedderEntryFunction);
|
||||||
SetMethod(context, target, "compileSerializeMain", CompileSerializeMain);
|
SetMethod(context, target, "compileSerializeMain", CompileSerializeMain);
|
||||||
SetMethod(context, target, "setSerializeCallback", SetSerializeCallback);
|
SetMethod(context, target, "setSerializeCallback", SetSerializeCallback);
|
||||||
SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback);
|
SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback);
|
||||||
@ -1531,6 +1527,7 @@ void Initialize(Local<Object> target,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||||
|
registry->Register(GetEmbedderEntryFunction);
|
||||||
registry->Register(CompileSerializeMain);
|
registry->Register(CompileSerializeMain);
|
||||||
registry->Register(SetSerializeCallback);
|
registry->Register(SetSerializeCallback);
|
||||||
registry->Register(SetDeserializeCallback);
|
registry->Register(SetDeserializeCallback);
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#ifdef NDEBUG
|
||||||
|
#undef NDEBUG
|
||||||
|
#endif
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -58,9 +61,12 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
|
|||||||
int exit_code = 0;
|
int exit_code = 0;
|
||||||
|
|
||||||
node::EmbedderSnapshotData::Pointer snapshot;
|
node::EmbedderSnapshotData::Pointer snapshot;
|
||||||
|
auto snapshot_build_mode_it =
|
||||||
|
std::find(args.begin(), args.end(), "--embedder-snapshot-create");
|
||||||
auto snapshot_arg_it =
|
auto snapshot_arg_it =
|
||||||
std::find(args.begin(), args.end(), "--embedder-snapshot-blob");
|
std::find(args.begin(), args.end(), "--embedder-snapshot-blob");
|
||||||
if (snapshot_arg_it < args.end() - 1) {
|
if (snapshot_arg_it < args.end() - 1 &&
|
||||||
|
snapshot_build_mode_it == args.end()) {
|
||||||
FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "r");
|
FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "r");
|
||||||
assert(fp != nullptr);
|
assert(fp != nullptr);
|
||||||
snapshot = node::EmbedderSnapshotData::FromFile(fp);
|
snapshot = node::EmbedderSnapshotData::FromFile(fp);
|
||||||
@ -69,9 +75,11 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
|
|||||||
|
|
||||||
std::vector<std::string> errors;
|
std::vector<std::string> errors;
|
||||||
std::unique_ptr<CommonEnvironmentSetup> setup =
|
std::unique_ptr<CommonEnvironmentSetup> setup =
|
||||||
snapshot
|
snapshot ? CommonEnvironmentSetup::CreateFromSnapshot(
|
||||||
? CommonEnvironmentSetup::CreateWithSnapshot(
|
|
||||||
platform, &errors, snapshot.get(), args, exec_args)
|
platform, &errors, snapshot.get(), args, exec_args)
|
||||||
|
: snapshot_build_mode_it != args.end()
|
||||||
|
? CommonEnvironmentSetup::CreateForSnapshotting(
|
||||||
|
platform, &errors, args, exec_args)
|
||||||
: CommonEnvironmentSetup::Create(platform, &errors, 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)
|
||||||
@ -94,9 +102,12 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
|
|||||||
} else {
|
} else {
|
||||||
loadenv_ret = node::LoadEnvironment(
|
loadenv_ret = node::LoadEnvironment(
|
||||||
env,
|
env,
|
||||||
"const publicRequire ="
|
// Snapshots do not support userland require()s (yet)
|
||||||
|
"if (!require('v8').startupSnapshot.isBuildingSnapshot()) {"
|
||||||
|
" const publicRequire ="
|
||||||
" require('module').createRequire(process.cwd() + '/');"
|
" require('module').createRequire(process.cwd() + '/');"
|
||||||
"globalThis.require = publicRequire;"
|
" globalThis.require = publicRequire;"
|
||||||
|
"} else globalThis.require = require;"
|
||||||
"globalThis.embedVars = { nön_ascıı: '🏳️🌈' };"
|
"globalThis.embedVars = { nön_ascıı: '🏳️🌈' };"
|
||||||
"require('vm').runInThisContext(process.argv[1]);");
|
"require('vm').runInThisContext(process.argv[1]);");
|
||||||
}
|
}
|
||||||
@ -105,9 +116,20 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
exit_code = node::SpinEventLoop(env).FromMaybe(1);
|
exit_code = node::SpinEventLoop(env).FromMaybe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot_arg_it < args.end() - 1 &&
|
||||||
|
snapshot_build_mode_it != args.end()) {
|
||||||
|
snapshot = setup->CreateSnapshot();
|
||||||
|
assert(snapshot);
|
||||||
|
|
||||||
|
FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "w");
|
||||||
|
assert(fp != nullptr);
|
||||||
|
snapshot->ToFile(fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
node::Stop(env);
|
node::Stop(env);
|
||||||
}
|
|
||||||
|
|
||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ function resolveBuiltBinary(bin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const binary = resolveBuiltBinary('embedtest');
|
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)'])
|
||||||
@ -51,22 +50,30 @@ assert.strictEqual(
|
|||||||
child_process.spawnSync(binary, [`require(${fixturePath})`, 92]).status,
|
child_process.spawnSync(binary, [`require(${fixturePath})`, 92]).status,
|
||||||
92);
|
92);
|
||||||
|
|
||||||
|
function getReadFileCodeForPath(path) {
|
||||||
|
return `(require("fs").readFileSync(${JSON.stringify(path)}, "utf8"))`;
|
||||||
|
}
|
||||||
|
|
||||||
// Basic snapshot support
|
// Basic snapshot support
|
||||||
{
|
{
|
||||||
|
// readSync + eval since snapshots don't support userland require() (yet)
|
||||||
const snapshotFixture = fixtures.path('snapshot', 'echo-args.js');
|
const snapshotFixture = fixtures.path('snapshot', 'echo-args.js');
|
||||||
const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob');
|
const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob');
|
||||||
const buildSnapshotArgs = [snapshotFixture, 'arg1', 'arg2'];
|
const buildSnapshotArgs = [
|
||||||
|
`eval(${getReadFileCodeForPath(snapshotFixture)})`, 'arg1', 'arg2',
|
||||||
|
'--embedder-snapshot-blob', blobPath, '--embedder-snapshot-create',
|
||||||
|
];
|
||||||
const runEmbeddedArgs = ['--embedder-snapshot-blob', blobPath, 'arg3', 'arg4'];
|
const runEmbeddedArgs = ['--embedder-snapshot-blob', blobPath, 'arg3', 'arg4'];
|
||||||
|
|
||||||
fs.rmSync(blobPath, { force: true });
|
fs.rmSync(blobPath, { force: true });
|
||||||
assert.strictEqual(child_process.spawnSync(standaloneNodeBinary, [
|
assert.strictEqual(child_process.spawnSync(binary, [
|
||||||
'--snapshot-blob', blobPath, '--build-snapshot', ...buildSnapshotArgs,
|
'--', ...buildSnapshotArgs,
|
||||||
], {
|
], {
|
||||||
cwd: tmpdir.path,
|
cwd: tmpdir.path,
|
||||||
}).status, 0);
|
}).status, 0);
|
||||||
const spawnResult = child_process.spawnSync(binary, ['--', ...runEmbeddedArgs]);
|
const spawnResult = child_process.spawnSync(binary, ['--', ...runEmbeddedArgs]);
|
||||||
assert.deepStrictEqual(JSON.parse(spawnResult.stdout), {
|
assert.deepStrictEqual(JSON.parse(spawnResult.stdout), {
|
||||||
originalArgv: [standaloneNodeBinary, ...buildSnapshotArgs],
|
originalArgv: [binary, ...buildSnapshotArgs],
|
||||||
currentArgv: [binary, ...runEmbeddedArgs],
|
currentArgv: [binary, ...runEmbeddedArgs],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -75,10 +82,14 @@ assert.strictEqual(
|
|||||||
{
|
{
|
||||||
const snapshotFixture = fixtures.path('snapshot', 'create-worker-and-vm.js');
|
const snapshotFixture = fixtures.path('snapshot', 'create-worker-and-vm.js');
|
||||||
const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob');
|
const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob');
|
||||||
|
const buildSnapshotArgs = [
|
||||||
|
`eval(${getReadFileCodeForPath(snapshotFixture)})`,
|
||||||
|
'--embedder-snapshot-blob', blobPath, '--embedder-snapshot-create',
|
||||||
|
];
|
||||||
|
|
||||||
fs.rmSync(blobPath, { force: true });
|
fs.rmSync(blobPath, { force: true });
|
||||||
assert.strictEqual(child_process.spawnSync(standaloneNodeBinary, [
|
assert.strictEqual(child_process.spawnSync(binary, [
|
||||||
'--snapshot-blob', blobPath, '--build-snapshot', snapshotFixture,
|
'--', ...buildSnapshotArgs,
|
||||||
], {
|
], {
|
||||||
cwd: tmpdir.path,
|
cwd: tmpdir.path,
|
||||||
}).status, 0);
|
}).status, 0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user