Skip to content

Commit d830718

Browse files
committed
src: support snapshot in single executable applications
1 parent b3663e0 commit d830718

File tree

5 files changed

+79
-36
lines changed

5 files changed

+79
-36
lines changed

src/env.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,10 +525,12 @@ struct SnapshotData {
525525
bool Check() const;
526526
static bool FromFile(SnapshotData* out, FILE* in);
527527
static bool FromBlob(SnapshotData* out, const std::vector<char>& in);
528+
static bool FromBlob(SnapshotData* out, std::string_view in);
528529
static const SnapshotData* FromEmbedderWrapper(
529530
const EmbedderSnapshotData* data);
530531
EmbedderSnapshotData::Pointer AsEmbedderWrapper() const;
531532

533+
static bool IsSnapshotBlob(std::string_view data);
532534
~SnapshotData();
533535
};
534536

src/node.cc

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,31 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
295295
}));
296296
}
297297

298+
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
299+
if (sea::IsSingleExecutable()) {
300+
const std::string_view sea_code = sea::FindSingleExecutableCode();
301+
if (SnapshotData::IsSnapshotBlob(sea_code)) {
302+
if (env->snapshot_deserialize_main().IsEmpty()) {
303+
fprintf(
304+
stderr,
305+
"No deserialized main function found for the snapshot blob found"
306+
" in single executable binary.\n");
307+
return MaybeLocal<Value>();
308+
} else {
309+
return env->RunSnapshotDeserializeMain();
310+
}
311+
}
312+
313+
// TODO(addaleax): Find a way to reuse:
314+
//
315+
// LoadEnvironment(Environment*, const char*)
316+
//
317+
// instead and not add yet another main entry point here because this
318+
// already duplicates existing code.
319+
return StartExecution(env, "internal/main/single_executable_application");
320+
}
321+
#endif
322+
298323
// TODO(joyeecheung): move these conditions into JS land and let the
299324
// deserialize main function take precedence. For workers, we need to
300325
// move the pre-execution part into a different file that can be
@@ -312,18 +337,6 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
312337
first_argv = env->argv()[1];
313338
}
314339

