GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 532 668 79.6 %
Date: 2022-08-29 04:21:03 Branches: 135 236 57.2 %

Line Branch Exec Source
1
2
#include "node_snapshotable.h"
3
#include <iostream>
4
#include <sstream>
5
#include "base_object-inl.h"
6
#include "debug_utils-inl.h"
7
#include "env-inl.h"
8
#include "node_blob.h"
9
#include "node_builtins.h"
10
#include "node_contextify.h"
11
#include "node_errors.h"
12
#include "node_external_reference.h"
13
#include "node_file.h"
14
#include "node_internals.h"
15
#include "node_main_instance.h"
16
#include "node_metadata.h"
17
#include "node_process.h"
18
#include "node_snapshot_builder.h"
19
#include "node_util.h"
20
#include "node_v8.h"
21
#include "node_v8_platform-inl.h"
22
23
#if HAVE_INSPECTOR
24
#include "inspector/worker_inspector.h"  // ParentInspectorHandle
25
#endif
26
27
namespace node {
28
29
using v8::Context;
30
using v8::Function;
31
using v8::FunctionCallbackInfo;
32
using v8::HandleScope;
33
using v8::Isolate;
34
using v8::Local;
35
using v8::Object;
36
using v8::ObjectTemplate;
37
using v8::ScriptCompiler;
38
using v8::ScriptOrigin;
39
using v8::SnapshotCreator;
40
using v8::StartupData;
41
using v8::String;
42
using v8::TryCatch;
43
using v8::Value;
44
45
const uint32_t SnapshotData::kMagic;
46
47
std::ostream& operator<<(std::ostream& output,
48
                         const builtins::CodeCacheInfo& info) {
49
  output << "<builtins::CodeCacheInfo id=" << info.id
50
         << ", size=" << info.data.size() << ">\n";
51
  return output;
52
}
53
54
std::ostream& operator<<(std::ostream& output,
55
                         const std::vector<builtins::CodeCacheInfo>& vec) {
56
  output << "{\n";
57
  for (const auto& info : vec) {
58
    output << info;
59
  }
60
  output << "}\n";
61
  return output;
62
}
63
64
std::ostream& operator<<(std::ostream& output,
65
                         const std::vector<uint8_t>& vec) {
66
  output << "{\n";
67
  for (const auto& i : vec) {
68
    output << i << ",";
69
  }
70
  output << "}";
71
  return output;
72
}
73
74
class FileIO {
75
 public:
76
4
  explicit FileIO(FILE* file)
77
4
      : f(file),
78
4
        is_debug(per_process::enabled_debug_list.enabled(
79
4
            DebugCategory::MKSNAPSHOT)) {}
80
81
  template <typename... Args>
82
4064
  void Debug(const char* format, Args&&... args) const {
83
2628
    per_process::Debug(
84
        DebugCategory::MKSNAPSHOT, format, std::forward<Args>(args)...);
85
  }
86
87
  template <typename T>
88
  std::string ToStr(const T& arg) const {
89
    std::stringstream ss;
90
    ss << arg;
91
    return ss.str();
92
  }
93
94
  template <typename T>
95
  std::string GetName() const {
96
#define TYPE_LIST(V)                                                           \
97
  V(builtins::CodeCacheInfo)                                                   \
98
  V(PropInfo)                                                                  \
99
  V(std::string)
100
101
#define V(TypeName)                                                            \
102
  if (std::is_same_v<T, TypeName>) {                                           \
103
    return #TypeName;                                                          \
104
  }
105
    TYPE_LIST(V)
106
#undef V
107
108
    std::string name;
109
    if (std::is_arithmetic_v<T>) {
110
      if (!std::is_signed_v<T>) {
111
        name += "u";
112
      }
113
      name += std::is_integral_v<T> ? "int" : "float";
114
      name += std::to_string(sizeof(T) * 8);
115
      name += "_t";
116
    }
117
    return name;
118
  }
119
120
  FILE* f = nullptr;
121
  bool is_debug = false;
122
};
123
124
class FileReader : public FileIO {
125
 public:
126
2
  explicit FileReader(FILE* file) : FileIO(file) {}
127
2
  ~FileReader() {}
128
129
  // Helper for reading numeric types.
130
  template <typename T>
131
3584
  T Read() {
132
    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
133
    T result;
134
3584
    Read(&result, 1);
135
3584
    return result;
136
  }
137
138
  // Layout of vectors:
139
  // [ 4/8 bytes ] count
140
  // [   ...     ] contents (count * size of individual elements)
141
  template <typename T>
142
1192
  std::vector<T> ReadVector() {
143
1192
    if (is_debug) {
144
      std::string name = GetName<T>();
145
      Debug("\nReadVector<%s>()(%d-byte)\n", name.c_str(), sizeof(T));
146
    }
147
1192
    size_t count = static_cast<size_t>(Read<size_t>());
148
1192
    if (count == 0) {
149
4
      return std::vector<T>();
150
    }
151
1188
    if (is_debug) {
152
      Debug("Reading %d vector elements...\n", count);
153
    }
154
2376
    std::vector<T> result = ReadVector<T>(count, std::is_arithmetic<T>{});
155
1188
    if (is_debug) {
156
      std::string str = std::is_arithmetic_v<T> ? "" : ToStr(result);
157
      std::string name = GetName<T>();
158
      Debug("ReadVector<%s>() read %s\n", name.c_str(), str.c_str());
159
    }
160
1188
    return result;
161
  }
162
163
934
  std::string ReadString() {
164
934
    size_t length = Read<size_t>();
165
166
934
    if (is_debug) {
167
      Debug("ReadString(), length=%d: ", length);
168
    }
169
170
934
    CHECK_GT(length, 0);  // There should be no empty strings.
171
1868
    MallocedBuffer<char> buf(length + 1);
172
934
    size_t r = fread(buf.data, 1, length + 1, f);
173
934
    CHECK_EQ(r, length + 1);
174
934
    std::string result(buf.data, length);  // This creates a copy of buf.data.
175
176
934
    if (is_debug) {
177
      Debug("\"%s\", read %d bytes\n", result.c_str(), r);
178
    }
179
180
934
    read_total += r;
181
934
    return result;
182
  }
183
184
  size_t read_total = 0;
185
186
 private:
187
  // Helper for reading an array of numeric types.
188
  template <typename T>
189
4756
  void Read(T* out, size_t count) {
190
    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
191
    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
192
4756
    if (is_debug) {
193
      std::string name = GetName<T>();
194
      Debug("Read<%s>()(%d-byte), count=%d: ", name.c_str(), sizeof(T), count);
195
    }
196
197
4756
    size_t r = fread(out, sizeof(T), count, f);
198
4756
    CHECK_EQ(r, count);
199
200
4756
    if (is_debug) {
201
      std::string str =
202
          "{ " + std::to_string(out[0]) + (count > 1 ? ", ... }" : " }");
203
      Debug("%s, read %d bytes\n", str.c_str(), r);
204
    }
205
4756
    read_total += r;
206
4756
  }
207
208
  // Helper for reading numeric vectors.
209
  template <typename Number>
210
1168
  std::vector<Number> ReadVector(size_t count, std::true_type) {
211
    static_assert(std::is_arithmetic_v<Number>, "Not an arithmetic type");
212
    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
213
1168
    std::vector<Number> result(count);
214
1168
    Read(result.data(), count);
215
1168
    return result;
216
  }
217
218
  // Helper for reading non-numeric vectors.
219
  template <typename T>
220
20
  std::vector<T> ReadVector(size_t count, std::false_type) {
221
    static_assert(!std::is_arithmetic_v<T>, "Arithmetic type");
222
    DCHECK_GT(count, 0);  // Should not read contents for vectors of size 0.
223
20
    std::vector<T> result;
224
20
    result.reserve(count);
225
20
    bool original_is_debug = is_debug;
226
20
    is_debug = original_is_debug && !std::is_same_v<T, std::string>;
227
1876
    for (size_t i = 0; i < count; ++i) {
228
1856
      if (is_debug) {
229
        Debug("\n[%d] ", i);
230
      }
231
1856
      result.push_back(Read<T>());
232
    }
233
20
    is_debug = original_is_debug;
234
235
20
    return result;
236
  }
237
};
238
239
class FileWriter : public FileIO {
240
 public:
241
2
  explicit FileWriter(FILE* file) : FileIO(file) {}
242
2
  ~FileWriter() {}
243
244
  // Helper for writing numeric types.
245
  template <typename T>
246
3584
  size_t Write(const T& data) {
247
    static_assert(std::is_arithmetic_v<T>, "Not an arithmetic type");
248
3584
    return Write(&data, 1);
249
  }
250
251
  // Layout of vectors:
252
  // [ 4/8 bytes ] count
253
  // [   ...     ] contents (count * size of individual elements)
254
  template <typename T>
255
1192
  size_t WriteVector(const std::vector<T>& data) {
256
1192
    if (is_debug) {
257
      std::string str = std::is_arithmetic_v<T> ? "" : ToStr(data);
258
      std::string name = GetName<T>();
259
      Debug("\nWriteVector<%s>() (%d-byte), count=%d: %s\n",
260
            name.c_str(),
261
            sizeof(T),
262
            data.size(),
263
            str.c_str());
264
    }
265
266
1192
    size_t written_total = Write<size_t>(data.size());
267
1192
    if (data.size() == 0) {
268
4
      return written_total;
269
    }
270
1188
    written_total += WriteVector<T>(data, std::is_arithmetic<T>{});
271
272
1188
    if (is_debug) {
273
      std::string name = GetName<T>();
274
      Debug("WriteVector<%s>() wrote %d bytes\n", name.c_str(), written_total);
275
    }
276
277
1188
    return written_total;
278
  }
279
280
  // The layout of a written string:
281
  // [  4/8 bytes     ] length
282
  // [ |length| bytes ] contents
283
934
  size_t WriteString(const std::string& data) {
284
934
    CHECK_GT(data.size(), 0);  // No empty strings should be written.
285
934
    size_t written_total = Write<size_t>(data.size());
286
934
    if (is_debug) {
287
      std::string str = ToStr(data);
288
      Debug("WriteString(), length=%d: \"%s\"\n", data.size(), data.c_str());
289
    }
290
291
934
    size_t r = fwrite(data.c_str(), 1, data.size() + 1, f);
292
934
    CHECK_EQ(r, data.size() + 1);
293
934
    written_total += r;
294
295
934
    if (is_debug) {
296
      Debug("WriteString() wrote %d bytes\n", written_total);
297
    }
298
299
934
    return written_total;
300
  }
301
302
 private:
303
  // Helper for writing an array of numeric types.
304
  template <typename T>
305
4756
  size_t Write(const T* data, size_t count) {
306
    DCHECK_GT(count, 0);  // Should not write contents for vectors of size 0.
307
4756
    if (is_debug) {
308
      std::string str =
309
          "{ " + std::to_string(data[0]) + (count > 1 ? ", ... }" : " }");
310
      std::string name = GetName<T>();
311
      Debug("Write<%s>() (%d-byte), count=%d: %s",
312
            name.c_str(),
313
            sizeof(T),
314
            count,
315
            str.c_str());
316
    }
317
318
4756
    size_t r = fwrite(data, sizeof(T), count, f);
319
4756
    CHECK_EQ(r, count);
320
321
4756
    if (is_debug) {
322
      Debug(", wrote %d bytes\n", r);
323
    }
324
4756
    return r;
325
  }
326
327
  // Helper for writing numeric vectors.
328
  template <typename Number>
329
1168
  size_t WriteVector(const std::vector<Number>& data, std::true_type) {
330
1168
    return Write(data.data(), data.size());
331
  }
332
333
  // Helper for writing non-numeric vectors.
334
  template <typename T>
335
20
  size_t WriteVector(const std::vector<T>& data, std::false_type) {
336
    DCHECK_GT(data.size(),
337
              0);  // Should not write contents for vectors of size 0.
338
20
    size_t written_total = 0;
339
20
    bool original_is_debug = is_debug;
340
20
    is_debug = original_is_debug && !std::is_same_v<T, std::string>;
341
1876
    for (size_t i = 0; i < data.size(); ++i) {
342
1856
      if (is_debug) {
343
        Debug("\n[%d] ", i);
344
      }
345
1856
      written_total += Write<T>(data[i]);
346
    }
347
20
    is_debug = original_is_debug;
348
349
20
    return written_total;
350
  }
351
};
352
353
// Layout of serialized std::string:
354
// [  4/8 bytes     ] length
355
// [ |length| bytes ] contents
356
template <>
357
232
std::string FileReader::Read() {
358
232
  return ReadString();
359
}
360
template <>
361
232
size_t FileWriter::Write(const std::string& data) {
362
232
  return WriteString(data);
363
}
364
365
// Layout of v8::StartupData
366
// [  4/8 bytes       ] raw_size
367
// [ |raw_size| bytes ] contents
368
template <>
369
2
v8::StartupData FileReader::Read() {
370
2
  Debug("Read<v8::StartupData>()\n");
371
372
2
  int raw_size = Read<int>();
373
2
  Debug("size=%d\n", raw_size);
374
375
2
  CHECK_GT(raw_size, 0);  // There should be no startup data of size 0.
376
  // The data pointer of v8::StartupData would be deleted so it must be new'ed.
377
2
  std::unique_ptr<char> buf = std::unique_ptr<char>(new char[raw_size]);
378
2
  Read<char>(buf.get(), raw_size);
379
380
2
  return v8::StartupData{buf.release(), raw_size};
381
}
382
383
template <>
384
2
size_t FileWriter::Write(const v8::StartupData& data) {
385
2
  Debug("\nWrite<v8::StartupData>() size=%d\n", data.raw_size);
386
387
2
  CHECK_GT(data.raw_size, 0);  // There should be no startup data of size 0.
388
2
  size_t written_total = Write<int>(data.raw_size);
389
2
  written_total += Write<char>(data.data, static_cast<size_t>(data.raw_size));
390
391
2
  Debug("Write<v8::StartupData>() wrote %d bytes\n\n", written_total);
392
2
  return written_total;
393
}
394
395
// Layout of builtins::CodeCacheInfo
396
// [  4/8 bytes ]  length of the module id string
397
// [    ...     ]  |length| bytes of module id
398
// [  4/8 bytes ]  length of module code cache
399
// [    ...     ]  |length| bytes of module code cache
400
template <>
401
582
builtins::CodeCacheInfo FileReader::Read() {
402
582
  Debug("Read<builtins::CodeCacheInfo>()\n");
403
404
582
  builtins::CodeCacheInfo result{ReadString(), ReadVector<uint8_t>()};
405
406
582
  if (is_debug) {
407
    std::string str = ToStr(result);
408
    Debug("Read<builtins::CodeCacheInfo>() %s\n", str.c_str());
409
  }
410
582
  return result;
411
}
412
413
template <>
414
582
size_t FileWriter::Write(const builtins::CodeCacheInfo& data) {
415
1164
  Debug("\nWrite<builtins::CodeCacheInfo>() id = %s"
416
        ", size=%d\n",
417
582
        data.id.c_str(),
418
582
        data.data.size());
419
420
582
  size_t written_total = WriteString(data.id);
421
582
  written_total += WriteVector<uint8_t>(data.data);
422
423
582
  Debug("Write<builtins::CodeCacheInfo>() wrote %d bytes\n", written_total);
424
582
  return written_total;
425
}
426
427
// Layout of PropInfo
428
// [ 4/8 bytes ]  length of the data name string
429
// [    ...    ]  |length| bytes of data name
430
// [  4 bytes  ]  index in the PropInfo vector
431
// [ 4/8 bytes ]  index in the snapshot blob, can be used with
432
//                GetDataFromSnapshotOnce().
433
template <>
434
114
PropInfo FileReader::Read() {
435
114
  Debug("Read<PropInfo>()\n");
436
437
114
  PropInfo result;
438
114
  result.name = ReadString();
439
114
  result.id = Read<uint32_t>();
440
114
  result.index = Read<SnapshotIndex>();
441
442
114
  if (is_debug) {
443
    std::string str = ToStr(result);
444
    Debug("Read<PropInfo>() %s\n", str.c_str());
445
  }
446
447
114
  return result;
448
}
449
450
template <>
451
114
size_t FileWriter::Write(const PropInfo& data) {
452
114
  if (is_debug) {
453
    std::string str = ToStr(data);
454
    Debug("Write<PropInfo>() %s\n", str.c_str());
455
  }
456
457
114
  size_t written_total = WriteString(data.name);
458
114
  written_total += Write<uint32_t>(data.id);
459
114
  written_total += Write<SnapshotIndex>(data.index);
460
461
114
  Debug("Write<PropInfo>() wrote %d bytes\n", written_total);
462
114
  return written_total;
463
}
464
465
// Layout of AsyncHooks::SerializeInfo
466
// [ 4/8 bytes ]  snapshot index of async_ids_stack
467
// [ 4/8 bytes ]  snapshot index of fields
468
// [ 4/8 bytes ]  snapshot index of async_id_fields
469
// [ 4/8 bytes ]  snapshot index of js_execution_async_resources
470
// [ 4/8 bytes ]  length of native_execution_async_resources
471
// [   ...     ]  snapshot indices of each element in
472
//                native_execution_async_resources
473
template <>
474
2
AsyncHooks::SerializeInfo FileReader::Read() {
475
2
  Debug("Read<AsyncHooks::SerializeInfo>()\n");
476
477
2
  AsyncHooks::SerializeInfo result;
478
2
  result.async_ids_stack = Read<AliasedBufferIndex>();
479
2
  result.fields = Read<AliasedBufferIndex>();
480
2
  result.async_id_fields = Read<AliasedBufferIndex>();
481
2
  result.js_execution_async_resources = Read<SnapshotIndex>();
482
2
  result.native_execution_async_resources = ReadVector<SnapshotIndex>();
483
484
2
  if (is_debug) {
485
    std::string str = ToStr(result);
486
    Debug("Read<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
487
  }
488
489
2
  return result;
490
}
491
template <>
492
2
size_t FileWriter::Write(const AsyncHooks::SerializeInfo& data) {
493
2
  if (is_debug) {
494
    std::string str = ToStr(data);
495
    Debug("Write<AsyncHooks::SerializeInfo>() %s\n", str.c_str());
496
  }
497
498
2
  size_t written_total = Write<AliasedBufferIndex>(data.async_ids_stack);
499
2
  written_total += Write<AliasedBufferIndex>(data.fields);
500
2
  written_total += Write<AliasedBufferIndex>(data.async_id_fields);
501
2
  written_total += Write<SnapshotIndex>(data.js_execution_async_resources);
502
2
  written_total +=
503
2
      WriteVector<SnapshotIndex>(data.native_execution_async_resources);
504
505
2
  Debug("Write<AsyncHooks::SerializeInfo>() wrote %d bytes\n", written_total);
506
2
  return written_total;
507
}
508
509
// Layout of TickInfo::SerializeInfo
510
// [ 4/8 bytes ]  snapshot index of fields
511
template <>
512
2
TickInfo::SerializeInfo FileReader::Read() {
513
2
  Debug("Read<TickInfo::SerializeInfo>()\n");
514
515
  TickInfo::SerializeInfo result;
516
2
  result.fields = Read<AliasedBufferIndex>();
517
518
2
  if (is_debug) {
519
    std::string str = ToStr(result);
520
    Debug("Read<TickInfo::SerializeInfo>() %s\n", str.c_str());
521
  }
522
523
2
  return result;
524
}
525
526
template <>
527
2
size_t FileWriter::Write(const TickInfo::SerializeInfo& data) {
528
2
  if (is_debug) {
529
    std::string str = ToStr(data);
530
    Debug("Write<TickInfo::SerializeInfo>() %s\n", str.c_str());
531
  }
532
533
2
  size_t written_total = Write<AliasedBufferIndex>(data.fields);
534
535
2
  Debug("Write<TickInfo::SerializeInfo>() wrote %d bytes\n", written_total);
536
2
  return written_total;
537
}
538
539
// Layout of TickInfo::SerializeInfo
540
// [ 4/8 bytes ]  snapshot index of fields
541
template <>
542
2
ImmediateInfo::SerializeInfo FileReader::Read() {
543
  per_process::Debug(DebugCategory::MKSNAPSHOT,
544
                     "Read<ImmediateInfo::SerializeInfo>()\n");
545
546
  ImmediateInfo::SerializeInfo result;
547
2
  result.fields = Read<AliasedBufferIndex>();
548
2
  if (is_debug) {
549
    std::string str = ToStr(result);
550
    Debug("Read<ImmediateInfo::SerializeInfo>() %s\n", str.c_str());
551
  }
552
2
  return result;
553
}
554
555
template <>
556
2
size_t FileWriter::Write(const ImmediateInfo::SerializeInfo& data) {
557
2
  if (is_debug) {
558
    std::string str = ToStr(data);
559
    Debug("Write<ImmeidateInfo::SerializeInfo>() %s\n", str.c_str());
560
  }
561
562
2
  size_t written_total = Write<AliasedBufferIndex>(data.fields);
563
564
2
  Debug("Write<ImmeidateInfo::SerializeInfo>() wrote %d bytes\n",
565
        written_total);
566
2
  return written_total;
567
}
568
569
// Layout of PerformanceState::SerializeInfo
570
// [ 4/8 bytes ]  snapshot index of root
571
// [ 4/8 bytes ]  snapshot index of milestones
572
// [ 4/8 bytes ]  snapshot index of observers
573
template <>
574
2
performance::PerformanceState::SerializeInfo FileReader::Read() {
575
  per_process::Debug(DebugCategory::MKSNAPSHOT,
576
                     "Read<PerformanceState::SerializeInfo>()\n");
577
578
  performance::PerformanceState::SerializeInfo result;
579
2
  result.root = Read<AliasedBufferIndex>();
580
2
  result.milestones = Read<AliasedBufferIndex>();
581
2
  result.observers = Read<AliasedBufferIndex>();
582
2
  if (is_debug) {
583
    std::string str = ToStr(result);
584
    Debug("Read<PerformanceState::SerializeInfo>() %s\n", str.c_str());
585
  }
586
2
  return result;
587
}
588
589
template <>
590
2
size_t FileWriter::Write(
591
    const performance::PerformanceState::SerializeInfo& data) {
592
2
  if (is_debug) {
593
    std::string str = ToStr(data);
594
    Debug("Write<PerformanceState::SerializeInfo>() %s\n", str.c_str());
595
  }
596
597
2
  size_t written_total = Write<AliasedBufferIndex>(data.root);
598
2
  written_total += Write<AliasedBufferIndex>(data.milestones);
599
2
  written_total += Write<AliasedBufferIndex>(data.observers);
600
601
2
  Debug("Write<PerformanceState::SerializeInfo>() wrote %d bytes\n",
602
        written_total);
603
2
  return written_total;
604
}
605
606
// Layout of IsolateDataSerializeInfo
607
// [ 4/8 bytes ]  length of primitive_values vector
608
// [    ...    ]  |length| of primitive_values indices
609
// [ 4/8 bytes ]  length of template_values vector
610
// [    ...    ]  |length| of PropInfo data
611
template <>
612
2
IsolateDataSerializeInfo FileReader::Read() {
613
  per_process::Debug(DebugCategory::MKSNAPSHOT,
614
                     "Read<IsolateDataSerializeInfo>()\n");
615
616
2
  IsolateDataSerializeInfo result;
617
2
  result.primitive_values = ReadVector<SnapshotIndex>();
618
2
  result.template_values = ReadVector<PropInfo>();
619
2
  if (is_debug) {
620
    std::string str = ToStr(result);
621
    Debug("Read<IsolateDataSerializeInfo>() %s\n", str.c_str());
622
  }
623
2
  return result;
624
}
625
626
template <>
627
2
size_t FileWriter::Write(const IsolateDataSerializeInfo& data) {
628
2
  if (is_debug) {
629
    std::string str = ToStr(data);
630
    Debug("Write<IsolateDataSerializeInfo>() %s\n", str.c_str());
631
  }
632
633
2
  size_t written_total = WriteVector<SnapshotIndex>(data.primitive_values);
634
2
  written_total += WriteVector<PropInfo>(data.template_values);
635
636
2
  Debug("Write<IsolateDataSerializeInfo>() wrote %d bytes\n", written_total);
637
2
  return written_total;
638
}
639
640
template <>
641
2
EnvSerializeInfo FileReader::Read() {
642
  per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<EnvSerializeInfo>()\n");
643
2
  EnvSerializeInfo result;
644
2
  result.native_objects = ReadVector<PropInfo>();
645
2
  result.builtins = ReadVector<std::string>();
646
2
  result.async_hooks = Read<AsyncHooks::SerializeInfo>();
647
2
  result.tick_info = Read<TickInfo::SerializeInfo>();
648
2
  result.immediate_info = Read<ImmediateInfo::SerializeInfo>();
649
  result.performance_state =
650
2
      Read<performance::PerformanceState::SerializeInfo>();
651
2
  result.exiting = Read<AliasedBufferIndex>();
652
2
  result.stream_base_state = Read<AliasedBufferIndex>();
653
2
  result.should_abort_on_uncaught_toggle = Read<AliasedBufferIndex>();
654
2
  result.persistent_values = ReadVector<PropInfo>();
655
2
  result.context = Read<SnapshotIndex>();
656
2
  return result;
657
}
658
659
template <>
660
2
size_t FileWriter::Write(const EnvSerializeInfo& data) {
661
2
  if (is_debug) {
662
    std::string str = ToStr(data);
663
    Debug("\nWrite<EnvSerializeInfo>() %s\n", str.c_str());
664
  }
665
666
  // Use += here to ensure order of evaluation.
667
2
  size_t written_total = WriteVector<PropInfo>(data.native_objects);
668
2
  written_total += WriteVector<std::string>(data.builtins);
669
2
  written_total += Write<AsyncHooks::SerializeInfo>(data.async_hooks);
670
2
  written_total += Write<TickInfo::SerializeInfo>(data.tick_info);
671
2
  written_total += Write<ImmediateInfo::SerializeInfo>(data.immediate_info);
672
4
  written_total += Write<performance::PerformanceState::SerializeInfo>(
673
2
      data.performance_state);
674
2
  written_total += Write<AliasedBufferIndex>(data.exiting);
675
2
  written_total += Write<AliasedBufferIndex>(data.stream_base_state);
676
2
  written_total +=
677
2
      Write<AliasedBufferIndex>(data.should_abort_on_uncaught_toggle);
678
2
  written_total += WriteVector<PropInfo>(data.persistent_values);
679
2
  written_total += Write<SnapshotIndex>(data.context);
680
681
2
  Debug("Write<EnvSerializeInfo>() wrote %d bytes\n", written_total);
682
2
  return written_total;
683
}
684
685
// Layout of SnapshotMetadata
686
// [  1 byte   ]  type of the snapshot
687
// [ 4/8 bytes ]  length of the node version string
688
// [    ...    ]  |length| bytes of node version
689
// [ 4/8 bytes ]  length of the node arch string
690
// [    ...    ]  |length| bytes of node arch
691
// [ 4/8 bytes ]  length of the node platform string
692
// [    ...    ]  |length| bytes of node platform
693
// [  4 bytes  ]  v8 cache version tag
694
template <>
695
2
SnapshotMetadata FileReader::Read() {
696
  per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<SnapshotMetadata>()\n");
697
698
2
  SnapshotMetadata result;
699
2
  result.type = static_cast<SnapshotMetadata::Type>(Read<uint8_t>());
700
2
  result.node_version = ReadString();
701
2
  result.node_arch = ReadString();
702
2
  result.node_platform = ReadString();
703
2
  result.v8_cache_version_tag = Read<uint32_t>();
704
705
2
  if (is_debug) {
706
    std::string str = ToStr(result);
707
    Debug("Read<SnapshotMetadata>() %s\n", str.c_str());
708
  }
709
2
  return result;
710
}
711
712
template <>
713
2
size_t FileWriter::Write(const SnapshotMetadata& data) {
714
2
  if (is_debug) {
715
    std::string str = ToStr(data);
716
    Debug("\nWrite<SnapshotMetadata>() %s\n", str.c_str());
717
  }
718
2
  size_t written_total = 0;
719
  // We need the Node.js version, platform and arch to match because
720
  // Node.js may perform synchronizations that are platform-specific and they
721
  // can be changed in semver-patches.
722
2
  Debug("Write snapshot type %" PRIu8 "\n", static_cast<uint8_t>(data.type));
723
2
  written_total += Write<uint8_t>(static_cast<uint8_t>(data.type));
724
2
  Debug("Write Node.js version %s\n", data.node_version.c_str());
725
2
  written_total += WriteString(data.node_version);
726
2
  Debug("Write Node.js arch %s\n", data.node_arch);
727
2
  written_total += WriteString(data.node_arch);
728
2
  Debug("Write Node.js platform %s\n", data.node_platform);
729
2
  written_total += WriteString(data.node_platform);
730
4
  Debug("Write V8 cached data version tag %" PRIx32 "\n",
731
2
        data.v8_cache_version_tag);
732
2
  written_total += Write<uint32_t>(data.v8_cache_version_tag);
733
2
  return written_total;
734
}
735
736
// Layout of the snapshot blob
737
// [   4 bytes    ]  kMagic
738
// [   4/8 bytes  ]  length of Node.js version string
739
// [    ...       ]  contents of Node.js version string
740
// [   4/8 bytes  ]  length of Node.js arch string
741
// [    ...       ]  contents of Node.js arch string
742
// [    ...       ]  v8_snapshot_blob_data from SnapshotCreator::CreateBlob()
743
// [    ...       ]  isolate_data_info
744
// [    ...       ]  env_info
745
// [    ...       ]  code_cache
746
747
2
void SnapshotData::ToBlob(FILE* out) const {
748
4
  FileWriter w(out);
749
2
  w.Debug("SnapshotData::ToBlob()\n");
750
751
2
  size_t written_total = 0;
752
753
  // Metadata
754
2
  w.Debug("Write magic %" PRIx32 "\n", kMagic);
755
2
  written_total += w.Write<uint32_t>(kMagic);
756
2
  w.Debug("Write metadata\n");
757
2
  written_total += w.Write<SnapshotMetadata>(metadata);
758
759
2
  written_total += w.Write<v8::StartupData>(v8_snapshot_blob_data);
760
2
  w.Debug("Write isolate_data_indices\n");
761
2
  written_total += w.Write<IsolateDataSerializeInfo>(isolate_data_info);
762
2
  written_total += w.Write<EnvSerializeInfo>(env_info);
763
2
  w.Debug("Write code_cache\n");
764
2
  written_total += w.WriteVector<builtins::CodeCacheInfo>(code_cache);
765
2
  w.Debug("SnapshotData::ToBlob() Wrote %d bytes\n", written_total);
766
2
}
767
768
2
bool SnapshotData::FromBlob(SnapshotData* out, FILE* in) {
769
4
  FileReader r(in);
770
2
  r.Debug("SnapshotData::FromBlob()\n");
771
772
  DCHECK_EQ(out->data_ownership, SnapshotData::DataOwnership::kOwned);
773
774
  // Metadata
775
2
  uint32_t magic = r.Read<uint32_t>();
776
2
  r.Debug("Read magic %" PRIx32 "\n", magic);
777
2
  CHECK_EQ(magic, kMagic);
778
2
  out->metadata = r.Read<SnapshotMetadata>();
779
2
  r.Debug("Read metadata\n");
780
2
  if (!out->Check()) {
781
    return false;
782
  }
783
784
2
  out->v8_snapshot_blob_data = r.Read<v8::StartupData>();
785
2
  r.Debug("Read isolate_data_info\n");
786
2
  out->isolate_data_info = r.Read<IsolateDataSerializeInfo>();
787
2
  out->env_info = r.Read<EnvSerializeInfo>();
788
2
  r.Debug("Read code_cache\n");
789
2
  out->code_cache = r.ReadVector<builtins::CodeCacheInfo>();
790
791
2
  r.Debug("SnapshotData::FromBlob() read %d bytes\n", r.read_total);
792
2
  return true;
793
}
794
795
5408
bool SnapshotData::Check() const {
796
5408
  if (metadata.node_version != per_process::metadata.versions.node) {
797
    fprintf(stderr,
798
            "Failed to load the startup snapshot because it was built with"
799
            "Node.js version %s and the current Node.js version is %s.\n",
800
            metadata.node_version.c_str(),
801
            NODE_VERSION);
802
    return false;
803
  }
804
805
5408
  if (metadata.node_arch != per_process::metadata.arch) {
806
    fprintf(stderr,
807
            "Failed to load the startup snapshot because it was built with"
808
            "architecture %s and the architecture is %s.\n",
809
            metadata.node_arch.c_str(),
810
            NODE_ARCH);
811
    return false;
812
  }
813
814
5408
  if (metadata.node_platform != per_process::metadata.platform) {
815
    fprintf(stderr,
816
            "Failed to load the startup snapshot because it was built with"
817
            "platform %s and the current platform is %s.\n",
818
            metadata.node_platform.c_str(),
819
            NODE_PLATFORM);
820
    return false;
821
  }
822
823
5408
  uint32_t current_cache_version = v8::ScriptCompiler::CachedDataVersionTag();
824
5408
  if (metadata.v8_cache_version_tag != current_cache_version &&
825
129
      metadata.type == SnapshotMetadata::Type::kFullyCustomized) {
826
    // For now we only do this check for the customized snapshots - we know
827
    // that the flags we use in the default snapshot are limited and safe
828
    // enough so we can relax the constraints for it.
829
    fprintf(stderr,
830
            "Failed to load the startup snapshot because it was built with "
831
            "a different version of V8 or with different V8 configurations.\n"
832
            "Expected tag %" PRIx32 ", read %" PRIx32 "\n",
833
            current_cache_version,
834
            metadata.v8_cache_version_tag);
835
    return false;
836
  }
837
838
  // TODO(joyeecheung): check incompatible Node.js flags.
839
5408
  return true;
840
}
841
842
5482
SnapshotData::~SnapshotData() {
843
5482
  if (data_ownership == DataOwnership::kOwned &&
844
9
      v8_snapshot_blob_data.data != nullptr) {
845
8
    delete[] v8_snapshot_blob_data.data;
846
  }
847
5482
}
848
849
template <typename T>
850
3504
void WriteVector(std::ostream* ss, const T* vec, size_t size) {
851
26142028
  for (size_t i = 0; i < size; i++) {
852
26138524
    *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ',');
853
  }
854
3504
}
855
856
3492
static std::string GetCodeCacheDefName(const std::string& id) {
857
3492
  char buf[64] = {0};
858
3492
  size_t size = id.size();
859
3492
  CHECK_LT(size, sizeof(buf));
860
78108
  for (size_t i = 0; i < size; ++i) {
861
74616
    char ch = id[i];
862

74616
    buf[i] = (ch == '-' || ch == '/') ? '_' : ch;
863
  }
864
3492
  return std::string(buf) + std::string("_cache_data");
865
}
866
867
1746
static std::string FormatSize(size_t size) {
868
1746
  char buf[64] = {0};
869
1746
  if (size < 1024) {
870
156
    snprintf(buf, sizeof(buf), "%.2fB", static_cast<double>(size));
871
1590
  } else if (size < 1024 * 1024) {
872
1590
    snprintf(buf, sizeof(buf), "%.2fKB", static_cast<double>(size / 1024));
873
  } else {
874
    snprintf(
875
        buf, sizeof(buf), "%.2fMB", static_cast<double>(size / 1024 / 1024));
876
  }
877
1746
  return buf;
878
}
879
880
1746
static void WriteStaticCodeCacheData(std::ostream* ss,
881
                                     const builtins::CodeCacheInfo& info) {
882
1746
  *ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n";
883
1746
  WriteVector(ss, info.data.data(), info.data.size());
884
1746
  *ss << "};";
885
1746
}
886
887
1746
static void WriteCodeCacheInitializer(std::ostream* ss, const std::string& id) {
888
3492
  std::string def_name = GetCodeCacheDefName(id);
889
1746
  *ss << "    { \"" << id << "\",\n";
890
1746
  *ss << "      {" << def_name << ",\n";
891
1746
  *ss << "       " << def_name << " + arraysize(" << def_name << "),\n";
892
1746
  *ss << "      }\n";
893
1746
  *ss << "    },\n";
894
1746
}
895
896
6
void FormatBlob(std::ostream& ss, SnapshotData* data) {
897
6
  ss << R"(#include <cstddef>
898
#include "env.h"
899
#include "node_snapshot_builder.h"
900
#include "v8.h"
901
902
// This file is generated by tools/snapshot. Do not edit.
903
904
namespace node {
905
906
static const char v8_snapshot_blob_data[] = {
907
)";
908
6
  WriteVector(&ss,
909
              data->v8_snapshot_blob_data.data,
910
6
              data->v8_snapshot_blob_data.raw_size);
911
6
  ss << R"(};
912
913
static const int v8_snapshot_blob_size = )"
914
6
     << data->v8_snapshot_blob_data.raw_size << ";";
915
916
  // Windows can't deal with too many large vector initializers.
917
  // Store the data into static arrays first.
918
1752
  for (const auto& item : data->code_cache) {
919
1746
    WriteStaticCodeCacheData(&ss, item);
920
  }
921
922
6
  ss << R"(SnapshotData snapshot_data {
923
  // -- data_ownership begins --
924
  SnapshotData::DataOwnership::kNotOwned,
925
  // -- data_ownership ends --
926
  // -- metadata begins --
927
6
)" << data->metadata
928
6
     << R"(,
929
  // -- metadata ends --
930
  // -- v8_snapshot_blob_data begins --
931
  { v8_snapshot_blob_data, v8_snapshot_blob_size },
932
  // -- v8_snapshot_blob_data ends --
933
  // -- isolate_data_indices begins --
934
6
)" << data->isolate_data_info
935
6
     << R"(
