Skip to content

Commit d2746af

Browse files
committed
[WIP] allow snapshotting from the embedder API
1 parent d2c0f4d commit d2746af

File tree

8 files changed

+216
-171
lines changed

8 files changed

+216
-171
lines changed

src/api/embed_helpers.cc

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ using v8::Locker;
1414
using v8::Maybe;
1515
using v8::Nothing;
1616
using v8::SealHandleScope;
17+
using v8::SnapshotCreator;
1718

1819
namespace node {
1920

@@ -78,16 +79,18 @@ struct CommonEnvironmentSetup::Impl {
7879
MultiIsolatePlatform* platform = nullptr;
7980
uv_loop_t loop;
8081
std::shared_ptr<ArrayBufferAllocator> allocator;
82+
std::optional<SnapshotCreator> snapshot_creator;
8183
Isolate* isolate = nullptr;
8284
DeleteFnPtr<IsolateData, FreeIsolateData> isolate_data;
8385
DeleteFnPtr<Environment, FreeEnvironment> env;
84-
Global<Context> context;
86+
Global<Context> main_context;
8587
};
8688

8789
CommonEnvironmentSetup::CommonEnvironmentSetup(
8890
MultiIsolatePlatform* platform,
8991
std::vector<std::string>* errors,
9092
const EmbedderSnapshotData* snapshot_data,
93+
bool is_snapshotting,
9194
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
9295
: impl_(new Impl()) {
9396
CHECK_NOT_NULL(platform);
@@ -105,28 +108,42 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
105108
}
106109
loop->data = this;
107110

108-
impl_->allocator = ArrayBufferAllocator::Create();
109-
impl_->isolate =
110-
NewIsolate(impl_->allocator, &impl_->loop, platform, snapshot_data);
111-
Isolate* isolate = impl_->isolate;
111+
Isolate* isolate;
112+
if (is_snapshotting) {
113+
const std::vector<intptr_t>& external_references =
114+
SnapshotBuilder::CollectExternalReferences();
115+
isolate = impl_->isolate = Isolate::Allocate();
116+
// Must be done before the SnapshotCreator creation so that the
117+
// memory reducer can be initialized.
118+
platform->RegisterIsolate(isolate, loop);
119+
impl_->snapshot_creator.emplace(isolate, external_references.data());
120+
isolate->SetCaptureStackTraceForUncaughtExceptions(
121+
true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
122+
SetIsolateMiscHandlers(isolate, {});
123+
} else {
124+
impl_->allocator = ArrayBufferAllocator::Create();
125+
isolate = impl_->isolate =
126+
NewIsolate(impl_->allocator, &impl_->loop, platform, snapshot_data);
127+
}
112128

113129
{
114130
Locker locker(isolate);
115131
Isolate::Scope isolate_scope(isolate);
116132
impl_->isolate_data.reset(CreateIsolateData(
117133
isolate, loop, platform, impl_->allocator.get(), snapshot_data));
134+
impl_->isolate_data->options()->build_snapshot = is_snapshotting;
118135

119136
HandleScope handle_scope(isolate);
120137
if (snapshot_data) {
121138
impl_->env.reset(make_env(this));
122139
if (impl_->env) {
123-
impl_->context.Reset(isolate, impl_->env->context());
140+
impl_->main_context.Reset(isolate, impl_->env->context());
124141
}
125142
return;
126143
}
127144

128145
Local<Context> context = NewContext(isolate);
129-
impl_->context.Reset(isolate, context);
146+
impl_->main_context.Reset(isolate, context);
130147
if (context.IsEmpty()) {
131148
errors->push_back("Failed to initialize V8 Context");
132149
return;
@@ -141,7 +158,34 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
141158
MultiIsolatePlatform* platform,
142159
std::vector<std::string>* errors,
143160
std::function<Environment*(const CommonEnvironmentSetup*)> make_env)
144-
: CommonEnvironmentSetup(platform, errors, nullptr, make_env) {}
161+
: CommonEnvironmentSetup(platform, errors, nullptr, false, make_env) {}
162+
163+
std::unique_ptr<CommonEnvironmentSetup>
164+
CommonEnvironmentSetup::CreateForSnapshotting(
165+
MultiIsolatePlatform* platform,
166+
std::vector<std::string>* errors,
167+
const std::vector<std::string>& args,
168+
const std::vector<std::string>& exec_args) {
169+
// It's not guaranteed that a context that goes through
170+
// v8_inspector::V8Inspector::contextCreated() is runtime-independent,
171+
// so do not start the inspector on the main context when building
172+
// the default snapshot.
173+
uint64_t env_flags = EnvironmentFlags::kDefaultFlags |
174+
EnvironmentFlags::kNoCreateInspector;
175+
176+
auto ret = std::unique_ptr<CommonEnvironmentSetup>(new CommonEnvironmentSetup(
177+
platform, errors, nullptr, true,
178+
[&](const CommonEnvironmentSetup* setup) -> Environment* {
179+
return CreateEnvironment(
180+
setup->isolate_data(),
181+
setup->context(),
182+
args,
183+
exec_args,
184+
static_cast<EnvironmentFlags::Flags>(env_flags));
185+
}));
186+
if (!errors->empty()) ret.reset();
187+
return ret;
188+
}
145189

146190
CommonEnvironmentSetup::~CommonEnvironmentSetup() {
147191
if (impl_->isolate != nullptr) {
@@ -150,7 +194,7 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() {
150194
Locker locker(isolate);
151195
Isolate::Scope isolate_scope(isolate);
152196

153-
impl_->context.Reset();
197+
impl_->main_context.Reset();
154198
impl_->env.reset();
155199
impl_->isolate_data.reset();
156200
}
@@ -160,7 +204,10 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() {
160204
*static_cast<bool*>(data) = true;
161205
}, &platform_finished);
162206
impl_->platform->UnregisterIsolate(isolate);
163-
isolate->Dispose();
207+
if (impl_->snapshot_creator.has_value())
208+
impl_->snapshot_creator.reset();
209+
else
210+
isolate->Dispose();
164211

165212
// Wait until the platform has cleaned up all relevant resources.
166213
while (!platform_finished)
@@ -173,6 +220,20 @@ CommonEnvironmentSetup::~CommonEnvironmentSetup() {
173220
delete impl_;
174221
}
175222

223+
EmbedderSnapshotData::Pointer CommonEnvironmentSetup::CreateSnapshot() {
224+
CHECK_NOT_NULL(snapshot_creator());
225+
SnapshotData* snapshot_data = new SnapshotData();
226+
EmbedderSnapshotData::Pointer result{
227+
new EmbedderSnapshotData(snapshot_data, true)};
228+
229+
auto exit_code = SnapshotBuilder::CreateSnapshot(
230+
snapshot_data, this,
231+
static_cast<uint8_t>(SnapshotMetadata::Type::kFullyCustomized));
232+
if (exit_code != ExitCode::kNoFailure) return {};
233+
234+
return result;
235+
}
236+
176237
Maybe<int> SpinEventLoop(Environment* env) {
177238
Maybe<ExitCode> result = SpinEventLoopInternal(env);
178239
if (result.IsNothing()) {
@@ -203,7 +264,11 @@ Environment* CommonEnvironmentSetup::env() const {
203264
}
204265

205266
v8::Local<v8::Context> CommonEnvironmentSetup::context() const {
206-
return impl_->context.Get(impl_->isolate);
267+
return impl_->main_context.Get(impl_->isolate);
268+
}
269+
270+
v8::SnapshotCreator* CommonEnvironmentSetup::snapshot_creator() {
271+
return impl_->snapshot_creator ? &impl_->snapshot_creator.value() : nullptr;
207272
}
208273

209274
void EmbedderSnapshotData::DeleteSnapshotData::operator()(
@@ -232,6 +297,10 @@ EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) {
232297
return result;
233298
}
234299

300+
void EmbedderSnapshotData::ToFile(FILE* out) const {
301+
impl_->ToBlob(out);
302+
}
303+
235304
EmbedderSnapshotData::EmbedderSnapshotData(const SnapshotData* impl,
236305
bool owns_impl)
237306
: impl_(impl), owns_impl_(owns_impl) {}

src/node.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,11 +511,16 @@ class EmbedderSnapshotData {
511511
static Pointer BuiltinSnapshotData();
512512

513513
// Return an EmbedderSnapshotData object that is based on an input file.
514-
// Calling this method will not consume but not close the FILE* handle.
514+
// Calling this method will consume but not close the FILE* handle.
515515
// The FILE* handle can be closed immediately following this call.
516516
// If the snapshot is invalid, this returns an empty pointer.
517517
static Pointer FromFile(FILE* in);
518518

519+
// Write this EmbedderSnapshotData object to an output file.
520+
// Calling this method will not close the FILE* handle.
521+
// The FILE* handle can be closed immediately following this call.
522+
void ToFile(FILE* out) const;
523+
519524
// Returns whether custom snapshots can be used. Currently, this means
520525
// that V8 was configured without the shared-readonly-heap feature.
521526
static bool CanUseCustomSnapshotPerIsolate();
@@ -532,6 +537,7 @@ class EmbedderSnapshotData {
532537
const SnapshotData* impl_;
533538
bool owns_impl_;
534539
friend struct SnapshotData;
540+
friend class CommonEnvironmentSetup;
535541
};
536542

537543
// Overriding IsolateSettings may produce unexpected behavior
@@ -823,7 +829,29 @@ class NODE_EXTERN CommonEnvironmentSetup {
823829
const EmbedderSnapshotData* snapshot_data,
824830
EnvironmentArgs&&... env_args);
825831

832+
// Create an embedding setup which will be used for creating a snapshot
833+
// using CreateSnapshot().
834+
//
835+
// This will create and attach a v8::SnapshotCreator to this instance,
836+
// and the same restrictions apply to this instance that also apply to
837+
// other V8 snapshotting environments.
838+
// Not all Node.js APIs are supported in this case. Currently, there is
839+
// no support for native/host objects other than Node.js builtins
840+
// in the snapshot.
841+
//
842+
// Snapshots are an *experimental* feature. In particular, the embedder API
843+
// exposed through this class is subject to change or removal between Node.js
844+
// versions, including possible API and ABI breakage.
845+
static std::unique_ptr<CommonEnvironmentSetup> CreateForSnapshotting(
846+
MultiIsolatePlatform* platform,
847+
std::vector<std::string>* errors,
848+
const std::vector<std::string>& args = {},
849+
const std::vector<std::string>& exec_args = {});
850+
EmbedderSnapshotData::Pointer CreateSnapshot();
851+
826852
struct uv_loop_s* event_loop() const;
853+
v8::SnapshotCreator* snapshot_creator();
854+
// Empty for snapshotting environments.
827855
std::shared_ptr<ArrayBufferAllocator> array_buffer_allocator() const;
828856
v8::Isolate* isolate() const;
829857
IsolateData* isolate_data() const;
@@ -846,6 +874,7 @@ class NODE_EXTERN CommonEnvironmentSetup {
846874
MultiIsolatePlatform*,
847875
std::vector<std::string>*,
848876
const EmbedderSnapshotData*,
877+
bool is_snapshotting,
849878
std::function<Environment*(const CommonEnvironmentSetup*)>);
850879
};
851880

@@ -878,6 +907,7 @@ CommonEnvironmentSetup::CreateWithSnapshot(
878907
platform,
879908
errors,
880909
snapshot_data,
910+
false,
881911
[&](const CommonEnvironmentSetup* setup) -> Environment* {
882912
return CreateEnvironment(setup->isolate_data(),
883913
setup->context(),

src/node_main_instance.cc

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,6 @@ using v8::Isolate;
2929
using v8::Local;
3030
using v8::Locker;
3131

32-
NodeMainInstance::NodeMainInstance(Isolate* isolate,
33-
uv_loop_t* event_loop,
34-
MultiIsolatePlatform* platform,
35-
const std::vector<std::string>& args,
36-
const std::vector<std::string>& exec_args)
37-
: args_(args),
38-
exec_args_(exec_args),
39-
array_buffer_allocator_(nullptr),
40-
isolate_(isolate),
41-
platform_(platform),
42-
isolate_data_(nullptr),
43-
snapshot_data_(nullptr) {
44-
isolate_data_ =
45-
std::make_unique<IsolateData>(isolate_, event_loop, platform, nullptr);
46-
47-
SetIsolateMiscHandlers(isolate_, {});
48-
}
49-
50-
std::unique_ptr<NodeMainInstance> NodeMainInstance::Create(
51-
Isolate* isolate,
52-
uv_loop_t* event_loop,
53-
MultiIsolatePlatform* platform,
54-
const std::vector<std::string>& args,
55-
const std::vector<std::string>& exec_args) {
56-
return std::unique_ptr<NodeMainInstance>(
57-
new NodeMainInstance(isolate, event_loop, platform, args, exec_args));
58-
}
59-
6032
NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data,
6133
uv_loop_t* event_loop,
6234
MultiIsolatePlatform* platform,
@@ -88,13 +60,6 @@ NodeMainInstance::NodeMainInstance(const SnapshotData* snapshot_data,
8860
isolate_params_->constraints.max_young_generation_size_in_bytes();
8961
}
9062

91-
void NodeMainInstance::Dispose() {
92-
// This should only be called on a main instance that does not own its
93-
// isolate.
94-
CHECK_NULL(isolate_params_);
95-
platform_->DrainTasks(isolate_);
96-
}
97-
9863
NodeMainInstance::~NodeMainInstance() {
9964
if (isolate_params_ == nullptr) {
10065
return;

src/node_main_instance.h

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,6 @@ struct SnapshotData;
2222
// We may be able to create an abstract class to reuse some of the routines.
2323
class NodeMainInstance {
2424
public:
25-
// To create a main instance that does not own the isolate,
26-
// The caller needs to do:
27-
//
28-
// Isolate* isolate = Isolate::Allocate();
29-
// platform->RegisterIsolate(isolate, loop);
30-
// isolate->Initialize(...);
31-
// isolate->Enter();
32-
// std::unique_ptr<NodeMainInstance> main_instance =
33-
// NodeMainInstance::Create(isolate, loop, args, exec_args);
34-
//
35-
// When tearing it down:
36-
//
37-
// main_instance->Cleanup(); // While the isolate is entered
38-
// isolate->Exit();
39-
// isolate->Dispose();
40-
// platform->UnregisterIsolate(isolate);
41-
//
42-
// After calling Dispose() the main_instance is no longer accessible.
43-
static std::unique_ptr<NodeMainInstance> Create(
44-
v8::Isolate* isolate,
45-
uv_loop_t* event_loop,
46-
MultiIsolatePlatform* platform,
47-
const std::vector<std::string>& args,
48-
const std::vector<std::string>& exec_args);
49-
50-
void Dispose();
51-
5225
// Create a main instance that owns the isolate
5326
NodeMainInstance(const SnapshotData* snapshot_data,
5427
uv_loop_t* event_loop,

src/node_snapshot_builder.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ class NODE_EXTERN_PRIVATE SnapshotBuilder {
3131
static void InitializeIsolateParams(const SnapshotData* data,
3232
v8::Isolate::CreateParams* params);
3333

34-
private:
3534
static const std::vector<intptr_t>& CollectExternalReferences();
3635

37-
static std::unique_ptr<ExternalReferenceRegistry> registry_;
36+
static ExitCode CreateSnapshot(
37+
SnapshotData* out,
38+
CommonEnvironmentSetup* setup,
39+
/*SnapshotMetadata::Type*/uint8_t snapshot_type);
40+
41+
private:
42+
static std::unique_ptr<ExternalReferenceRegistry> registry_;
3843
};
3944
} // namespace node
4045

0 commit comments

Comments
 (0)