Skip to content

Commit 527718e

Browse files
committed
sea: generate code cache with deserialized isolate
V8 now requires code cache to be compiled from an isolate with the same RO space layout as the one that's going to deserialize the cache, so for a binary built with snapshot, we need to compile the code cache using a deserialized isolate. Drive-by: ignore "useCodeCache" when "useSnapshot" is true because the compilation would've been done during build time anyway in that case, and print a warning for it.
1 parent 1f2ad05 commit 527718e

File tree

4 files changed

+82
-10
lines changed

4 files changed

+82
-10
lines changed

src/node_sea.cc

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ ExitCode GenerateSnapshotForSEA(const SeaConfig& config,
411411

412412
std::optional<std::string> GenerateCodeCache(std::string_view main_path,
413413
std::string_view main_script) {
414-
RAIIIsolate raii_isolate;
414+
RAIIIsolate raii_isolate(SnapshotBuilder::GetEmbeddedSnapshotData());
415415
Isolate* isolate = raii_isolate.get();
416416

417417
HandleScope handle_scope(isolate);
@@ -489,14 +489,19 @@ ExitCode GenerateSingleExecutableBlob(
489489
std::optional<std::string_view> optional_sv_code_cache;
490490
std::string code_cache;
491491
if (static_cast<bool>(config.flags & SeaFlags::kUseCodeCache)) {
492-
std::optional<std::string> optional_code_cache =
493-
GenerateCodeCache(config.main_path, main_script);
494-
if (!optional_code_cache.has_value()) {
495-
FPrintF(stderr, "Cannot generate V8 code cache\n");
496-
return ExitCode::kGenericUserError;
492+
if (builds_snapshot_from_main) {
493+
FPrintF(stderr,
494+
"\"useCodeCache\" is redundant when \"useSnapshot\" is true\n");
495+
} else {
496+
std::optional<std::string> optional_code_cache =
497+
GenerateCodeCache(config.main_path, main_script);
498+
if (!optional_code_cache.has_value()) {
499+
FPrintF(stderr, "Cannot generate V8 code cache\n");
500+
return ExitCode::kGenericUserError;
501+
}
502+
code_cache = optional_code_cache.value();
503+
optional_sv_code_cache = code_cache;
497504
}
498-
code_cache = optional_code_cache.value();
499-
optional_sv_code_cache = code_cache;
500505
}
501506

502507
SeaResource sea{

src/util.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "node_buffer.h"
2828
#include "node_errors.h"
2929
#include "node_internals.h"
30+
#include "node_snapshot_builder.h"
3031
#include "node_v8_platform-inl.h"
3132
#include "string_bytes.h"
3233
#include "uv.h"
@@ -677,13 +678,16 @@ Local<String> UnionBytes::ToStringChecked(Isolate* isolate) const {
677678
}
678679
}
679680

680-
RAIIIsolate::RAIIIsolate()
681+
RAIIIsolate::RAIIIsolate(const SnapshotData* data)
681682
: allocator_{ArrayBuffer::Allocator::NewDefaultAllocator()} {
682683
isolate_ = Isolate::Allocate();
683684
CHECK_NOT_NULL(isolate_);
684685
per_process::v8_platform.Platform()->RegisterIsolate(isolate_,
685686
uv_default_loop());
686687
Isolate::CreateParams params;
688+
if (data != nullptr) {
689+
SnapshotBuilder::InitializeIsolateParams(data, &params);
690+
}
687691
params.array_buffer_allocator = allocator_.get();
688692
Isolate::Initialize(isolate_, params);
689693
}

src/util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ void SetConstructorFunction(v8::Isolate* isolate,
971971
// Simple RAII class to spin up a v8::Isolate instance.
972972
class RAIIIsolate {
973973
public:
974-
RAIIIsolate();
974+
RAIIIsolate(const SnapshotData* data = nullptr);
975975
~RAIIIsolate();
976976

977977
v8::Isolate* get() const { return isolate_; }
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use strict';
2+
3+
require('../common');
4+
5+
const {
6+
injectAndCodeSign,
7+
skipIfSingleExecutableIsNotSupported,
8+
} = require('../common/sea');
9+
10+
skipIfSingleExecutableIsNotSupported();
11+
12+
// This tests "useCodeCache" is ignored when "useSnapshot" is true.
13+
14+
const tmpdir = require('../common/tmpdir');
15+
const { copyFileSync, writeFileSync, existsSync } = require('fs');
16+
const { spawnSync } = require('child_process');
17+
const { join } = require('path');
18+
const assert = require('assert');
19+
20+
const configFile = join(tmpdir.path, 'sea-config.json');
21+
const seaPrepBlob = join(tmpdir.path, 'sea-prep.blob');
22+
const outputFile = join(tmpdir.path, process.platform === 'win32' ? 'sea.exe' : 'sea');
23+
24+
{
25+
tmpdir.refresh();
26+
const code = `
27+
const {
28+
setDeserializeMainFunction,
29+
} = require('v8').startupSnapshot;
30+
31+
setDeserializeMainFunction(() => {
32+
console.log('Hello from snapshot');
33+
});
34+
`;
35+
36+
writeFileSync(join(tmpdir.path, 'snapshot.js'), code, 'utf-8');
37+
writeFileSync(configFile, `
38+
{
39+
"main": "snapshot.js",
40+
"output": "sea-prep.blob",
41+
"useSnapshot": true,
42+
"useCodeCache": true
43+
}
44+
`);
45+
46+
let child = spawnSync(
47+
process.execPath,
48+
['--experimental-sea-config', 'sea-config.json'],
49+
{
50+
cwd: tmpdir.path
51+
});
52+
assert.match(
53+
child.stderr.toString(),
54+
/"useCodeCache" is redundant when "useSnapshot" is true/);
55+
56+
assert(existsSync(seaPrepBlob));
57+
58+
copyFileSync(process.execPath, outputFile);
59+
injectAndCodeSign(outputFile, seaPrepBlob);
60+
61+
child = spawnSync(outputFile);
62+
assert.strictEqual(child.stdout.toString().trim(), 'Hello from snapshot');
63+
}

0 commit comments

Comments
 (0)