936
  // -- isolate_data_indices ends --
937
  ,
938
  // -- env_info begins --
939
6
)" << data->env_info
940
6
     << R"(
941
  // -- env_info ends --
942
  ,
943
  // -- code_cache begins --
944
  {)";
945
1752
  for (const auto& item : data->code_cache) {
946
1746
    WriteCodeCacheInitializer(&ss, item.id);
947
  }
948
6
  ss << R"(
949
  }
950
  // -- code_cache ends --
951
};
952
953
const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() {
954
  Mutex::ScopedLock lock(snapshot_data_mutex_);
955
  return &snapshot_data;
956
}
957
}  // namespace node
958
)";
959
6
}
960
961
// Reset context settings that need to be initialized again after
962
// deserialization.
963
13
static void ResetContextSettingsBeforeSnapshot(Local<Context> context) {
964
  // Reset the AllowCodeGenerationFromStrings flag to true (default value) so
965
  // that it can be re-initialized with v8 flag
966
  // --disallow-code-generation-from-strings and recognized in
967
  // node::InitializeContextRuntime.
968
13
  context->AllowCodeGenerationFromStrings(true);
969
13
}
970
971
Mutex SnapshotBuilder::snapshot_data_mutex_;
972
973
6147
const std::vector<intptr_t>& SnapshotBuilder::CollectExternalReferences() {
974

6147
  static auto registry = std::make_unique<ExternalReferenceRegistry>();
975
6147
  return registry->external_references();
976
}
977
978
6140
void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data,
979
                                              Isolate::CreateParams* params) {
980
6140
  params->external_references = CollectExternalReferences().data();
981
6140
  params->snapshot_blob =
982
6140
      const_cast<v8::StartupData*>(&(data->v8_snapshot_blob_data));
983
6140
}
984
985
// TODO(joyeecheung): share these exit code constants across the code base.
986
constexpr int UNCAUGHT_EXCEPTION_ERROR = 1;
987
constexpr int BOOTSTRAP_ERROR = 10;
988
constexpr int SNAPSHOT_ERROR = 14;
989
990
7
int SnapshotBuilder::Generate(SnapshotData* out,
991
                              const std::vector<std::string> args,
992
                              const std::vector<std::string> exec_args) {
993
  const std::vector<intptr_t>& external_references =
994
7
      CollectExternalReferences();
995
7
  Isolate* isolate = Isolate::Allocate();
996
  // Must be done before the SnapshotCreator creation so  that the
997
  // memory reducer can be initialized.
998
14
  per_process::v8_platform.Platform()->RegisterIsolate(isolate,
999
7
                                                       uv_default_loop());
1000
1001
14
  SnapshotCreator creator(isolate, external_references.data());
1002
1003
7
  isolate->SetCaptureStackTraceForUncaughtExceptions(
1004
      true, 10, v8::StackTrace::StackTraceOptions::kDetailed);
1005
1006
7
  Environment* env = nullptr;
1007
  std::unique_ptr<NodeMainInstance> main_instance =
1008
      NodeMainInstance::Create(isolate,
1009
                               uv_default_loop(),
1010
7
                               per_process::v8_platform.Platform(),
1011
                               args,
1012
14
                               exec_args);
1013
1014
  // The cleanups should be done in case of an early exit due to errors.
1015
7
  auto cleanup = OnScopeLeave([&]() {
1016
    // Must be done while the snapshot creator isolate is entered i.e. the
1017
    // creator is still alive. The snapshot creator destructor will destroy
1018
    // the isolate.
1019
7
    if (env != nullptr) {
1020
7
      FreeEnvironment(env);
1021
    }
1022
7
    main_instance->Dispose();
1023
7
    per_process::v8_platform.Platform()->UnregisterIsolate(isolate);
1024
14
  });
1025
1026
  // It's only possible to be kDefault in node_mksnapshot.
1027
  SnapshotMetadata::Type snapshot_type =
1028
7
      per_process::cli_options->build_snapshot
1029
7
          ? SnapshotMetadata::Type::kFullyCustomized
1030
7
          : SnapshotMetadata::Type::kDefault;
1031
1032
  {
1033
7
    HandleScope scope(isolate);
1034
7
    TryCatch bootstrapCatch(isolate);
1035
1036
7
    auto print_Exception = OnScopeLeave([&]() {
1037
7
      if (bootstrapCatch.HasCaught()) {
1038
2
        PrintCaughtException(
1039
1
            isolate, isolate->GetCurrentContext(), bootstrapCatch);
1040
      }
1041
7
    });
1042
1043
    // The default context with only things created by V8.
1044
7
    Local<Context> default_context = Context::New(isolate);
1045
1046
    // The context used by the vm module.
1047
    Local<Context> vm_context;
1048
    {
1049
      Local<ObjectTemplate> global_template =
1050
7
          main_instance->isolate_data()->contextify_global_template();
1051
7
      CHECK(!global_template.IsEmpty());
1052
7
      if (!contextify::ContextifyContext::CreateV8Context(
1053
7
               isolate, global_template, nullptr, nullptr)
1054
7
               .ToLocal(&vm_context)) {
1055
        return SNAPSHOT_ERROR;
1056
      }
1057
    }
1058
1059
    // The Node.js-specific context with primodials, can be used by workers
1060
    // TODO(joyeecheung): investigate if this can be used by vm contexts
1061
    // without breaking compatibility.
1062
7
    Local<Context> base_context = NewContext(isolate);
1063
7
    if (base_context.IsEmpty()) {
1064
      return BOOTSTRAP_ERROR;
1065
    }
1066
7
    ResetContextSettingsBeforeSnapshot(base_context);
1067
1068
7
    Local<Context> main_context = NewContext(isolate);
1069
7
    if (main_context.IsEmpty()) {
1070
      return BOOTSTRAP_ERROR;
1071
    }
1072
    // Initialize the main instance context.
1073
    {
1074
7
      Context::Scope context_scope(main_context);
1075
1076
      // Create the environment.
1077
7
      env = new Environment(main_instance->isolate_data(),
1078
                            main_context,
1079
                            args,
1080
                            exec_args,
1081
                            nullptr,
1082
                            node::EnvironmentFlags::kDefaultFlags,
1083
7
                            {});
1084
1085
      // Run scripts in lib/internal/bootstrap/
1086
14
      if (env->RunBootstrapping().IsEmpty()) {
1087
        return BOOTSTRAP_ERROR;
1088
      }
1089
      // If --build-snapshot is true, lib/internal/main/mksnapshot.js would be
1090
      // loaded via LoadEnvironment() to execute process.argv[1] as the entry
1091
      // point (we currently only support this kind of entry point, but we
1092
      // could also explore snapshotting other kinds of execution modes
1093
      // in the future).
1094
7
      if (snapshot_type == SnapshotMetadata::Type::kFullyCustomized) {
1095
#if HAVE_INSPECTOR
1096
        // TODO(joyeecheung): move this before RunBootstrapping().
1097
1
        env->InitializeInspector({});
1098
#endif
1099
2
        if (LoadEnvironment(env, StartExecutionCallback{}).IsEmpty()) {
1100
1
          return UNCAUGHT_EXCEPTION_ERROR;
1101
        }
1102
        // FIXME(joyeecheung): right now running the loop in the snapshot
1103
        // builder seems to introduces inconsistencies in JS land that need to
1104
        // be synchronized again after snapshot restoration.
1105
        int exit_code = SpinEventLoop(env).FromMaybe(UNCAUGHT_EXCEPTION_ERROR);
1106
        if (exit_code != 0) {
1107
          return exit_code;
1108
        }
1109
      }
1110
1111
6
      if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1112
        env->PrintAllBaseObjects();
1113
        printf("Environment = %p\n", env);
1114
      }
1115
1116
      // Serialize the native states
1117
      out->isolate_data_info =
1118
6
          main_instance->isolate_data()->Serialize(&creator);
1119
6
      out->env_info = env->Serialize(&creator);
1120
1121
#ifdef NODE_USE_NODE_CODE_CACHE
1122
      // Regenerate all the code cache.
1123
6
      if (!builtins::BuiltinLoader::CompileAllBuiltins(main_context)) {
1124
        return UNCAUGHT_EXCEPTION_ERROR;
1125
      }
1126
6
      builtins::BuiltinLoader::CopyCodeCache(&(out->code_cache));
1127
1752
      for (const auto& item : out->code_cache) {
1128
1746
        std::string size_str = FormatSize(item.data.size());
1129
        per_process::Debug(DebugCategory::MKSNAPSHOT,
1130
                           "Generated code cache for %d: %s\n",
1131
1746
                           item.id.c_str(),
1132
3492
                           size_str.c_str());
1133
      }
1134
#endif
1135
1136
6
      ResetContextSettingsBeforeSnapshot(main_context);
1137
    }
1138
1139
    // Global handles to the contexts can't be disposed before the
1140
    // blob is created. So initialize all the contexts before adding them.
1141
    // TODO(joyeecheung): figure out how to remove this restriction.
1142
6
    creator.SetDefaultContext(default_context);
1143
6
    size_t index = creator.AddContext(vm_context);
1144
6
    CHECK_EQ(index, SnapshotData::kNodeVMContextIndex);
1145
6
    index = creator.AddContext(base_context);
1146
6
    CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex);
