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