GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 520 655 79.4 %
Date: 2022-08-21 04:19:51 Branches: 132 230 57.4 %

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

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

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

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

12
  if (!queues_are_empty ||
1144
6
      per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
1145
    PrintLibuvHandleInformation(env->event_loop(), stderr);
1146
  }
1147
6
  if (!queues_are_empty) {
1148
    return SNAPSHOT_ERROR;
1149
  }
1150
6
  return 0;
1151
}
1152
1153
6
int SnapshotBuilder::Generate(std::ostream& out,
1154
                              const std::vector<std::string> args,
1155
                              const std::vector<std::string> exec_args) {
1156
12
  SnapshotData data;
1157
6
  int exit_code = Generate(&data, args, exec_args);
1158
6
  if (exit_code != 0) {
1159
    return exit_code;
1160
  }
1161
6
  FormatBlob(out, &data);
1162
6
  return exit_code;
1163
}
1164
1165
46388
SnapshotableObject::SnapshotableObject(Environment* env,
1166
                                       Local<Object> wrap,
1167
46388
                                       EmbedderObjectType type)
1168
46388
    : BaseObject(env, wrap), type_(type) {
1169
46388
}
1170
1171
48
const char* SnapshotableObject::GetTypeNameChars() const {
1172

48
  switch (type_) {
1173
#define V(PropertyName, NativeTypeName)                                        \
1174
  case EmbedderObjectType::k_##PropertyName: {                                 \
1175
    return NativeTypeName::type_name.c_str();                                  \
1176
  }
1177
48
    SERIALIZABLE_OBJECT_TYPES(V)
1178
#undef V
1179
    default: { UNREACHABLE(); }
1180
  }
1181
}
1182
1183
21400
void DeserializeNodeInternalFields(Local<Object> holder,
1184
                                   int index,
1185
                                   StartupData payload,
1186
                                   void* env) {
1187
21400
  if (payload.raw_size == 0) {
1188
    holder->SetAlignedPointerInInternalField(index, nullptr);
1189
    return;
1190
  }
1191
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1192
                     "Deserialize internal field %d of %p, size=%d\n",
1193
42800
                     static_cast<int>(index),
1194
21400
                     (*holder),
1195
21400
                     static_cast<int>(payload.raw_size));
1196
1197
21400
  if (payload.raw_size == 0) {
1198
    holder->SetAlignedPointerInInternalField(index, nullptr);
1199
    return;
1200
  }
1201
1202
  DCHECK_EQ(index, BaseObject::kEmbedderType);
1203
1204
21400
  Environment* env_ptr = static_cast<Environment*>(env);
1205
21400
  const InternalFieldInfoBase* info =
1206
      reinterpret_cast<const InternalFieldInfoBase*>(payload.data);
1207
  // TODO(joyeecheung): we can add a constant kNodeEmbedderId to the
1208
  // beginning of every InternalFieldInfoBase to ensure that we don't
1209
  // step on payloads that were not serialized by Node.js.
1210

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