1147
6
    index = creator.AddContext(main_context,
1148
                               {SerializeNodeContextInternalFields, env});
1149
6
    CHECK_EQ(index, SnapshotData::kNodeMainContextIndex);
1150
  }
1151
1152
  // Must be out of HandleScope
1153
  out->v8_snapshot_blob_data =
1154
6
      creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kKeep);
1155
1156
  // We must be able to rehash the blob when we restore it or otherwise
1157
  // the hash seed would be fixed by V8, introducing a vulnerability.
1158
6
  if (!out->v8_snapshot_blob_data.CanBeRehashed()) {
1159
    return SNAPSHOT_ERROR;
1160
  }
1161
1162
12
  out->metadata = SnapshotMetadata{snapshot_type,
1163
                                   per_process::metadata.versions.node,
1164
                                   per_process::metadata.arch,
1165
                                   per_process::metadata.platform,
1166
6
                                   v8::ScriptCompiler::CachedDataVersionTag()};
1167
1168
  // We cannot resurrect the handles from the snapshot, so make sure that
1169
  // no handles are left open in the environment after the blob is created
1170
  // (which should trigger a GC and close all handles that can be closed).
1171
  bool queues_are_empty =
1172

6
      env->req_wrap_queue()->IsEmpty() && env->handle_wrap_queue()->IsEmpty();
