Skip to content

Commit

Permalink
src: allow blobs instead of FILE*s in embedder snapshot API
Browse files Browse the repository at this point in the history
This is a shared follow-up to 06bb6b4 and a466fea
now that both have been merged.
  • Loading branch information
addaleax committed Feb 4, 2023
1 parent 3000d77 commit e16e66e
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 9 deletions.
16 changes: 16 additions & 0 deletions src/api/embed_helpers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,18 @@ EmbedderSnapshotData::Pointer EmbedderSnapshotData::BuiltinSnapshotData() {
SnapshotBuilder::GetEmbeddedSnapshotData(), false)};
}

EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromBlob(
const std::vector<char>& 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::Pointer EmbedderSnapshotData::FromFile(FILE* in) {
SnapshotData* snapshot_data = new SnapshotData();
CHECK_EQ(snapshot_data->data_ownership, SnapshotData::DataOwnership::kOwned);
Expand All @@ -302,6 +314,10 @@ EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) {
return result;
}

std::vector<char> EmbedderSnapshotData::ToBlob() const {
return impl_->ToBlob();
}

void EmbedderSnapshotData::ToFile(FILE* out) const {
impl_->ToBlob(out);
}
Expand Down
2 changes: 2 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -518,10 +518,12 @@ struct SnapshotData {
std::vector<builtins::CodeCacheInfo> code_cache;

void ToBlob(FILE* out) const;
std::vector<char> ToBlob() const;
// If returns false, the metadata doesn't match the current Node.js binary,
// and the caller should not consume the snapshot data.
bool Check() const;
static bool FromBlob(SnapshotData* out, FILE* in);
static bool FromBlob(SnapshotData* out, const std::vector<char>& in);
static const SnapshotData* FromEmbedderWrapper(
const EmbedderSnapshotData* data);
EmbedderSnapshotData::Pointer AsEmbedderWrapper() const;
Expand Down
2 changes: 2 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -515,11 +515,13 @@ class EmbedderSnapshotData {
// 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);
static Pointer FromBlob(const std::vector<char>& 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;
std::vector<char> ToBlob() const;

// Returns whether custom snapshots can be used. Currently, this means
// that V8 was configured without the shared-readonly-heap feature.
Expand Down
16 changes: 12 additions & 4 deletions src/node_snapshotable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ size_t SnapshotSerializer::Write(const SnapshotMetadata& data) {
// [ ... ] env_info
// [ ... ] code_cache

void SnapshotData::ToBlob(FILE* out) const {
std::vector<char> SnapshotData::ToBlob() const {
SnapshotSerializer w;
w.Debug("SnapshotData::ToBlob()\n");

Expand All @@ -858,9 +858,14 @@ void SnapshotData::ToBlob(FILE* out) const {
written_total += w.Write<EnvSerializeInfo>(env_info);
w.Debug("Write code_cache\n");
written_total += w.WriteVector<builtins::CodeCacheInfo>(code_cache);
size_t num_written = fwrite(w.sink.data(), w.sink.size(), 1, out);
CHECK_EQ(num_written, 1);
w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
return w.sink;
}

void SnapshotData::ToBlob(FILE* out) const {
const std::vector<char> sink = ToBlob();
size_t num_written = fwrite(sink.data(), sink.size(), 1, out);
CHECK_EQ(num_written, 1);
}

const SnapshotData* SnapshotData::FromEmbedderWrapper(
Expand All @@ -883,8 +888,11 @@ bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
std::vector<char> sink(size);
size_t num_read = fread(sink.data(), size, 1, in);
CHECK_EQ(num_read, 1);
return FromBlob(out, sink);
}

SnapshotDeserializer r(sink);
bool SnapshotData::FromBlob(SnapshotData* out, const std::vector<char>& in) {
SnapshotDeserializer r(in);
r.Debug("SnapshotData::FromBlob()\n");

DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);
Expand Down
29 changes: 26 additions & 3 deletions test/embedding/embedtest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,28 @@ int RunNodeInstance(MultiIsolatePlatform* platform,
std::find(args.begin(), args.end(), "--embedder-snapshot-create");
auto snapshot_arg_it =
std::find(args.begin(), args.end(), "--embedder-snapshot-blob");
auto snapshot_as_file_it =
std::find(args.begin(), args.end(), "--embedder-snapshot-as-file");
if (snapshot_arg_it < args.end() - 1 &&
snapshot_build_mode_it == args.end()) {
FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "r");
const char* filename = (snapshot_arg_it + 1)->c_str();
FILE* fp = fopen(filename, "r");
assert(fp != nullptr);
snapshot = node::EmbedderSnapshotData::FromFile(fp);
if (snapshot_as_file_it != args.end()) {
snapshot = node::EmbedderSnapshotData::FromFile(fp);
} else {
uv_fs_t req;
int statret = uv_fs_stat(nullptr, &req, filename, nullptr);
assert(statret == 0);
size_t filesize = req.statbuf.st_size;
uv_fs_req_cleanup(&req);

std::vector<char> vec(filesize);
size_t read = fread(vec.data(), filesize, 1, fp);
assert(read == 1);
snapshot = node::EmbedderSnapshotData::FromBlob(vec);
}
assert(snapshot);
fclose(fp);
}

Expand Down Expand Up @@ -125,7 +142,13 @@ int RunNodeInstance(MultiIsolatePlatform* platform,

FILE* fp = fopen((snapshot_arg_it + 1)->c_str(), "w");
assert(fp != nullptr);
snapshot->ToFile(fp);
if (snapshot_as_file_it != args.end()) {
snapshot->ToFile(fp);
} else {
const std::vector<char> vec = snapshot->ToBlob();
size_t written = fwrite(vec.data(), vec.size(), 1, fp);
assert(written == 1);
}
fclose(fp);
}

Expand Down
7 changes: 5 additions & 2 deletions test/embedding/test-embedding.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,18 @@ function getReadFileCodeForPath(path) {
}

// Basic snapshot support
{
for (const extraSnapshotArgs of [[], ['--embedder-snapshot-as-file']]) {
// readSync + eval since snapshots don't support userland require() (yet)
const snapshotFixture = fixtures.path('snapshot', 'echo-args.js');
const blobPath = path.join(tmpdir.path, 'embedder-snapshot.blob');
const buildSnapshotArgs = [
`eval(${getReadFileCodeForPath(snapshotFixture)})`, 'arg1', 'arg2',
'--embedder-snapshot-blob', blobPath, '--embedder-snapshot-create',
...extraSnapshotArgs,
];
const runEmbeddedArgs = [
'--embedder-snapshot-blob', blobPath, ...extraSnapshotArgs, 'arg3', 'arg4',
];
const runEmbeddedArgs = ['--embedder-snapshot-blob', blobPath, 'arg3', 'arg4'];

fs.rmSync(blobPath, { force: true });
assert.strictEqual(child_process.spawnSync(binary, [
Expand Down

0 comments on commit e16e66e

Please sign in to comment.