Skip to content

Commit d9538a9

Browse files
authored
src: apply IsolateSettings when using a snapshot
`NewIsolate()` deferred `SetIsolateErrorHandlers()` when snapshot data was passed, and `CreateEnvironment()` later installed the handlers with default `IsolateSettings` after deserializing the main context. An embedder's `fatal_error_callback`, `oom_error_callback`, `should_abort_on_uncaught_exception_callback` and `prepare_stack_trace_callback` were therefore dropped whenever a snapshot was used, and the per-isolate message listener was added even if `MESSAGE_LISTENER_WITH_ERROR_LEVEL` had been cleared. The only way to keep custom handlers was to call `SetIsolateUpForNode()` again after `CreateEnvironment()`. Install all handlers in `NewIsolate()` regardless of snapshot data, as its documentation already describes, and stop touching isolate handlers in `CreateEnvironment()`. The deferral dates from the initial isolate snapshot work; every handler already copes with a missing `Environment`, since without a snapshot they are installed before any context exists, and workers have been calling `SetIsolateUpForNode()` right after a snapshot `NewIsolate()` anyway. Refs: #27321 Refs: #45888 Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com> PR-URL: #65407 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent c4336d9 commit d9538a9

5 files changed

Lines changed: 120 additions & 15 deletions

File tree

‎src/api/environment.cc‎

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ void SetIsolateCreateParamsForNode(Isolate::CreateParams* params) {
225225
#endif
226226
}
227227

228-
void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
228+
static void SetIsolateErrorHandlers(v8::Isolate* isolate,
229+
const IsolateSettings& s) {
229230
if (s.flags & MESSAGE_LISTENER_WITH_ERROR_LEVEL)
230231
isolate->AddMessageListenerWithErrorLevel(
231232
errors::PerIsolateMessageListener,
@@ -350,16 +351,7 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
350351

351352
SetIsolateCreateParamsForNode(params);
352353
Isolate::Initialize(isolate, *params);
353-
354-
Isolate::Scope isolate_scope(isolate);
355-
356-
if (snapshot_data == nullptr) {
357-
// If in deserialize mode, delay until after the deserialization is
358-
// complete.
359-
SetIsolateUpForNode(isolate, settings);
360-
} else {
361-
SetIsolateMiscHandlers(isolate, settings);
362-
}
354+
SetIsolateUpForNode(isolate, settings);
363355

364356
return isolate;
365357
}
@@ -469,7 +461,6 @@ Environment* CreateEnvironment(
469461
FreeEnvironment(env);
470462
return nullptr;
471463
}
472-
SetIsolateErrorHandlers(isolate, {});
473464
}
474465

475466
Context::Scope context_scope(context);

‎src/node_internals.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,6 @@ class InitializationResultImpl final : public InitializationResult {
380380
MultiIsolatePlatform* platform_ = nullptr;
381381
};
382382

383-
void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s);
384383
void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s);
385384
void SetIsolateCreateParamsForNode(v8::Isolate::CreateParams* params);
386385

‎src/node_worker.cc‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,6 @@ class WorkerThreadData {
190190
return;
191191
}
192192

193-
SetIsolateUpForNode(isolate);
194-
195193
// Be sure it's called before Environment::InitializeDiagnostics()
196194
// so that this callback stays when the callback of
197195
// --heapsnapshot-near-heap-limit gets is popped.

‎test/embedding/embedtest.cc‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ using node::MultiIsolatePlatform;
1919
using v8::Context;
2020
using v8::HandleScope;
2121
using v8::Isolate;
22+
using v8::Local;
2223
using v8::Locker;
2324
using v8::MaybeLocal;
2425
using v8::V8;
@@ -27,6 +28,11 @@ using v8::Value;
2728
static int RunNodeInstance(MultiIsolatePlatform* platform,
2829
const std::vector<std::string>& args,
2930
const std::vector<std::string>& exec_args);
31+
static int RunSnapshotWithIsolateSettings(
32+
MultiIsolatePlatform* platform,
33+
const node::EmbedderSnapshotData* snapshot,
34+
const std::vector<std::string>& args,
35+
const std::vector<std::string>& exec_args);
3036

3137
// --create-v8-startup-blob <file>: a plain V8 startup blob (what V8's
3238
// mksnapshot produces), to test building the Node.js snapshot on top of one.
@@ -130,6 +136,7 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
130136
// Running snapshot:
131137
// embedtest --embedder-snapshot-blob blob-path
132138
// [--embedder-snapshot-as-file]
139+
// [--embedder-isolate-settings]
133140
// arg1 arg2...
134141
// No snapshot:
135142
// embedtest arg1 arg2...
@@ -139,6 +146,7 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
139146
std::vector<std::string> filtered_args;
140147
bool is_building_snapshot = false;
141148
bool snapshot_as_file = false;
149+
bool with_isolate_settings = false;
142150
std::optional<node::SnapshotConfig> snapshot_config;
143151
std::string snapshot_blob_path;
144152
for (size_t i = 0; i < args.size(); ++i) {
@@ -147,6 +155,8 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
147155
is_building_snapshot = true;
148156
} else if (arg == "--embedder-snapshot-as-file") {
149157
snapshot_as_file = true;
158+
} else if (arg == "--embedder-isolate-settings") {
159+
with_isolate_settings = true;
150160
} else if (arg == "--without-code-cache") {
151161
if (!snapshot_config.has_value()) {
152162
snapshot_config = node::SnapshotConfig{};
@@ -217,6 +227,11 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
217227
node::GetAnonymousMainPath());
218228
}
219229