1173

12
  if (!queues_are_empty ||
1174
6
      per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1175
    PrintLibuvHandleInformation(env->event_loop(), stderr);
1176
  }
1177
6
  if (!queues_are_empty) {
1178
    return SNAPSHOT_ERROR;
1179
  }
1180
6
  return 0;
1181
}
1182
1183
6
int SnapshotBuilder::Generate(std::ostream& out,
1184
                              const std::vector<std::string> args,
1185
                              const std::vector<std::string> exec_args) {
1186
12
  SnapshotData data;
1187
6
  int exit_code = Generate(&data, args, exec_args);
1188
6
  if (exit_code != 0) {
1189
    return exit_code;
1190
  }
1191
6
  FormatBlob(out, &data);
1192
6
  return exit_code;
1193
}
1194
1195
46819
SnapshotableObject::SnapshotableObject(Environment* env,
1196
                                       Local<Object> wrap,
1197
46819
                                       EmbedderObjectType type)
1198
46819
    : BaseObject(env, wrap), type_(type) {
1199
46819
}
1200
1201
48
const char* SnapshotableObject::GetTypeNameChars() const {
1202

48
  switch (type_) {
1203
#define V(PropertyName, NativeTypeName)                                        \
1204
  case EmbedderObjectType::k_##PropertyName: {                                 \
1205
    return NativeTypeName::type_name.c_str();                                  \
1206
  }
1207
48
    SERIALIZABLE_OBJECT_TYPES(V)
1208
#undef V
1209
    default: { UNREACHABLE(); }
1210
  }
