11#include " node_sea.h"
22
3+ #include " blob_serializer_deserializer-inl.h"
34#include " debug_utils-inl.h"
45#include " env-inl.h"
56#include " json_parser.h"
@@ -34,16 +35,6 @@ namespace node {
3435namespace sea {
3536
3637namespace {
37- // A special number that will appear at the beginning of the single executable
38- // preparation blobs ready to be injected into the binary. We use this to check
39- // that the data given to us are intended for building single executable
40- // applications.
41- const uint32_t kMagic = 0x143da20 ;
42-
43- enum class SeaFlags : uint32_t {
44- kDefault = 0 ,
45- kDisableExperimentalSeaWarning = 1 << 0 ,
46- };
4738
4839SeaFlags operator |(SeaFlags x, SeaFlags y) {
4940 return static_cast <SeaFlags>(static_cast <uint32_t >(x) |
@@ -59,47 +50,100 @@ SeaFlags operator|=(/* NOLINT (runtime/references) */ SeaFlags& x, SeaFlags y) {
5950 return x = x | y;
6051}
6152
62- struct SeaResource {
63- SeaFlags flags = SeaFlags::kDefault ;
64- std::string_view code;
65- static constexpr size_t kHeaderSize = sizeof (kMagic ) + sizeof (SeaFlags);
53+ class SeaSerializer : public BlobSerializer <SeaSerializer> {
54+ public:
55+ SeaSerializer ()
56+ : BlobSerializer<SeaSerializer>(
57+ per_process::enabled_debug_list.enabled(DebugCategory::SEA)) {}
58+
59+ template <typename T,
60+ std::enable_if_t <!std::is_same<T, std::string>::value>* = nullptr ,
61+ std::enable_if_t <!std::is_arithmetic<T>::value>* = nullptr >
62+ size_t Write (const T& data);
6663};
6764
68- SeaResource FindSingleExecutableResource () {
65+ template <>
66+ size_t SeaSerializer::Write (const SeaResource& sea) {
67+ sink.reserve (SeaResource::kHeaderSize + sea.code .size ());
68+
69+ Debug (" Write SEA magic %x\n " , kMagic );
70+ size_t written_total = WriteArithmetic<uint32_t >(kMagic );
71+
72+ uint32_t flags = static_cast <uint32_t >(sea.flags );
73+ Debug (" Write SEA flags %x\n " , flags);
74+ written_total += WriteArithmetic<uint32_t >(flags);
75+ DCHECK_EQ (written_total, SeaResource::kHeaderSize );
76+
77+ Debug (" Write SEA resource code %p, size=%zu\n " ,
78+ sea.code .data (),
79+ sea.code .size ());
80+ written_total += WriteStringView (sea.code , StringLogMode::kAddressAndContent );
81+ return written_total;
82+ }
83+
84+ class SeaDeserializer : public BlobDeserializer <SeaDeserializer> {
85+ public:
86+ explicit SeaDeserializer (std::string_view v)
87+ : BlobDeserializer<SeaDeserializer>(
88+ per_process::enabled_debug_list.enabled(DebugCategory::SEA), v) {}
89+
90+ template <typename T,
91+ std::enable_if_t <!std::is_same<T, std::string>::value>* = nullptr ,
92+ std::enable_if_t <!std::is_arithmetic<T>::value>* = nullptr >
93+ T Read ();
94+ };
95+
96+ template <>
97+ SeaResource SeaDeserializer::Read () {
98+ uint32_t magic = ReadArithmetic<uint32_t >();
99+ Debug (" Read SEA magic %x\n " , magic);
100+
101+ CHECK_EQ (magic, kMagic );
102+ SeaFlags flags (static_cast <SeaFlags>(ReadArithmetic<uint32_t >()));
103+ Debug (" Read SEA flags %x\n " , static_cast <uint32_t >(flags));
104+ CHECK_EQ (read_total, SeaResource::kHeaderSize );
105+
106+ std::string_view code = ReadStringView (StringLogMode::kAddressAndContent );
107+ Debug (" Read SEA resource code %p, size=%zu\n " , code.data (), code.size ());
108+ return {flags, code};
109+ }
110+
111+ std::string_view FindSingleExecutableBlob () {
69112 CHECK (IsSingleExecutable ());
70- static const SeaResource sea_resource = []() -> SeaResource {
113+ static const std::string_view result = []() -> std::string_view {
71114 size_t size;
72115#ifdef __APPLE__
73116 postject_options options;
74117 postject_options_init (&options);
75118 options.macho_segment_name = " NODE_SEA" ;
76- const char * code = static_cast <const char *>(
119+ const char * blob = static_cast <const char *>(
77120 postject_find_resource (" NODE_SEA_BLOB" , &size, &options));
78121#else
79- const char * code = static_cast <const char *>(
122+ const char * blob = static_cast <const char *>(
80123 postject_find_resource (" NODE_SEA_BLOB" , &size, nullptr ));
81124#endif
82- uint32_t first_word = reinterpret_cast <const uint32_t *>(code)[0 ];
83- CHECK_EQ (first_word, kMagic );
84- SeaFlags flags{
85- reinterpret_cast <const SeaFlags*>(code + sizeof (first_word))[0 ]};
86- // TODO(joyeecheung): do more checks here e.g. matching the versions.
87- return {
88- flags,
89- {
90- code + SeaResource::kHeaderSize ,
91- size - SeaResource::kHeaderSize ,
92- },
93- };
125+ return {blob, size};
94126 }();
95- return sea_resource;
127+ per_process::Debug (DebugCategory::SEA,
128+ " Found SEA blob %p, size=%zu\n " ,
129+ result.data (),
130+ result.size ());
131+ return result;
96132}
97133
98- } // namespace
134+ } // anonymous namespace
99135
100- std::string_view FindSingleExecutableCode () {
101- SeaResource sea_resource = FindSingleExecutableResource ();
102- return sea_resource.code ;
136+ SeaResource FindSingleExecutableResource () {
137+ static const SeaResource sea_resource = []() -> SeaResource {
138+ std::string_view blob = FindSingleExecutableBlob ();
139+ per_process::Debug (DebugCategory::SEA,
140+ " Found SEA resource %p, size=%zu\n " ,
141+ blob.data (),
142+ blob.size ());
143+ SeaDeserializer deserializer (blob);
144+ return deserializer.Read <SeaResource>();
145+ }();
146+ return sea_resource;
103147}
104148
105149bool IsSingleExecutable () {
@@ -194,51 +238,46 @@ std::optional<SeaConfig> ParseSingleExecutableConfig(
194238 return result;
195239}
196240
197- bool GenerateSingleExecutableBlob (const SeaConfig& config) {
241+ ExitCode GenerateSingleExecutableBlob (const SeaConfig& config) {
198242 std::string main_script;
199243 // TODO(joyeecheung): unify the file utils.
200244 int r = ReadFileSync (&main_script, config.main_path .c_str ());
201245 if (r != 0 ) {
202246 const char * err = uv_strerror (r);
203247 FPrintF (stderr, " Cannot read main script %s:%s\n " , config.main_path , err);
204- return false ;
248+ return ExitCode:: kGenericUserError ;
205249 }
206250
207- std::vector<char > sink;
208- // TODO(joyeecheung): reuse the SnapshotSerializerDeserializer for this.
209- sink.reserve (SeaResource::kHeaderSize + main_script.size ());
210- const char * pos = reinterpret_cast <const char *>(&kMagic );
211- sink.insert (sink.end (), pos, pos + sizeof (kMagic ));
212- pos = reinterpret_cast <const char *>(&(config.flags ));
213- sink.insert (sink.end (), pos, pos + sizeof (SeaFlags));
214- sink.insert (
215- sink.end (), main_script.data (), main_script.data () + main_script.size ());
216-
217- uv_buf_t buf = uv_buf_init (sink.data (), sink.size ());
251+ SeaResource sea{config.flags , main_script};
252+
253+ SeaSerializer serializer;
254+ serializer.Write (sea);
255+
256+ uv_buf_t buf = uv_buf_init (serializer.sink .data (), serializer.sink .size ());
218257 r = WriteFileSync (config.output_path .c_str (), buf);
219258 if (r != 0 ) {
220259 const char * err = uv_strerror (r);
221260 FPrintF (stderr, " Cannot write output to %s:%s\n " , config.output_path , err);
222- return false ;
261+ return ExitCode:: kGenericUserError ;
223262 }
224263
225264 FPrintF (stderr,
226265 " Wrote single executable preparation blob to %s\n " ,
227266 config.output_path );
228- return true ;
267+ return ExitCode:: kNoFailure ;
229268}
230269
231270} // anonymous namespace
232271
233272ExitCode BuildSingleExecutableBlob (const std::string& config_path) {
234273 std::optional<SeaConfig> config_opt =
235274 ParseSingleExecutableConfig (config_path);
236- if (! config_opt.has_value () ||
237- ! GenerateSingleExecutableBlob (config_opt.value ())) {
238- return ExitCode:: kGenericUserError ;
275+ if (config_opt.has_value ()) {
276+ ExitCode code = GenerateSingleExecutableBlob (config_opt.value ());
277+ return code ;
239278 }
240279
241- return ExitCode::kNoFailure ;
280+ return ExitCode::kGenericUserError ;
242281}
243282
244283void Initialize (Local<Object> target,
0 commit comments