xref: /third_party/node/src/node_snapshotable.cc (revision 1cb0ef41)
1
2#include "node_snapshotable.h"
3#include <iostream>
4#include <sstream>
5#include <vector>
6#include "base_object-inl.h"
7#include "blob_serializer_deserializer-inl.h"
8#include "debug_utils-inl.h"
9#include "env-inl.h"
10#include "node_blob.h"
11#include "node_builtins.h"
12#include "node_contextify.h"
13#include "node_errors.h"
14#include "node_external_reference.h"
15#include "node_file.h"
16#include "node_internals.h"
17#include "node_main_instance.h"
18#include "node_metadata.h"
19#include "node_process.h"
20#include "node_snapshot_builder.h"
21#include "node_url.h"
22#include "node_util.h"
23#include "node_v8.h"
24#include "node_v8_platform-inl.h"
25
26#if HAVE_INSPECTOR
27#include "inspector/worker_inspector.h"  // ParentInspectorHandle
28#endif
29
30namespace node {
31
32using v8::Context;
33using v8::Function;
34using v8::FunctionCallbackInfo;
35using v8::HandleScope;
36using v8::Isolate;
37using v8::Local;
38using v8::Object;
39using v8::ObjectTemplate;
40using v8::ScriptCompiler;
41using v8::ScriptOrigin;
42using v8::SnapshotCreator;
43using v8::StartupData;
44using v8::String;
45using v8::TryCatch;
46using v8::Value;
47
48const uint32_t SnapshotData::kMagic;
49
50std::ostream& operator<<(std::ostream& output,
51                         const builtins::CodeCacheInfo& info) {
52  output << "<builtins::CodeCacheInfo id=" << info.id
53         << ", size=" << info.data.size() << ">\n";
54  return output;
55}
56
57std::ostream& operator<<(std::ostream& output,
58                         const std::vector<builtins::CodeCacheInfo>& vec) {
59  output << "{\n";
60  for (const auto& info : vec) {
61    output << info;
62  }
63  output << "}\n";
64  return output;
65}
66
67std::ostream& operator<<(std::ostream& output,
68                         const std::vector<uint8_t>& vec) {
69  output << "{\n";
70  for (const auto& i : vec) {
71    output << i << ",";
72  }
73  output << "}";
74  return output;
75}
76
77std::ostream& operator<<(std::ostream& output,
78                         const std::vector<PropInfo>& vec) {
79  output << "{\n";
80  for (const auto& info : vec) {
81    output << "  " << info << ",\n";
82  }
83  output << "}";
84  return output;
85}
86
87std::ostream& operator<<(std::ostream& output, const PropInfo& info) {
88  output << "{ \"" << info.name << "\", " << std::to_string(info.id) << ", "
89         << std::to_string(info.index) << " }";
90  return output;
91}
92
93std::ostream& operator<<(std::ostream& output,
94                         const std::vector<std::string>& vec) {
95  output << "{\n";
96  for (const auto& info : vec) {
97    output << "  \"" << info << "\",\n";
98  }
99  output << "}";
100  return output;
101}
102
103std::ostream& operator<<(std::ostream& output, const RealmSerializeInfo& i) {
104  output << "{\n"
105         << "// -- builtins begins --\n"
106         << i.builtins << ",\n"
107         << "// -- builtins ends --\n"
108         << "// -- persistent_values begins --\n"
109         << i.persistent_values << ",\n"
110         << "// -- persistent_values ends --\n"
111         << "// -- native_objects begins --\n"
112         << i.native_objects << ",\n"
113         << "// -- native_objects ends --\n"
114         << i.context << ",  // context\n"
115         << "}";
116  return output;
117}
118
119std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
120  output << "{\n"
121         << "// -- async_hooks begins --\n"
122         << i.async_hooks << ",\n"
123         << "// -- async_hooks ends --\n"
124         << i.tick_info << ",  // tick_info\n"
125         << i.immediate_info << ",  // immediate_info\n"
126         << i.timeout_info << ",  // timeout_info\n"
127         << "// -- performance_state begins --\n"
128         << i.performance_state << ",\n"
129         << "// -- performance_state ends --\n"
130         << i.exiting << ",  // exiting\n"
131         << i.stream_base_state << ",  // stream_base_state\n"
132         << i.should_abort_on_uncaught_toggle
133         << ",  // should_abort_on_uncaught_toggle\n"
134         << "// -- principal_realm begins --\n"
135         << i.principal_realm << ",\n"
136         << "// -- principal_realm ends --\n"
137         << "}";
138  return output;
139}
140
141class SnapshotSerializerDeserializer {
142 public:
143  SnapshotSerializerDeserializer()
144      : is_debug(per_process::enabled_debug_list.enabled(
145            DebugCategory::MKSNAPSHOT)) {}
146
147  template <typename... Args>
148  void Debug(const char* format, Args&&... args) const {
149    per_process::Debug(
150        DebugCategory::MKSNAPSHOT, format, std::forward<Args>(args)...);
151  }
152
153  template <typename T>
154  std::string ToStr(const T& arg) const {
155    std::stringstream ss;
156    ss << arg;
157    return ss.str();
158  }
159
160  template <typename T>
161  std::string GetName() const {
162#define TYPE_LIST(V)                                                           \
163  V(builtins::CodeCacheInfo)                                                   \
164  V(PropInfo)                                                                  \
165  V(std::string)
166
167#define V(TypeName)                                                            \
168  if constexpr (std::is_same_v<T, TypeName>) {                                 \
169    return #TypeName;                                                          \
170  } else  // NOLINT(readability/braces)
171    TYPE_LIST(V)
172#undef V
173
174    if constexpr (std::is_arithmetic_v<T>) {
175      return (std::is_unsigned_v<T>   ? "uint"
176              : std::is_integral_v<T> ? "int"
177                                      : "float") +
178             std::to_string(sizeof(T) * 8) + "_t";
179    }
180    return "";
181  }
182
183  bool is_debug = false;
184};
185
186class SnapshotDeserializer : public SnapshotSerializerDeserializer {
187 public:
188  explicit SnapshotDeserializer(const std::vector<char>& s)
189      : SnapshotSerializerDeserializer(), sink(s) {}
190  ~SnapshotDeserializer() {}
191
192  // Helper for reading numeric types.
193  template <typename T>
194  T Read() {
195    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
196    T result;
197    Read(&result, 1);
198    return result;
199  }
200
201  // Layout of vectors:
202  // [ 4/8 bytes ] count
203  // [   ...     ] contents (count * size of individual elements)
204  template <typename T>
205  std::vector<T> ReadVector() {
206    if (is_debug) {
207      std::string name = GetName<T>();
208      Debug("\nReadVector<%s>()(%d-byte)\n", name.c_str(), sizeof(T));
209    }
210    size_t count = static_cast<size_t>(Read<size_t>());
211    if (count == 0) {
212      return std::vector<T>();
213    }
214    if (is_debug) {
215      Debug("Reading %d vector elements...\n", count);
216    }
217    std::vector<T> result = ReadVector<T>(count, std::is_arithmetic<T>{});
218    if (is_debug) {
219      std::string str = std::is_arithmetic_v<T> ? "" : ToStr(result);
220      std::string name = GetName<T>();
221      Debug("ReadVector<%s>() read %s\n", name.c_str(), str.c_str());
222    }
223    return result;
224  }
225
226  std::string ReadString() {
227    size_t length = Read<size_t>();
228
229    if (is_debug) {
230      Debug("ReadString(), length=%d: ", length);
231    }
232
233    CHECK_GT(length, 0);  // There should be no empty strings.
234    MallocedBuffer<char> buf(length + 1);
235    memcpy(buf.data, sink.data() + read_total, length + 1);
236    std::string result(buf.data, length);  // This creates a copy of buf.data.
237
238    if (is_debug) {
239      Debug("\"%s\", read %zu bytes\n", result.c_str(), length + 1);
240    }
241
242    read_total += length + 1;
243    return result;
244  }
245
246  size_t read_total = 0;
247  const std::vector<char>& sink;
248
249 private:
250  // Helper for reading an array of numeric types.
251  template <typename T>
252  void Read(T* out, size_t count) {
253    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
254    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
255    if (is_debug) {
256      std::string name = GetName<T>();
257      Debug("Read<%s>()(%d-byte), count=%d: ", name.c_str(), sizeof(T), count);
258    }
259
260    size_t size = sizeof(T) * count;
261    memcpy(out, sink.data() + read_total, size);
262
263    if (is_debug) {
264      std::string str =
265          "{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }");
266      Debug("%s, read %zu bytes\n", str.c_str(), size);
267    }
268    read_total += size;
269  }
270
271  // Helper for reading numeric vectors.
272  template <typename Number>
273  std::vector<Number> ReadVector(size_t count, std::true_type) {
274    static_assert(std::is_arithmetic_v<Number>, "Not an arithmetic type");
275    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
276    std::vector<Number> result(count);
277    Read(result.data(), count);
278    return result;
279  }
280
281  // Helper for reading non-numeric vectors.
282  template <typename T>
283  std::vector<T> ReadVector(size_t count, std::false_type) {
284    static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
285    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
286    std::vector<T> result;
287    result.reserve(count);
288    bool original_is_debug = is_debug;
289    is_debug = original_is_debug && !std::is_same_v<T, std::string>;
290    for (size_t i = 0; i < count; ++i) {
291      if (is_debug) {
292        Debug("\n[%d] ", i);
293      }
294      result.push_back(Read<T>());
295    }
296    is_debug = original_is_debug;
297
298    return result;
299  }
300};
301
302class SnapshotSerializer : public SnapshotSerializerDeserializer {
303 public:
304  SnapshotSerializer() : SnapshotSerializerDeserializer() {
305    // Currently the snapshot blob built with an empty script is around 4MB.
306    // So use that as the default sink size.
307    sink.reserve(4 * 1024 * 1024);
308  }
309  ~SnapshotSerializer() {}
310  std::vector<char> sink;
311
312  // Helper for writing numeric types.
313  template <typename T>
314  size_t Write(const T& data) {
315    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
316    return Write(&data, 1);
317  }
318
319  // Layout of vectors:
320  // [ 4/8 bytes ] count
321  // [   ...     ] contents (count * size of individual elements)
322  template <typename T>
323  size_t WriteVector(const std::vector<T>& data) {
324    if (is_debug) {
325      std::string str = std::is_arithmetic_v<T> ? "" : ToStr(data);
326      std::string name = GetName<T>();
327      Debug("\nWriteVector<%s>() (%d-byte), count=%d: %s\n",
328            name.c_str(),
329            sizeof(T),
330            data.size(),
331            str.c_str());
332    }
333
334    size_t written_total = Write<size_t>(data.size());
335    if (data.size() == 0) {
336      return written_total;
337    }
338    written_total += WriteVector<T>(data, std::is_arithmetic<T>{});
339
340    if (is_debug) {
341      std::string name = GetName<T>();
342      Debug("WriteVector<%s>() wrote %d bytes\n", name.c_str(), written_total);
343    }
344
345    return written_total;
346  }
347
348  // The layout of a written string:
349  // [  4/8 bytes     ] length
350  // [ |length| bytes ] contents
351  size_t WriteString(const std::string& data) {
352    CHECK_GT(data.size(), 0);  // No empty strings should be written.
353    size_t written_total = Write<size_t>(data.size());
354    if (is_debug) {
355      std::string str = ToStr(data);
356      Debug("WriteString(), length=%zu: \"%s\"\n", data.size(), data.c_str());
357    }
358
359    // Write the null-terminated string.
360    size_t length = data.size() + 1;
361    sink.insert(sink.end(), data.c_str(), data.c_str() + length);
362    written_total += length;
363
364    if (is_debug) {
365      Debug("WriteString() wrote %zu bytes\n", written_total);
366    }
367
368    return written_total;
369  }
370
371 private:
372  // Helper for writing an array of numeric types.
373  template <typename T>
374  size_t Write(const T* data, size_t count) {
375    DCHECK_GT(count, 0);  // Should not write contents for vectors of size 0.
376    if (is_debug) {
377      std::string str =
378          "{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }");
379      std::string name = GetName<T>();
380      Debug("Write<%s>() (%zu-byte), count=%zu: %s",
381            name.c_str(),
382            sizeof(T),
383            count,
384            str.c_str());
385    }
386
387    size_t size = sizeof(T) * count;
388    const char* pos = reinterpret_cast<const char*>(data);
389    sink.insert(sink.end(), pos, pos + size);
390
391    if (is_debug) {
392      Debug(", wrote %zu bytes\n", size);
393    }
394    return size;
395  }
396
397  // Helper for writing numeric vectors.
398  template <typename Number>
399  size_t WriteVector(const std::vector<Number>& data, std::true_type) {
400    return Write(data.data(), data.size());
401  }
402
403  // Helper for writing non-numeric vectors.
404  template <typename T>
405  size_t WriteVector(const std::vector<T>& data, std::false_type) {
406    DCHECK_GT(data.size(),
407              0);  // Should not write contents for vectors of size 0.
408    size_t written_total = 0;
409    bool original_is_debug = is_debug;
410    is_debug = original_is_debug && !std::is_same_v<T, std::string>;
411    for (size_t i = 0; i < data.size(); ++i) {
412      if (is_debug) {
413        Debug("\n[%d] ", i);
414      }
415      written_total += Write<T>(data[i]);
416    }
417    is_debug = original_is_debug;
418
419    return written_total;
420  }
421};
422
423// Layout of serialized std::string:
424// [  4/8 bytes     ] length
425// [ |length| bytes ] contents
426template <>
427std::string SnapshotDeserializer::Read() {
428  return ReadString();
429}
430template <>
431size_t SnapshotSerializer::Write(const std::string& data) {
432  return WriteString(data);
433}
434
435// Layout of v8::StartupData
436// [  4/8 bytes       ] raw_size
437// [ |raw_size| bytes ] contents
438template <>
439v8::StartupData SnapshotDeserializer::Read() {
440  Debug("Read<v8::StartupData>()\n");
441
442  int raw_size = Read<int>();
443  Debug("size=%d\n", raw_size);
444
445  CHECK_GT(raw_size, 0);  // There should be no startup data of size 0.
446  // The data pointer of v8::StartupData would be deleted so it must be new'ed.
447  std::unique_ptr<char> buf = std::unique_ptr<char>(new char[raw_size]);
448  Read<char>(buf.get(), raw_size);
449
450  return v8::StartupData{buf.release(), raw_size};
451}
452
453template <>
454size_t SnapshotSerializer::Write(const v8::StartupData& data) {
455  Debug("\nWrite<v8::StartupData>() size=%d\n", data.raw_size);
456
457  CHECK_GT(data.raw_size, 0);  // There should be no startup data of size 0.
458  size_t written_total = Write<int>(data.raw_size);
459  written_total += Write<char>(data.data, static_cast<size_t>(data.raw_size));
460
461  Debug("Write<v8::StartupData>() wrote %d bytes\n\n", written_total);
462  return written_total;
463}
464
465// Layout of builtins::CodeCacheInfo
466// [  4/8 bytes ]  length of the module id string
467// [    ...     ]  |length| bytes of module id
468// [  4/8 bytes ]  length of module code cache
469// [    ...     ]  |length| bytes of module code cache
470template <>
471builtins::CodeCacheInfo SnapshotDeserializer::Read() {
472  Debug("Read<builtins::CodeCacheInfo>()\n");
473
474  builtins::CodeCacheInfo result{ReadString(), ReadVector<uint8_t>()};
475
476  if (is_debug) {
477    std::string str = ToStr(result);
478    Debug("Read<builtins::CodeCacheInfo>() %s\n", str.c_str());
479  }
480  return result;
481}
482
483template <>
484size_t SnapshotSerializer::Write(const builtins::CodeCacheInfo& data) {
485  Debug("\nWrite<builtins::CodeCacheInfo>() id = %s"
486        ", size=%d\n",
487        data.id.c_str(),
488        data.data.size());
489
490  size_t written_total = WriteString(data.id);
491  written_total += WriteVector<uint8_t>(data.data);
492
493  Debug("Write<builtins::CodeCacheInfo>() wrote %d bytes\n", written_total);
494  return written_total;
495}
496
497// Layout of PropInfo
498// [ 4/8 bytes ]  length of the data name string
499// [    ...    ]  |length| bytes of data name
500// [  4 bytes  ]  index in the PropInfo vector
501// [ 4/8 bytes ]  index in the snapshot blob, can be used with
502//                GetDataFromSnapshotOnce().
503template <>
504PropInfo SnapshotDeserializer::Read() {
505  Debug("Read<PropInfo>()\n");
506
507  PropInfo result;
508  result.name = ReadString();
509  result.id = Read<uint32_t>();
510  result.index = Read<SnapshotIndex>();
511
512  if (is_debug) {
513    std::string str = ToStr(result);
514    Debug("Read<PropInfo>() %s\n", str.c_str());
515  }
516
517  return result;
518}
519
520template <>
521size_t SnapshotSerializer::Write(const PropInfo& data) {
522  if (is_debug) {
523    std::string str = ToStr(data);
524    Debug("Write<PropInfo>() %s\n", str.c_str());
525  }
526
527  size_t written_total = WriteString(data.name);
528  written_total += Write<uint32_t>(data.id);
529  written_total += Write<SnapshotIndex>(data.index);
530
531  Debug("Write<PropInfo>() wrote %d bytes\n", written_total);
532  return written_total;
533}
534
535// Layout of AsyncHooks::SerializeInfo
536// [ 4/8 bytes ]  snapshot index of async_ids_stack
537// [ 4/8 bytes ]  snapshot index of fields
538// [ 4/8 bytes ]  snapshot index of async_id_fields
539// [ 4/8 bytes ]  snapshot index of js_execution_async_resources
540// [ 4/8 bytes ]  length of native_execution_async_resources
541// [   ...     ]  snapshot indices of each element in
542//                native_execution_async_resources
543template <>
544AsyncHooks::SerializeInfo SnapshotDeserializer::Read() {
545  Debug("Read<AsyncHooks::SerializeInfo>()\n");
546
547  AsyncHooks::SerializeInfo result;
548  result.async_ids_stack = Read<AliasedBufferIndex>();
549  result.fields = Read<AliasedBufferIndex>();
550  result.async_id_fields = Read<AliasedBufferIndex>();
551  result.js_execution_async_resources = Read<SnapshotIndex>();
552  result.native_execution_async_resources = ReadVector<SnapshotIndex>();
553
554  if (is_debug) {
555    std::string str = ToStr(result);
556    Debug("Read<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
557  }
558
559  return result;
560}
561template <>
562size_t SnapshotSerializer::Write(const AsyncHooks::SerializeInfo& data) {
563  if (is_debug) {
564    std::string str = ToStr(data);
565    Debug("Write<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
566  }
567
568  size_t written_total = Write<AliasedBufferIndex>(data.async_ids_stack);
569  written_total += Write<AliasedBufferIndex>(data.fields);
570  written_total += Write<AliasedBufferIndex>(data.async_id_fields);
571  written_total += Write<SnapshotIndex>(data.js_execution_async_resources);
572  written_total +=
573      WriteVector<SnapshotIndex>(data.native_execution_async_resources);
574
575  Debug("Write<AsyncHooks::SerializeInfo>() wrote %d bytes\n", written_total);
576  return written_total;
577}
578
579// Layout of TickInfo::SerializeInfo
580// [ 4/8 bytes ]  snapshot index of fields
581template <>
582TickInfo::SerializeInfo SnapshotDeserializer::Read() {
583  Debug("Read<TickInfo::SerializeInfo>()\n");
584
585  TickInfo::SerializeInfo result;
586  result.fields = Read<AliasedBufferIndex>();
587
588  if (is_debug) {
589    std::string str = ToStr(result);
590    Debug("Read<TickInfo::SerializeInfo>() %s\n", str.c_str());
591  }
592
593  return result;
594}
595
596template <>
597size_t SnapshotSerializer::Write(const TickInfo::SerializeInfo& data) {
598  if (is_debug) {
599    std::string str = ToStr(data);
600    Debug("Write<TickInfo::SerializeInfo>() %s\n", str.c_str());
601  }
602
603  size_t written_total = Write<AliasedBufferIndex>(data.fields);
604
605  Debug("Write<TickInfo::SerializeInfo>() wrote %d bytes\n", written_total);
606  return written_total;
607}
608
609// Layout of TickInfo::SerializeInfo
610// [ 4/8 bytes ]  snapshot index of fields
611template <>
612ImmediateInfo::SerializeInfo SnapshotDeserializer::Read() {
613  per_process::Debug(DebugCategory::MKSNAPSHOT,
614                     "Read<ImmediateInfo::SerializeInfo>()\n");
615
616  ImmediateInfo::SerializeInfo result;
617  result.fields = Read<AliasedBufferIndex>();
618  if (is_debug) {
619    std::string str = ToStr(result);
620    Debug("Read<ImmediateInfo::SerializeInfo>() %s\n", str.c_str());
621  }
622  return result;
623}
624
625template <>
626size_t SnapshotSerializer::Write(const ImmediateInfo::SerializeInfo& data) {
627  if (is_debug) {
628    std::string str = ToStr(data);
629    Debug("Write<ImmediateInfo::SerializeInfo>() %s\n", str.c_str());
630  }
631
632  size_t written_total = Write<AliasedBufferIndex>(data.fields);
633
634  Debug("Write<ImmediateInfo::SerializeInfo>() wrote %d bytes\n",
635        written_total);
636  return written_total;
637}
638
639// Layout of PerformanceState::SerializeInfo
640// [ 4/8 bytes ]  snapshot index of root
641// [ 4/8 bytes ]  snapshot index of milestones
642// [ 4/8 bytes ]  snapshot index of observers
643template <>
644performance::PerformanceState::SerializeInfo SnapshotDeserializer::Read() {
645  per_process::Debug(DebugCategory::MKSNAPSHOT,
646                     "Read<PerformanceState::SerializeInfo>()\n");
647
648  performance::PerformanceState::SerializeInfo result;
649  result.root = Read<AliasedBufferIndex>();
650  result.milestones = Read<AliasedBufferIndex>();
651  result.observers = Read<AliasedBufferIndex>();
652  if (is_debug) {
653    std::string str = ToStr(result);
654    Debug("Read<PerformanceState::SerializeInfo>() %s\n", str.c_str());
655  }
656  return result;
657}
658
659template <>
660size_t SnapshotSerializer::Write(
661    const performance::PerformanceState::SerializeInfo& data) {
662  if (is_debug) {
663    std::string str = ToStr(data);
664    Debug("Write<PerformanceState::SerializeInfo>() %s\n", str.c_str());
665  }
666
667  size_t written_total = Write<AliasedBufferIndex>(data.root);
668  written_total += Write<AliasedBufferIndex>(data.milestones);
669  written_total += Write<AliasedBufferIndex>(data.observers);
670
671  Debug("Write<PerformanceState::SerializeInfo>() wrote %d bytes\n",
672        written_total);
673  return written_total;
674}
675
676// Layout of IsolateDataSerializeInfo
677// [ 4/8 bytes ]  length of primitive_values vector
678// [    ...    ]  |length| of primitive_values indices
679// [ 4/8 bytes ]  length of template_values vector
680// [    ...    ]  |length| of PropInfo data
681template <>
682IsolateDataSerializeInfo SnapshotDeserializer::Read() {
683  per_process::Debug(DebugCategory::MKSNAPSHOT,
684                     "Read<IsolateDataSerializeInfo>()\n");
685
686  IsolateDataSerializeInfo result;
687  result.primitive_values = ReadVector<SnapshotIndex>();
688  result.template_values = ReadVector<PropInfo>();
689  if (is_debug) {
690    std::string str = ToStr(result);
691    Debug("Read<IsolateDataSerializeInfo>() %s\n", str.c_str());
692  }
693  return result;
694}
695
696template <>
697size_t SnapshotSerializer::Write(const IsolateDataSerializeInfo& data) {
698  if (is_debug) {
699    std::string str = ToStr(data);
700    Debug("Write<IsolateDataSerializeInfo>() %s\n", str.c_str());
701  }
702
703  size_t written_total = WriteVector<SnapshotIndex>(data.primitive_values);
704  written_total += WriteVector<PropInfo>(data.template_values);
705
706  Debug("Write<IsolateDataSerializeInfo>() wrote %d bytes\n", written_total);
707  return written_total;
708}
709
710template <>
711RealmSerializeInfo SnapshotDeserializer::Read() {
712  per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<RealmSerializeInfo>()\n");
713  RealmSerializeInfo result;
714  result.builtins = ReadVector<std::string>();
715  result.persistent_values = ReadVector<PropInfo>();
716  result.native_objects = ReadVector<PropInfo>();
717  result.context = Read<SnapshotIndex>();
718  return result;
719}
720
721template <>
722size_t SnapshotSerializer::Write(const RealmSerializeInfo& data) {
723  if (is_debug) {
724    std::string str = ToStr(data);
725    Debug("\nWrite<RealmSerializeInfo>() %s\n", str.c_str());
726  }
727
728  // Use += here to ensure order of evaluation.
729  size_t written_total = WriteVector<std::string>(data.builtins);
730  written_total += WriteVector<PropInfo>(data.persistent_values);
731  written_total += WriteVector<PropInfo>(data.native_objects);
732  written_total += Write<SnapshotIndex>(data.context);
733
734  Debug("Write<RealmSerializeInfo>() wrote %d bytes\n", written_total);
735  return written_total;
736}
737
738template <>
739EnvSerializeInfo SnapshotDeserializer::Read() {
740  per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<EnvSerializeInfo>()\n");
741  EnvSerializeInfo result;
742  result.async_hooks = Read<AsyncHooks::SerializeInfo>();
743  result.tick_info = Read<TickInfo::SerializeInfo>();
744  result.immediate_info = Read<ImmediateInfo::SerializeInfo>();
745  result.timeout_info = Read<AliasedBufferIndex>();
746  result.performance_state =
747      Read<performance::PerformanceState::SerializeInfo>();
748  result.exiting = Read<AliasedBufferIndex>();
749  result.stream_base_state = Read<AliasedBufferIndex>();
750  result.should_abort_on_uncaught_toggle = Read<AliasedBufferIndex>();
751  result.principal_realm = Read<RealmSerializeInfo>();
752  return result;
753}
754
755template <>
756size_t SnapshotSerializer::Write(const EnvSerializeInfo& data) {
757  if (is_debug) {
758    std::string str = ToStr(data);
759    Debug("\nWrite<EnvSerializeInfo>() %s\n", str.c_str());
760  }
761
762  // Use += here to ensure order of evaluation.
763  size_t written_total = Write<AsyncHooks::SerializeInfo>(data.async_hooks);
764  written_total += Write<TickInfo::SerializeInfo>(data.tick_info);
765  written_total += Write<ImmediateInfo::SerializeInfo>(data.immediate_info);
766  written_total += Write<AliasedBufferIndex>(data.timeout_info);
767  written_total += Write<performance::PerformanceState::SerializeInfo>(
768      data.performance_state);
769  written_total += Write<AliasedBufferIndex>(data.exiting);
770  written_total += Write<AliasedBufferIndex>(data.stream_base_state);
771  written_total +=
772      Write<AliasedBufferIndex>(data.should_abort_on_uncaught_toggle);
773  written_total += Write<RealmSerializeInfo>(data.principal_realm);
774
775  Debug("Write<EnvSerializeInfo>() wrote %d bytes\n", written_total);
776  return written_total;
777}
778
779// Layout of SnapshotMetadata
780// [  1 byte   ]  type of the snapshot
781// [ 4/8 bytes ]  length of the node version string
782// [    ...    ]  |length| bytes of node version
783// [ 4/8 bytes ]  length of the node arch string
784// [    ...    ]  |length| bytes of node arch
785// [ 4/8 bytes ]  length of the node platform string
786// [    ...    ]  |length| bytes of node platform
787// [  4 bytes  ]  v8 cache version tag
788template <>
789SnapshotMetadata SnapshotDeserializer::Read() {
790  per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<SnapshotMetadata>()\n");
791
792  SnapshotMetadata result;
793  result.type = static_cast<SnapshotMetadata::Type>(Read<uint8_t>());
794  result.node_version = ReadString();
795  result.node_arch = ReadString();
796  result.node_platform = ReadString();
797  result.v8_cache_version_tag = Read<uint32_t>();
798
799  if (is_debug) {
800    std::string str = ToStr(result);
801    Debug("Read<SnapshotMetadata>() %s\n", str.c_str());
802  }
803  return result;
804}
805
806template <>
807size_t SnapshotSerializer::Write(const SnapshotMetadata& data) {
808  if (is_debug) {
809    std::string str = ToStr(data);
810    Debug("\nWrite<SnapshotMetadata>() %s\n", str.c_str());
811  }
812  size_t written_total = 0;
813  // We need the Node.js version, platform and arch to match because
814  // Node.js may perform synchronizations that are platform-specific and they
815  // can be changed in semver-patches.
816  Debug("Write snapshot type %" PRIu8 "\n", static_cast<uint8_t>(data.type));
817  written_total += Write<uint8_t>(static_cast<uint8_t>(data.type));
818  Debug("Write Node.js version %s\n", data.node_version.c_str());
819  written_total += WriteString(data.node_version);
820  Debug("Write Node.js arch %s\n", data.node_arch);
821  written_total += WriteString(data.node_arch);
822  Debug("Write Node.js platform %s\n", data.node_platform);
823  written_total += WriteString(data.node_platform);
824  Debug("Write V8 cached data version tag %" PRIx32 "\n",
825        data.v8_cache_version_tag);
826  written_total += Write<uint32_t>(data.v8_cache_version_tag);
827  return written_total;
828}
829
830// Layout of the snapshot blob
831// [   4 bytes    ]  kMagic
832// [   4/8 bytes  ]  length of Node.js version string
833// [    ...       ]  contents of Node.js version string
834// [   4/8 bytes  ]  length of Node.js arch string
835// [    ...       ]  contents of Node.js arch string
836// [    ...       ]  v8_snapshot_blob_data from SnapshotCreator::CreateBlob()
837// [    ...       ]  isolate_data_info
838// [    ...       ]  env_info
839// [    ...       ]  code_cache
840
841void SnapshotData::ToBlob(FILE* out) const {
842  SnapshotSerializer w;
843  w.Debug("SnapshotData::ToBlob()\n");
844
845  size_t written_total = 0;
846
847  // Metadata
848  w.Debug("Write magic %" PRIx32 "\n", kMagic);
849  written_total += w.Write<uint32_t>(kMagic);
850  w.Debug("Write metadata\n");
851  written_total += w.Write<SnapshotMetadata>(metadata);
852
853  written_total += w.Write<v8::StartupData>(v8_snapshot_blob_data);
854  w.Debug("Write isolate_data_indices\n");
855  written_total += w.Write<IsolateDataSerializeInfo>(isolate_data_info);
856  written_total += w.Write<EnvSerializeInfo>(env_info);
857  w.Debug("Write code_cache\n");
858  written_total += w.WriteVector<builtins::CodeCacheInfo>(code_cache);
859  size_t num_written = fwrite(w.sink.data(), w.sink.size(), 1, out);
860  CHECK_EQ(num_written, 1);
861  w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
862  CHECK_EQ(fflush(out), 0);
863}
864
865bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
866  CHECK_EQ(ftell(in), 0);
867  int err = fseek(in, 0, SEEK_END);
868  CHECK_EQ(err, 0);
869  size_t size = ftell(in);
870  CHECK_NE(size, static_cast<size_t>(-1L));
871  err = fseek(in, 0, SEEK_SET);
872  CHECK_EQ(err, 0);
873
874  std::vector<char> sink(size);
875  size_t num_read = fread(sink.data(), size, 1, in);
876  CHECK_EQ(num_read, 1);
877
878  SnapshotDeserializer r(sink);
879  r.Debug("SnapshotData::FromBlob()\n");
880
881  DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);
882
883  // Metadata
884  uint32_t magic = r.Read<uint32_t>();
885  r.Debug("Read magic %" PRIx32 "\n", magic);
886  CHECK_EQ(magic, kMagic);
887  out->metadata = r.Read<SnapshotMetadata>();
888  r.Debug("Read metadata\n");
889  if (!out->Check()) {
890    return false;
891  }
892
893  out->v8_snapshot_blob_data = r.Read<v8::StartupData>();
894  r.Debug("Read isolate_data_info\n");
895  out->isolate_data_info = r.Read<IsolateDataSerializeInfo>();
896  out->env_info = r.Read<EnvSerializeInfo>();
897  r.Debug("Read code_cache\n");
898  out->code_cache = r.ReadVector<builtins::CodeCacheInfo>();
899
900  r.Debug("SnapshotData::FromBlob() read %d bytes\n", r.read_total);
901  return true;
902}
903
904bool SnapshotData::Check() const {
905  if (metadata.node_version != per_process::metadata.versions.node) {
906    fprintf(stderr,
907            "Failed to load the startup snapshot because it was built with"
908            "Node.js version %s and the current Node.js version is %s.\n",
909            metadata.node_version.c_str(),
910            NODE_VERSION);
911    return false;
912  }
913
914  if (metadata.node_arch != per_process::metadata.arch) {
915    fprintf(stderr,
916            "Failed to load the startup snapshot because it was built with"
917            "architecture %s and the architecture is %s.\n",
918            metadata.node_arch.c_str(),
919            NODE_ARCH);
920    return false;
921  }
922
923  if (metadata.node_platform != per_process::metadata.platform) {
924    fprintf(stderr,
925            "Failed to load the startup snapshot because it was built with"
926            "platform %s and the current platform is %s.\n",
927            metadata.node_platform.c_str(),
928            NODE_PLATFORM);
929    return false;
930  }
931
932  uint32_t current_cache_version = v8::ScriptCompiler::CachedDataVersionTag();
933  if (metadata.v8_cache_version_tag != current_cache_version &&
934      metadata.type == SnapshotMetadata::Type::kFullyCustomized) {
935    // For now we only do this check for the customized snapshots - we know
936    // that the flags we use in the default snapshot are limited and safe
937    // enough so we can relax the constraints for it.
938    fprintf(stderr,
939            "Failed to load the startup snapshot because it was built with "
940            "a different version of V8 or with different V8 configurations.\n"
941            "Expected tag %" PRIx32 ", read %" PRIx32 "\n",
942            current_cache_version,
943            metadata.v8_cache_version_tag);
944    return false;
945  }
946
947  // TODO(joyeecheung): check incompatible Node.js flags.
948  return true;
949}
950
951SnapshotData::~SnapshotData() {
952  if (data_ownership == DataOwnership::kOwned &&
953      v8_snapshot_blob_data.data != nullptr) {
954    delete[] v8_snapshot_blob_data.data;
955  }
956}
957
958template <typename T>
959void WriteVector(std::ostream* ss, const T* vec, size_t size) {
960  for (size_t i = 0; i < size; i++) {
961    *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
962  }
963}
964
965static std::string GetCodeCacheDefName(const std::string& id) {
966  char buf[64] = {0};
967  size_t size = id.size();
968  CHECK_LT(size, sizeof(buf));
969  for (size_t i = 0; i < size; ++i) {
970    char ch = id[i];
971    buf[i] = (ch == '-' || ch == '/') ? '_' : ch;
972  }
973  return std::string(buf) + std::string("_cache_data");
974}
975
976static std::string FormatSize(size_t size) {
977  char buf[64] = {0};
978  if (size < 1024) {
979    snprintf(buf, sizeof(buf), "%.2fB", static_cast<double>(size));
980  } else if (size < 1024 * 1024) {
981    snprintf(buf, sizeof(buf), "%.2fKB", static_cast<double>(size / 1024));
982  } else {
983    snprintf(
984        buf, sizeof(buf), "%.2fMB", static_cast<double>(size / 1024 / 1024));
985  }
986  return buf;
987}
988
989static void WriteStaticCodeCacheData(std::ostream* ss,
990                                     const builtins::CodeCacheInfo& info) {
991  *ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n";
992  WriteVector(ss, info.data.data(), info.data.size());
993  *ss << "};";
994}
995
996static void WriteCodeCacheInitializer(std::ostream* ss, const std::string& id) {
997  std::string def_name = GetCodeCacheDefName(id);
998  *ss << "    { \"" << id << "\",\n";
999  *ss << "      {" << def_name << ",\n";
1000  *ss << "       " << def_name << " + arraysize(" << def_name << "),\n";
1001  *ss << "      }\n";
1002  *ss << "    },\n";
1003}
1004
1005void FormatBlob(std::ostream& ss, const SnapshotData* data) {
1006  ss << R"(#include <cstddef>
1007#include "env.h"
1008#include "node_snapshot_builder.h"
1009#include "v8.h"
1010
1011// This file is generated by tools/snapshot. Do not edit.
1012
1013namespace node {
1014
1015static const char v8_snapshot_blob_data[] = {
1016)";
1017  WriteVector(&ss,
1018              data->v8_snapshot_blob_data.data,
1019              data->v8_snapshot_blob_data.raw_size);
1020  ss << R"(};
1021
1022static const int v8_snapshot_blob_size = )"
1023     << data->v8_snapshot_blob_data.raw_size << ";";
1024
1025  // Windows can't deal with too many large vector initializers.
1026  // Store the data into static arrays first.
1027  for (const auto& item : data->code_cache) {
1028    WriteStaticCodeCacheData(&ss, item);
1029  }
1030
1031  ss << R"(const SnapshotData snapshot_data {
1032  // -- data_ownership begins --
1033  SnapshotData::DataOwnership::kNotOwned,
1034  // -- data_ownership ends --
1035  // -- metadata begins --
1036)" << data->metadata
1037     << R"(,
1038  // -- metadata ends --
1039  // -- v8_snapshot_blob_data begins --
1040  { v8_snapshot_blob_data, v8_snapshot_blob_size },
1041  // -- v8_snapshot_blob_data ends --
1042  // -- isolate_data_info begins --
1043)" << data->isolate_data_info
1044     << R"(
1045  // -- isolate_data_info ends --
1046  ,
1047  // -- env_info begins --
1048)" << data->env_info
1049     << R"(
1050  // -- env_info ends --
1051  ,
1052  // -- code_cache begins --
1053  {)";
1054  for (const auto& item : data->code_cache) {
1055    WriteCodeCacheInitializer(&ss, item.id);
1056  }
1057  ss << R"(
1058  }
1059  // -- code_cache ends --
1060};
1061
1062const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() {
1063  return &snapshot_data;
1064}
1065}  // namespace node
1066)";
1067}
1068
1069// Reset context settings that need to be initialized again after
1070// deserialization.
1071static void ResetContextSettingsBeforeSnapshot(Local<Context> context) {
1072  // Reset the AllowCodeGenerationFromStrings flag to true (default value) so
1073  // that it can be re-initialized with v8 flag
1074  // --disallow-code-generation-from-strings and recognized in
1075  // node::InitializeContextRuntime.
1076  context->AllowCodeGenerationFromStrings(true);
1077}
1078
1079const std::vector<intptr_t>& SnapshotBuilder::CollectExternalReferences() {
1080  static auto registry = std::make_unique<ExternalReferenceRegistry>();
1081  return registry->external_references();
1082}
1083
1084void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
1085                                              Isolate::CreateParams* params) {
1086  params->external_references = CollectExternalReferences().data();
1087  params->snapshot_blob =
1088      const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
1089}
1090
1091// TODO(joyeecheung): share these exit code constants across the code base.
1092constexpr int UNCAUGHT_EXCEPTION_ERROR = 1;
1093constexpr int BOOTSTRAP_ERROR = 10;
1094constexpr int SNAPSHOT_ERROR = 14;
1095
1096int SnapshotBuilder::Generate(SnapshotData* out,
1097                              const std::vector<std::string> args,
1098                              const std::vector<std::string> exec_args) {
1099  const std::vector<intptr_t>& external_references =
1100      CollectExternalReferences();
1101  Isolate* isolate = Isolate::Allocate();
1102  // Must be done before the SnapshotCreator creation so  that the
1103  // memory reducer can be initialized.
1104  per_process::v8_platform.Platform()->RegisterIsolate(isolate,
1105                                                       uv_default_loop());
1106
1107  SnapshotCreator creator(isolate, external_references.data());
1108
1109  isolate->SetCaptureStackTraceForUncaughtExceptions(
1110      true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
1111
1112  Environment* env = nullptr;
1113  std::unique_ptr<NodeMainInstance> main_instance =
1114      NodeMainInstance::Create(isolate,
1115                               uv_default_loop(),
1116                               per_process::v8_platform.Platform(),
1117                               args,
1118                               exec_args);
1119
1120  // The cleanups should be done in case of an early exit due to errors.
1121  auto cleanup = OnScopeLeave([&]() {
1122    // Must be done while the snapshot creator isolate is entered i.e. the
1123    // creator is still alive. The snapshot creator destructor will destroy
1124    // the isolate.
1125    if (env != nullptr) {
1126      FreeEnvironment(env);
1127    }
1128    main_instance->Dispose();
1129    per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
1130  });
1131
1132  // It's only possible to be kDefault in node_mksnapshot.
1133  SnapshotMetadata::Type snapshot_type =
1134      per_process::cli_options->build_snapshot
1135          ? SnapshotMetadata::Type::kFullyCustomized
1136          : SnapshotMetadata::Type::kDefault;
1137
1138  {
1139    HandleScope scope(isolate);
1140    TryCatch bootstrapCatch(isolate);
1141
1142    auto print_Exception = OnScopeLeave([&]() {
1143      if (bootstrapCatch.HasCaught()) {
1144        PrintCaughtException(
1145            isolate, isolate->GetCurrentContext(), bootstrapCatch);
1146      }
1147    });
1148
1149    // The default context with only things created by V8.
1150    Local<Context> default_context = Context::New(isolate);
1151
1152    // The context used by the vm module.
1153    Local<Context> vm_context;
1154    {
1155      Local<ObjectTemplate> global_template =
1156          main_instance->isolate_data()->contextify_global_template();
1157      CHECK(!global_template.IsEmpty());
1158      if (!contextify::ContextifyContext::CreateV8Context(
1159               isolate, global_template, nullptr, nullptr)
1160               .ToLocal(&vm_context)) {
1161        return SNAPSHOT_ERROR;
1162      }
1163    }
1164
1165    // The Node.js-specific context with primodials, can be used by workers
1166    // TODO(joyeecheung): investigate if this can be used by vm contexts
1167    // without breaking compatibility.
1168    Local<Context> base_context = NewContext(isolate);
1169    if (base_context.IsEmpty()) {
1170      return BOOTSTRAP_ERROR;
1171    }
1172    ResetContextSettingsBeforeSnapshot(base_context);
1173
1174    Local<Context> main_context = NewContext(isolate);
1175    if (main_context.IsEmpty()) {
1176      return BOOTSTRAP_ERROR;
1177    }
1178    // Initialize the main instance context.
1179    {
1180      Context::Scope context_scope(main_context);
1181
1182      // Create the environment.
1183      // It's not guaranteed that a context that goes through
1184      // v8_inspector::V8Inspector::contextCreated() is runtime-independent,
1185      // so do not start the inspector on the main context when building
1186      // the default snapshot.
1187      uint64_t env_flags = EnvironmentFlags::kDefaultFlags |
1188                           EnvironmentFlags::kNoCreateInspector;
1189
1190      env = CreateEnvironment(main_instance->isolate_data(),
1191                              main_context,
1192                              args,
1193                              exec_args,
1194                              static_cast<EnvironmentFlags::Flags>(env_flags));
1195
1196      // This already ran scripts in lib/internal/bootstrap/, if it fails return
1197      if (env == nullptr) {
1198        return BOOTSTRAP_ERROR;
1199      }
1200      // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
1201      // loaded via LoadEnvironment() to execute process.argv[1] as the entry
1202      // point (we currently only support this kind of entry point, but we
1203      // could also explore snapshotting other kinds of execution modes
1204      // in the future).
1205      if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) {
1206#if HAVE_INSPECTOR
1207        // TODO(joyeecheung): move this before RunBootstrapping().
1208        env->InitializeInspector({});
1209#endif
1210        if (LoadEnvironment(env, StartExecutionCallback{}).IsEmpty()) {
1211          return UNCAUGHT_EXCEPTION_ERROR;
1212        }
1213        // FIXME(joyeecheung): right now running the loop in the snapshot
1214        // builder seems to introduces inconsistencies in JS land that need to
1215        // be synchronized again after snapshot restoration.
1216        int exit_code = SpinEventLoop(env).FromMaybe(UNCAUGHT_EXCEPTION_ERROR);
1217        if (exit_code != 0) {
1218          return exit_code;
1219        }
1220      }
1221
1222      if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1223        env->ForEachRealm([](Realm* realm) { realm->PrintInfoForSnapshot(); });
1224        printf("Environment = %p\n", env);
1225      }
1226
1227      // Serialize the native states
1228      out->isolate_data_info =
1229          main_instance->isolate_data()->Serialize(&creator);
1230      out->env_info = env->Serialize(&creator);
1231
1232#ifdef NODE_USE_NODE_CODE_CACHE
1233      // Regenerate all the code cache.
1234      if (!env->builtin_loader()->CompileAllBuiltins(main_context)) {
1235        return UNCAUGHT_EXCEPTION_ERROR;
1236      }
1237      env->builtin_loader()->CopyCodeCache(&(out->code_cache));
1238      for (const auto& item : out->code_cache) {
1239        std::string size_str = FormatSize(item.data.size());
1240        per_process::Debug(DebugCategory::MKSNAPSHOT,
1241                           "Generated code cache for %d: %s\n",
1242                           item.id.c_str(),
1243                           size_str.c_str());
1244      }
1245#endif
1246
1247      ResetContextSettingsBeforeSnapshot(main_context);
1248    }
1249
1250    // Global handles to the contexts can't be disposed before the
1251    // blob is created. So initialize all the contexts before adding them.
1252    // TODO(joyeecheung): figure out how to remove this restriction.
1253    creator.SetDefaultContext(default_context);
1254    size_t index = creator.AddContext(vm_context);
1255    CHECK_EQ(index, SnapshotData::kNodeVMContextIndex);
1256    index = creator.AddContext(base_context);
1257    CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex);
1258    index = creator.AddContext(main_context,
1259                               {SerializeNodeContextInternalFields, env});
1260    CHECK_EQ(index, SnapshotData::kNodeMainContextIndex);
1261  }
1262
1263  // Must be out of HandleScope
1264  out->v8_snapshot_blob_data =
1265      creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kKeep);
1266
1267  // We must be able to rehash the blob when we restore it or otherwise
1268  // the hash seed would be fixed by V8, introducing a vulnerability.
1269  if (!out->v8_snapshot_blob_data.CanBeRehashed()) {
1270    return SNAPSHOT_ERROR;
1271  }
1272
1273  out->metadata = SnapshotMetadata{snapshot_type,
1274                                   per_process::metadata.versions.node,
1275                                   per_process::metadata.arch,
1276                                   per_process::metadata.platform,
1277                                   v8::ScriptCompiler::CachedDataVersionTag()};
1278
1279  // We cannot resurrect the handles from the snapshot, so make sure that
1280  // no handles are left open in the environment after the blob is created
1281  // (which should trigger a GC and close all handles that can be closed).
1282  bool queues_are_empty =
1283      env->req_wrap_queue()->IsEmpty() && env->handle_wrap_queue()->IsEmpty();
1284  if (!queues_are_empty ||
1285      per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1286    PrintLibuvHandleInformation(env->event_loop(), stderr);
1287  }
1288  if (!queues_are_empty) {
1289    return SNAPSHOT_ERROR;
1290  }
1291  return 0;
1292}
1293
1294int SnapshotBuilder::Generate(std::ostream& out,
1295                              const std::vector<std::string> args,
1296                              const std::vector<std::string> exec_args) {
1297  SnapshotData data;
1298  int exit_code = Generate(&data, args, exec_args);
1299  if (exit_code != 0) {
1300    return exit_code;
1301  }
1302  FormatBlob(out, &data);
1303  return exit_code;
1304}
1305
1306SnapshotableObject::SnapshotableObject(Realm* realm,
1307                                       Local<Object> wrap,
1308                                       EmbedderObjectType type)
1309    : BaseObject(realm, wrap), type_(type) {}
1310
1311std::string SnapshotableObject::GetTypeName() const {
1312  switch (type_) {
1313#define V(PropertyName, NativeTypeName)                                        \
1314  case EmbedderObjectType::k_##PropertyName: {                                 \
1315    return #NativeTypeName;                                                    \
1316  }
1317    SERIALIZABLE_OBJECT_TYPES(V)
1318#undef V
1319    default: { UNREACHABLE(); }
1320  }
1321}
1322
1323void DeserializeNodeInternalFields(Local<Object> holder,
1324                                   int index,
1325                                   StartupData payload,
1326                                   void* env) {
1327  if (payload.raw_size == 0) {
1328    holder->SetAlignedPointerInInternalField(index, nullptr);
1329    return;
1330  }
1331  per_process::Debug(DebugCategory::MKSNAPSHOT,
1332                     "Deserialize internal field %d of %p, size=%d\n",
1333                     static_cast<int>(index),
1334                     (*holder),
1335                     static_cast<int>(payload.raw_size));
1336
1337  if (payload.raw_size == 0) {
1338    holder->SetAlignedPointerInInternalField(index, nullptr);
1339    return;
1340  }
1341
1342  DCHECK_EQ(index, BaseObject::kEmbedderType);
1343
1344  Environment* env_ptr = static_cast<Environment*>(env);
1345  const InternalFieldInfoBase* info =
1346      reinterpret_cast<const InternalFieldInfoBase*>(payload.data);
1347  // TODO(joyeecheung): we can add a constant kNodeEmbedderId to the
1348  // beginning of every InternalFieldInfoBase to ensure that we don't
1349  // step on payloads that were not serialized by Node.js.
1350  switch (info->type) {
1351#define V(PropertyName, NativeTypeName)                                        \
1352  case EmbedderObjectType::k_##PropertyName: {                                 \
1353    per_process::Debug(DebugCategory::MKSNAPSHOT,                              \
1354                       "Object %p is %s\n",                                    \
1355                       (*holder),                                              \
1356                       #NativeTypeName);                                       \
1357    env_ptr->EnqueueDeserializeRequest(                                        \
1358        NativeTypeName::Deserialize,                                           \
1359        holder,                                                                \
1360        index,                                                                 \
1361        info->Copy<NativeTypeName::InternalFieldInfo>());                      \
1362    break;                                                                     \
1363  }
1364    SERIALIZABLE_OBJECT_TYPES(V)
1365#undef V
1366    default: {
1367      // This should only be reachable during development when trying to
1368      // deserialize a snapshot blob built by a version of Node.js that
1369      // has more recognizable EmbedderObjectTypes than the deserializing
1370      // Node.js binary.
1371      fprintf(stderr,
1372              "Unknown embedder object type %" PRIu8 ", possibly caused by "
1373              "mismatched Node.js versions\n",
1374              static_cast<uint8_t>(info->type));
1375      ABORT();
1376    }
1377  }
1378}
1379
1380StartupData SerializeNodeContextInternalFields(Local<Object> holder,
1381                                               int index,
1382                                               void* env) {
1383  // We only do one serialization for the kEmbedderType slot, the result
1384  // contains everything necessary for deserializing the entire object,
1385  // including the fields whose index is bigger than kEmbedderType
1386  // (most importantly, BaseObject::kSlot).
1387  // For Node.js this design is enough for all the native binding that are
1388  // serializable.
1389  if (index != BaseObject::kEmbedderType) {
1390    return StartupData{nullptr, 0};
1391  }
1392
1393  void* type_ptr = holder->GetAlignedPointerFromInternalField(index);
1394  if (type_ptr == nullptr) {
1395    return StartupData{nullptr, 0};
1396  }
1397
1398  uint16_t type = *(static_cast<uint16_t*>(type_ptr));
1399  per_process::Debug(DebugCategory::MKSNAPSHOT, "type = 0x%x\n", type);
1400  if (type != kNodeEmbedderId) {
1401    return StartupData{nullptr, 0};
1402  }
1403
1404  per_process::Debug(DebugCategory::MKSNAPSHOT,
1405                     "Serialize internal field, index=%d, holder=%p\n",
1406                     static_cast<int>(index),
1407                     *holder);
1408
1409  void* native_ptr =
1410      holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
1411  per_process::Debug(DebugCategory::MKSNAPSHOT, "native = %p\n", native_ptr);
1412  DCHECK(static_cast<BaseObject*>(native_ptr)->is_snapshotable());
1413  SnapshotableObject* obj = static_cast<SnapshotableObject*>(native_ptr);
1414
1415  per_process::Debug(DebugCategory::MKSNAPSHOT,
1416                     "Object %p is %s, ",
1417                     *holder,
1418                     obj->GetTypeName());
1419  InternalFieldInfoBase* info = obj->Serialize(index);
1420
1421  per_process::Debug(DebugCategory::MKSNAPSHOT,
1422                     "payload size=%d\n",
1423                     static_cast<int>(info->length));
1424  return StartupData{reinterpret_cast<const char*>(info),
1425                     static_cast<int>(info->length)};
1426}
1427
1428void SerializeSnapshotableObjects(Realm* realm,
1429                                  SnapshotCreator* creator,
1430                                  RealmSerializeInfo* info) {
1431  HandleScope scope(realm->isolate());
1432  Local<Context> context = realm->context();
1433  uint32_t i = 0;
1434  realm->ForEachBaseObject([&](BaseObject* obj) {
1435    // If there are any BaseObjects that are not snapshotable left
1436    // during context serialization, V8 would crash due to unregistered
1437    // global handles and print detailed information about them.
1438    if (!obj->is_snapshotable()) {
1439      return;
1440    }
1441    SnapshotableObject* ptr = static_cast<SnapshotableObject*>(obj);
1442
1443    std::string type_name = ptr->GetTypeName();
1444    per_process::Debug(DebugCategory::MKSNAPSHOT,
1445                       "Serialize snapshotable object %i (%p), "
1446                       "object=%p, type=%s\n",
1447                       static_cast<int>(i),
1448                       ptr,
1449                       *(ptr->object()),
1450                       type_name);
1451
1452    if (ptr->PrepareForSerialization(context, creator)) {
1453      SnapshotIndex index = creator->AddData(context, obj->object());
1454      per_process::Debug(DebugCategory::MKSNAPSHOT,
1455                         "Serialized with index=%d\n",
1456                         static_cast<int>(index));
1457      info->native_objects.push_back({type_name, i, index});
1458    }
1459    i++;
1460  });
1461}
1462
1463namespace mksnapshot {
1464
1465void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
1466  CHECK(args[0]->IsString());
1467  Local<String> filename = args[0].As<String>();
1468  Local<String> source = args[1].As<String>();
1469  Isolate* isolate = args.GetIsolate();
1470  Local<Context> context = isolate->GetCurrentContext();
1471  ScriptOrigin origin(isolate, filename, 0, 0, true);
1472  // TODO(joyeecheung): do we need all of these? Maybe we would want a less
1473  // internal version of them.
1474  std::vector<Local<String>> parameters = {
1475      FIXED_ONE_BYTE_STRING(isolate, "require"),
1476      FIXED_ONE_BYTE_STRING(isolate, "__filename"),
1477      FIXED_ONE_BYTE_STRING(isolate, "__dirname"),
1478  };
1479  ScriptCompiler::Source script_source(source, origin);
1480  Local<Function> fn;
1481  if (ScriptCompiler::CompileFunction(context,
1482                                      &script_source,
1483                                      parameters.size(),
1484                                      parameters.data(),
1485                                      0,
1486                                      nullptr,
1487                                      ScriptCompiler::kNoCompileOptions)
1488          .ToLocal(&fn)) {
1489    args.GetReturnValue().Set(fn);
1490  }
1491}
1492
1493void SetSerializeCallback(const FunctionCallbackInfo<Value>& args) {
1494  Environment* env = Environment::GetCurrent(args);
1495  CHECK(env->snapshot_serialize_callback().IsEmpty());
1496  CHECK(args[0]->IsFunction());
1497  env->set_snapshot_serialize_callback(args[0].As<Function>());
1498}
1499
1500void SetDeserializeCallback(const FunctionCallbackInfo<Value>& args) {
1501  Environment* env = Environment::GetCurrent(args);
1502  CHECK(env->snapshot_deserialize_callback().IsEmpty());
1503  CHECK(args[0]->IsFunction());
1504  env->set_snapshot_deserialize_callback(args[0].As<Function>());
1505}
1506
1507void SetDeserializeMainFunction(const FunctionCallbackInfo<Value>& args) {
1508  Environment* env = Environment::GetCurrent(args);
1509  CHECK(env->snapshot_deserialize_main().IsEmpty());
1510  CHECK(args[0]->IsFunction());
1511  env->set_snapshot_deserialize_main(args[0].As<Function>());
1512}
1513
1514void Initialize(Local<Object> target,
1515                Local<Value> unused,
1516                Local<Context> context,
1517                void* priv) {
1518  SetMethod(context, target, "compileSerializeMain", CompileSerializeMain);
1519  SetMethod(context, target, "setSerializeCallback", SetSerializeCallback);
1520  SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback);
1521  SetMethod(context,
1522            target,
1523            "setDeserializeMainFunction",
1524            SetDeserializeMainFunction);
1525}
1526
1527void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1528  registry->Register(CompileSerializeMain);
1529  registry->Register(SetSerializeCallback);
1530  registry->Register(SetDeserializeCallback);
1531  registry->Register(SetDeserializeMainFunction);
1532}
1533}  // namespace mksnapshot
1534}  // namespace node
1535
1536NODE_BINDING_CONTEXT_AWARE_INTERNAL(mksnapshot, node::mksnapshot::Initialize)
1537NODE_BINDING_EXTERNAL_REFERENCE(mksnapshot,
1538                                node::mksnapshot::RegisterExternalReferences)
1539