1211
}
1212
1213
21632
void DeserializeNodeInternalFields(Local<Object> holder,
1214
                                   int index,
1215
                                   StartupData payload,
1216
                                   void* env) {
1217
21632
  if (payload.raw_size == 0) {
1218
    holder->SetAlignedPointerInInternalField(index, nullptr);
1219
    return;
1220
  }
1221
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1222
                     "Deserialize internal field %d of %p, size=%d\n",
1223
43264
                     static_cast<int>(index),
1224
21632
                     (*holder),
1225
21632
                     static_cast<int>(payload.raw_size));
1226
1227
21632
  if (payload.raw_size == 0) {
1228
    holder->SetAlignedPointerInInternalField(index, nullptr);
1229
    return;
1230
  }
1231
1232
  DCHECK_EQ(index, BaseObject::kEmbedderType);
1233
1234
21632
  Environment* env_ptr = static_cast<Environment*>(env);
1235
21632
  const InternalFieldInfoBase* info =
1236
      reinterpret_cast<const InternalFieldInfoBase*>(payload.data);
1237
  // TODO(joyeecheung): we can add a constant kNodeEmbedderId to the
1238
  // beginning of every InternalFieldInfoBase to ensure that we don't
1239
  // step on payloads that were not serialized by Node.js.
1240