230+
if (snapshot && with_isolate_settings) {
231+
return RunSnapshotWithIsolateSettings(
232+
platform, snapshot.get(), filtered_args, exec_args);
233+
}
234+
220235
std::vector<std::string> errors;
221236
std::unique_ptr<CommonEnvironmentSetup> setup;
222237

@@ -300,3 +315,67 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
300315

301316
return exit_code;
302317
}
318+
319+
// CommonEnvironmentSetup does not take IsolateSettings, so this goes through
320+
// NewIsolate()/CreateIsolateData()/CreateEnvironment() directly.
321+
static int RunSnapshotWithIsolateSettings(
322+
MultiIsolatePlatform* platform,
323+
const node::EmbedderSnapshotData* snapshot,
324+
const std::vector<std::string>& args,
325+
const std::vector<std::string>& exec_args) {
326+
uv_loop_t loop;
327+
int ret = uv_loop_init(&loop);
328+
assert(ret == 0);
329+
330+
std::shared_ptr<node::ArrayBufferAllocator> allocator =
331+
node::ArrayBufferAllocator::Create();
332+
node::IsolateSettings settings;
333+
settings.prepare_stack_trace_callback = [](Local<Context> context,
334+
Local<Value> exception,
335+
Local<v8::Array> trace) {
336+
return MaybeLocal<Value>(v8::String::NewFromUtf8Literal(
337+
v8::Isolate::GetCurrent(), "stack trace prepared by the embedder"));
338+
};
339+
Isolate* isolate =
340+
node::NewIsolate(allocator, &loop, platform, snapshot, settings);
341+
assert(isolate != nullptr);
342+
343+
int exit_code = 1;
344+
{
345+
Locker locker(isolate);
346+
Isolate::Scope isolate_scope(isolate);
347+
HandleScope handle_scope(isolate);
348+
349+
std::unique_ptr<node::IsolateData, decltype(&node::FreeIsolateData)>
350+
isolate_data(node::CreateIsolateData(
351+
isolate, &loop, platform, allocator.get(), snapshot),
352+
node::FreeIsolateData);
353+
std::unique_ptr<Environment, decltype(&node::FreeEnvironment)> env(
354+
node::CreateEnvironment(
355+
isolate_data.get(), Local<Context>(), args, exec_args),
356+
node::FreeEnvironment);
357+
assert(env);
358+
359+
Context::Scope context_scope(node::GetMainContext(env.get()));
360+
if (!node::LoadEnvironment(env.get(), node::StartExecutionCallback{})
361+
.IsEmpty()) {
362+
exit_code = node::SpinEventLoop(env.get()).FromMaybe(1);
363+
}
364+
node::Stop(env.get());
365+
}
366+
367+
bool platform_finished = false;
368+
platform->AddIsolateFinishedCallback(
369+
isolate,
370+
[](void* data) {
371+
bool* finished = static_cast<bool*>(data);
372+
*finished = true;
373+
},
374+
&platform_finished);
375+
platform->DisposeIsolate(isolate);
376+
while (!platform_finished) uv_run(&loop, UV_RUN_ONCE);
377+
ret = uv_loop_close(&loop);
378+
assert(ret == 0);
379+
380+
return exit_code;
381+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
// IsolateSettings passed to NewIsolate() with a snapshot must survive
4+
// CreateEnvironment(); see RunSnapshotWithIsolateSettings() in embedtest.cc.
5+
6+
const common = require('../common');
7+
const tmpdir = require('../common/tmpdir');
8+
9+
const {
10+
spawnSyncAndAssert,
11+
spawnSyncAndExitWithoutError,
12+
} = require('../common/child_process');
13+
14+
const embedtest = common.resolveBuiltBinary('embedtest');
15+
const snapshotBlobArgs = [
16+
'--embedder-snapshot-blob', tmpdir.resolve('embedder-snapshot.blob'),
17+
];
18+
const buildSnapshotScript = `
19+
require('v8').startupSnapshot.setDeserializeMainFunction(() => {
20+
console.log(new Error('from the snapshot main function').stack);
21+
});
22+
`;
23+
24+
tmpdir.refresh();
25+
26+
spawnSyncAndExitWithoutError(
27+
embedtest,
28+
['--', buildSnapshotScript, ...snapshotBlobArgs, '--embedder-snapshot-create'],
29+
{ cwd: tmpdir.path });
30+
31+
spawnSyncAndAssert(
32+
embedtest,
33+
['--', ...snapshotBlobArgs, '--embedder-isolate-settings'],
34+
{ cwd: tmpdir.path },
35+
{
36+
trim: true,
37+
stdout: 'stack trace prepared by the embedder',
38+
});

0 commit comments

Comments
 (0)