diff --git a/src/api/embed_helpers.cc b/src/api/embed_helpers.cc index 02c4fdf1b53a44..04c1aa10c8cac0 100644 --- a/src/api/embed_helpers.cc +++ b/src/api/embed_helpers.cc @@ -291,6 +291,18 @@ EmbedderSnapshotData::Pointer EmbedderSnapshotData::BuiltinSnapshotData() { SnapshotBuilder::GetEmbeddedSnapshotData(), false)}; } +EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromBlob( + const std::vector& 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); @@ -302,6 +314,10 @@ EmbedderSnapshotData::Pointer EmbedderSnapshotData::FromFile(FILE* in) { return result; } +std::vector EmbedderSnapshotData::ToBlob() const { + return impl_->ToBlob(); +} + void EmbedderSnapshotData::ToFile(FILE* out) const { impl_->ToBlob(out); } diff --git a/src/env.h b/src/env.h index 0d432378322ebc..ee823e7f7bacfe 100644 --- a/src/env.h +++ b/src/env.h @@ -518,10 +518,12 @@ struct SnapshotData { std::vector code_cache; void ToBlob(FILE* out) const; + std::vector 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& in); static const SnapshotData* FromEmbedderWrapper( const EmbedderSnapshotData* data); EmbedderSnapshotData::Pointer AsEmbedderWrapper() const; diff --git a/src/node.h b/src/node.h index 15c8b1ba3c3980..48477e970829c3 100644 --- a/src/node.h +++ b/src/node.h @@ -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& 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 ToBlob() const; // Returns whether custom snapshots can be used. Currently, this means // that V8 was configured without the shared-readonly-heap feature. diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index aa66eb331267c4..dfa5597d5d97a0 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -840,7 +840,7 @@ size_t SnapshotSerializer::Write(const SnapshotMetadata& data) { // [ ... ] env_info // [ ... ] code_cache -void SnapshotData::ToBlob(FILE* out) const { +std::vector SnapshotData::ToBlob() const { SnapshotSerializer w; w.Debug("SnapshotData::ToBlob()\n"); @@ -858,9 +858,14 @@ void SnapshotData::ToBlob(FILE* out) const { written_total += w.Write(env_info); w.Debug("Write code_cache\n"); written_total += w.WriteVector(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 sink = ToBlob(); + size_t num_written = fwrite(sink.data(), sink.size(), 1, out); + CHECK_EQ(num_written, 1); } const SnapshotData* SnapshotData::FromEmbedderWrapper( @@ -883,8 +888,11 @@ bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) { std::vector 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& in) { + SnapshotDeserializer r(in); r.Debug("SnapshotData::FromBlob()\n"); DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned); diff --git a/test/embedding/embedtest.cc b/test/embedding/embedtest.cc index ef07056d5bd688..675ce77a0b210f 100644 --- a/test/embedding/embedtest.cc +++ b/test/embedding/embedtest.cc @@ -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 vec(filesize); + size_t read = fread(vec.data(), filesize, 1, fp); + assert(read == 1); + snapshot = node::EmbedderSnapshotData::FromBlob(vec); + } + assert(snapshot); fclose(fp); } @@ -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 vec = snapshot->ToBlob(); + size_t written = fwrite(vec.data(), vec.size(), 1, fp); + assert(written == 1); + } fclose(fp); } diff --git a/test/embedding/test-embedding.js b/test/embedding/test-embedding.js index 197158e78f2e03..498c05fdebf377 100644 --- a/test/embedding/test-embedding.js +++ b/test/embedding/test-embedding.js @@ -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, [