21632
  switch (info->type) {
1241
#define V(PropertyName, NativeTypeName)                                        \
1242
  case EmbedderObjectType::k_##PropertyName: {                                 \
1243
    per_process::Debug(DebugCategory::MKSNAPSHOT,                              \
1244
                       "Object %p is %s\n",                                    \
1245
                       (*holder),                                              \
1246
                       NativeTypeName::type_name.c_str());                     \
1247
    env_ptr->EnqueueDeserializeRequest(                                        \
1248
        NativeTypeName::Deserialize,                                           \
1249
        holder,                                                                \
1250
        index,                                                                 \
1251
        info->Copy<NativeTypeName::InternalFieldInfo>());                      \
1252
    break;                                                                     \
1253
  }
1254
43264
    SERIALIZABLE_OBJECT_TYPES(V)
1255
#undef V
1256
    default: {
1257
      // This should only be reachable during development when trying to
1258
      // deserialize a snapshot blob built by a version of Node.js that
1259
      // has more recognizable EmbedderObjectTypes than the deserializing
1260
      // Node.js binary.
1261
      fprintf(stderr,
1262
              "Unknown embedder object type %" PRIu8 ", possibly caused by "
1263
              "mismatched Node.js versions\n",
1264
              static_cast<uint8_t>(info->type));
1265
      ABORT();
1266
    }
