GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_snapshotable.cc Lines: 519 657 79.0 %
Date: 2022-08-12 04:19:25 Branches: 136 234 58.1 %

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

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

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

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

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

24
  switch (type_) {
1172
#define V(PropertyName, NativeTypeName)                                        \
1173
  case EmbedderObjectType::k_##PropertyName: {                                 \
1174
    return NativeTypeName::type_name.c_str();                                  \
1175
  }
1176
24
    SERIALIZABLE_OBJECT_TYPES(V)
1177
#undef V
1178
    default: { UNREACHABLE(); }
1179
  }
1180
}
1181
1182
24
bool IsSnapshotableType(FastStringKey key) {
1183
#define V(PropertyName, NativeTypeName)                                        \
1184
  if (key == NativeTypeName::type_name) {                                      \
1185
    return true;                                                               \
1186
  }
1187


24
  SERIALIZABLE_OBJECT_TYPES(V)
1188
#undef V
1189
1190
  return false;
1191
}
1192
1193
21320
void DeserializeNodeInternalFields(Local<Object> holder,
1194
                                   int index,
1195
                                   StartupData payload,
1196
                                   void* env) {
1197
21320
  if (payload.raw_size == 0) {
1198
    holder->SetAlignedPointerInInternalField(index, nullptr);
1199
    return;
1200
  }
1201
  per_process::Debug(DebugCategory::MKSNAPSHOT,
1202
                     "Deserialize internal field %d of %p, size=%d\n",
1203
42640
                     static_cast<int>(index),
1204
21320
                     (*holder),
1205
21320
                     static_cast<int>(payload.raw_size));
1206
1207
21320
  if (payload.raw_size == 0) {
1208
    holder->SetAlignedPointerInInternalField(index, nullptr);
1209
    return;
1210
  }
1211
1212
  DCHECK_EQ(index, BaseObject::kEmbedderType);
1213
1214
21320
  Environment* env_ptr = static_cast<Environment*>(env);
1215
21320
  const InternalFieldInfo* info =
1216
      reinterpret_cast<const InternalFieldInfo*>(payload.data);
1217
  // TODO(joyeecheung): we can add a constant kNodeEmbedderId to the
1218
  // beginning of every InternalFieldInfo to ensure that we don't
1219
  // step on payloads that were not serialized by Node.js.
1220

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