315-
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
316-
if (sea::IsSingleExecutable()) {
317-
// TODO(addaleax): Find a way to reuse:
318-
//
319-
// LoadEnvironment(Environment*, const char*)
320-
//
321-
// instead and not add yet another main entry point here because this
322-
// already duplicates existing code.
323-
return StartExecution(env, "internal/main/single_executable_application");
324-
}
325-
#endif
326-
327340
if (first_argv == "inspect") {
328341
return StartExecution(env, "internal/main/inspect");
329342
}
@@ -1177,8 +1190,25 @@ ExitCode LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
11771190
ExitCode exit_code = result->exit_code_enum();
11781191
// nullptr indicates there's no snapshot data.
11791192
DCHECK_NULL(*snapshot_data_ptr);
1180-
// --snapshot-blob indicates that we are reading a customized snapshot.
1181-
if (!per_process::cli_options->snapshot_blob.empty()) {
1193+
1194+
bool has_cli_snapshot_blob = !per_process::cli_options->snapshot_blob.empty();
1195+
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
1196+
if (sea::IsSingleExecutable()) {
1197+
const std::string_view sea_code = sea::FindSingleExecutableCode();
1198+
if (SnapshotData::IsSnapshotBlob(sea_code)) {
1199+
std::unique_ptr<SnapshotData> read_data =
1200+
std::make_unique<SnapshotData>();
1201+
if (SnapshotData::FromBlob(read_data.get(), sea_code)) {
1202+
*snapshot_data_ptr = read_data.release();
1203+
} else {
1204+
fprintf(stderr, "Invalid snapshot data in single executable binary\n");
1205+
return ExitCode::kGenericUserError;
1206+
}
1207+
}
1208+
} else if (has_cli_snapshot_blob) {
1209+
#else
1210+
if (has_cli_snapshot_blob) {
1211+
#endif
11821212
std::string filename = per_process::cli_options->snapshot_blob;
11831213
FILE* fp = fopen(filename.c_str(), "rb");
11841214
if (fp == nullptr) {

src/node_sea.cc

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,11 @@ using v8::Value;
3131

3232
namespace {
3333

34-
const std::string_view FindSingleExecutableCode() {
35-
static const std::string_view sea_code = []() -> std::string_view {
36-
size_t size;
37-
#ifdef __APPLE__
38-
postject_options options;
39-
postject_options_init(&options);
40-
options.macho_segment_name = "NODE_JS";
41-
const char* code = static_cast<const char*>(
42-
postject_find_resource("NODE_JS_CODE", &size, &options));
43-
#else
44-
const char* code = static_cast<const char*>(
45-
postject_find_resource("NODE_JS_CODE", &size, nullptr));
46-
#endif
47-
return {code, size};
48-
}();
49-
return sea_code;
50-
}
51-
5234
void GetSingleExecutableCode(const FunctionCallbackInfo<Value>& args) {
5335
node::Environment* env = node::Environment::GetCurrent(args);
5436

55-
static const std::string_view sea_code = FindSingleExecutableCode();
37+
static const std::string_view sea_code =
38+
node::sea::FindSingleExecutableCode();
5639

5740
if (sea_code.empty()) {
5841
return;
@@ -83,6 +66,24 @@ void GetSingleExecutableCode(const FunctionCallbackInfo<Value>& args) {
8366
namespace node {
8467
namespace sea {
8568

69+
const std::string_view FindSingleExecutableCode() {
70+
static const std::string_view sea_code = []() -> std::string_view {
71+
size_t size;
72+
#ifdef __APPLE__
73+
postject_options options;
74+
postject_options_init(&options);
75+
options.macho_segment_name = "NODE_JS";
76+
const char* code = static_cast<const char*>(
77+
postject_find_resource("NODE_JS_CODE", &size, &options));
78+
#else
79+
const char* code = static_cast<const char*>(
80+
postject_find_resource("NODE_JS_CODE", &size, nullptr));
81+
#endif
82+
return {code, size};
83+
}();
84+
return sea_code;
85+
}
86+
8687
bool IsSingleExecutable() {
8788
return postject_has_resource();
8889
}

src/node_sea.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55

66
#if !defined(DISABLE_SINGLE_EXECUTABLE_APPLICATION)
77

8+
#include <string_view>
89
#include <tuple>
910

1011
namespace node {
1112
namespace sea {
1213

1314
bool IsSingleExecutable();
1415
std::tuple<int, char**> FixupArgsForSEA(int argc, char** argv);
15-
16+
const std::string_view FindSingleExecutableCode();
1617
} // namespace sea
1718
} // namespace node
1819

src/node_snapshotable.cc

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class SnapshotSerializerDeserializer {
187187

188188
class SnapshotDeserializer : public SnapshotSerializerDeserializer {
189189
public:
190-
explicit SnapshotDeserializer(const std::vector<char>& s)
190+
explicit SnapshotDeserializer(const std::string_view s)
191191
: SnapshotSerializerDeserializer(), sink(s) {}
192192
~SnapshotDeserializer() {}
193193

@@ -246,7 +246,7 @@ class SnapshotDeserializer : public SnapshotSerializerDeserializer {
246246
}
247247

248248
size_t read_total = 0;
249-
const std::vector<char>& sink;
249+
const std::string_view sink;
250250

251251
private:
252252
// Helper for reading an array of numeric types.
@@ -883,6 +883,10 @@ bool SnapshotData::FromFile(SnapshotData* out, FILE* in) {
883883
}
884884

885885
bool SnapshotData::FromBlob(SnapshotData* out, const std::vector<char>& in) {
886+
return FromBlob(out, std::string_view(in.data(), in.size()));
887+
}
888+
889+
bool SnapshotData::FromBlob(SnapshotData* out, const std::string_view in) {
886890
SnapshotDeserializer r(in);
887891
r.Debug("SnapshotData::FromBlob()\n");
888892

@@ -909,6 +913,11 @@ bool SnapshotData::FromBlob(SnapshotData* out, const std::vector<char>& in) {
909913
return true;
910914
}
911915

916+
bool SnapshotData::IsSnapshotBlob(const std::string_view data) {
917+
const uint32_t* ptr = reinterpret_cast<const uint32_t*>(data.data());
918+
return (ptr[0] == kMagic);
919+
}
920+
912921
bool SnapshotData::Check() const {
913922
if (metadata.node_version != per_process::metadata.versions.node) {
914923
fprintf(stderr,

0 commit comments

Comments
 (0)