1267
  }
1268
}
1269
1270
828
StartupData SerializeNodeContextInternalFields(Local<Object> holder,
1271
                                               int index,
1272
                                               void* env) {
1273
  // We only do one serialization for the kEmbedderType slot, the result
1274
  // contains everything necessary for deserializing the entire object,
1275
  // including the fields whose index is bigger than kEmbedderType
1276
  // (most importantly, BaseObject::kSlot).
1277
  // For Node.js this design is enough for all the native binding that are
1278
  // serializable.
1279
828
  if (index != BaseObject::kEmbedderType) {
1280
414
    return StartupData{nullptr, 0};
1281
  }
1282
1283
414
  void* type_ptr = holder->GetAlignedPointerFromInternalField(index);
1284
414
  if (type_ptr == nullptr) {
1285
390
    return StartupData{nullptr, 0};
1286
  }
1287
1288
24
  uint16_t type = *(static_cast<uint16_t*>(type_ptr));
1289
  per_process::Debug(DebugCategory::MKSNAPSHOT, "type = 0x%x\n", type);
1290
24
  if (type != kNodeEmbedderId) {
1291
    return StartupData{nullptr, 0};
1292
  }
1293
1294
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1295
                     "Serialize internal field, index=%d, holder=%p\n",
1296
48
                     static_cast<int>(index),
1297
48
                     *holder);
1298
1299
  void* native_ptr =
1300
48
      holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
1301
  per_process::Debug(DebugCategory::MKSNAPSHOT, "native = %p\n", native_ptr);
1302
  DCHECK(static_cast<BaseObject*>(native_ptr)->is_snapshotable());
1303
24
  SnapshotableObject* obj = static_cast<SnapshotableObject*>(native_ptr);
1304
1305
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1306
                     "Object %p is %s, ",
1307
24
                     *holder,
1308
24
                     obj->GetTypeNameChars());
1309
24
  InternalFieldInfoBase* info = obj->Serialize(index);
1310
1311
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1312
                     "payload size=%d\n",
1313
24
                     static_cast<int>(info->length));
1314
  return StartupData{reinterpret_cast<const char*>(info),
1315
24
                     static_cast<int>(info->length)};
1316
}
1317
1318
6
void SerializeSnapshotableObjects(Environment* env,
1319
                                  SnapshotCreator* creator,
1320
                                  EnvSerializeInfo* info) {
1321
6
  uint32_t i = 0;
1322
6
  env->ForEachBaseObject([&](BaseObject* obj) {
1323
    // If there are any BaseObjects that are not snapshotable left
1324
    // during context serialization, V8 would crash due to unregistered
1325
    // global handles and print detailed information about them.
1326
24
    if (!obj->is_snapshotable()) {
1327
      return;
1328
    }
1329
24
    SnapshotableObject* ptr = static_cast<SnapshotableObject*>(obj);
1330
1331
24
    const char* type_name = ptr->GetTypeNameChars();
1332
    per_process::Debug(DebugCategory::MKSNAPSHOT,
1333
                       "Serialize snapshotable object %i (%p), "
1334
                       "object=%p, type=%s\n",
1335
96
                       static_cast<int>(i),
1336
                       ptr,
1337
48
                       *(ptr->object()),
1338
                       type_name);
1339
1340
24
    if (ptr->PrepareForSerialization(env->context(), creator)) {
1341
24
      SnapshotIndex index = creator->AddData(env->context(), obj->object());
1342
      per_process::Debug(DebugCategory::MKSNAPSHOT,
1343
                         "Serialized with index=%d\n",
1344
24
                         static_cast<int>(index));
1345
24
      info->native_objects.push_back({type_name, i, index});
1346
    }
1347
24
    i++;
1348
  });
1349
6
}
1350
1351
namespace mksnapshot {
1352
1353
1
void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
1354
2
  CHECK(args[0]->IsString());
1355
2
  Local<String> filename = args[0].As<String>();
1356
2
  Local<String> source = args[1].As<String>();
1357
1
  Isolate* isolate = args.GetIsolate();
1358
1
  Local<Context> context = isolate->GetCurrentContext();
1359
1
  ScriptOrigin origin(isolate, filename, 0, 0, true);
1360
  // TODO(joyeecheung): do we need all of these? Maybe we would want a less
1361
  // internal version of them.
1362
  std::vector<Local<String>> parameters = {
1363
1
      FIXED_ONE_BYTE_STRING(isolate, "require"),
1364
1
      FIXED_ONE_BYTE_STRING(isolate, "__filename"),
1365
1
      FIXED_ONE_BYTE_STRING(isolate, "__dirname"),
1366
3
  };
1367
1
  ScriptCompiler::Source script_source(source, origin);
1368
  Local<Function> fn;
1369
1
  if (ScriptCompiler::CompileFunction(context,
1370
                                      &script_source,
1371
                                      parameters.size(),
1372
                                      parameters.data(),
1373
                                      0,
1374
                                      nullptr,
1375
1
                                      ScriptCompiler::kEagerCompile)
1376
1
          .ToLocal(&fn)) {
1377
2
    args.GetReturnValue().Set(fn);
1378
  }
1379
1
}
1380
1381
1
void SetSerializeCallback(const FunctionCallbackInfo<Value>& args) {
1382
1
  Environment* env = Environment::GetCurrent(args);
1383
2
  CHECK(env->snapshot_serialize_callback().IsEmpty());
1384
1
  CHECK(args[0]->IsFunction());
1385
2
  env->set_snapshot_serialize_callback(args[0].As<Function>());
1386
1
}
1387
1388
1
void SetDeserializeCallback(const FunctionCallbackInfo<Value>& args) {
1389
1
  Environment* env = Environment::GetCurrent(args);
1390
2
  CHECK(env->snapshot_deserialize_callback().IsEmpty());
1391
1
  CHECK(args[0]->IsFunction());
1392
2
  env->set_snapshot_deserialize_callback(args[0].As<Function>());
1393
1
}
1394
1395
void SetDeserializeMainFunction(const FunctionCallbackInfo<Value>& args) {
1396
  Environment* env = Environment::GetCurrent(args);
1397
  CHECK(env->snapshot_deserialize_main().IsEmpty());
1398
  CHECK(args[0]->IsFunction());
1399
  env->set_snapshot_deserialize_main(args[0].As<Function>());
1400
}
1401
1402
781
void Initialize(Local<Object> target,
1403
                Local<Value> unused,
1404
                Local<Context> context,
1405
                void* priv) {
1406
781
  SetMethod(context, target, "compileSerializeMain", CompileSerializeMain);
1407
781
  SetMethod(context, target, "setSerializeCallback", SetSerializeCallback);
1408
781
  SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback);
1409
781
  SetMethod(context,
1410
            target,
1411
            "setDeserializeMainFunction",
1412
            SetDeserializeMainFunction);
1413
781
}
1414
1415
5415
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1416
5415
  registry->Register(CompileSerializeMain);
1417
5415
  registry->Register(SetSerializeCallback);
1418
5415
  registry->Register(SetDeserializeCallback);
1419
5415
  registry->Register(SetDeserializeMainFunction);
1420
5415
}
1421
}  // namespace mksnapshot
1422
}  // namespace node
1423
1424
5487
NODE_MODULE_CONTEXT_AWARE_INTERNAL(mksnapshot, node::mksnapshot::Initialize)
1425
5415
NODE_MODULE_EXTERNAL_REFERENCE(mksnapshot,
1426
                               node::mksnapshot::RegisterExternalReferences)