#include "node_snapshotable.h" #include #include #include "base_object-inl.h" #include "debug_utils-inl.h" #include "env-inl.h" #include "node_errors.h" #include "node_external_reference.h" #include "node_file.h" #include "node_internals.h" #include "node_main_instance.h" #include "node_v8.h" #include "node_v8_platform-inl.h" namespace node { using v8::Context; using v8::HandleScope; using v8::Isolate; using v8::Local; using v8::Object; using v8::SnapshotCreator; using v8::StartupData; using v8::TryCatch; using v8::Value; template void WriteVector(std::ostringstream* ss, const T* vec, size_t size) { for (size_t i = 0; i < size; i++) { *ss << std::to_string(vec[i]) << (i == size - 1 ? '\n' : ','); } } std::string FormatBlob(SnapshotData* data) { std::ostringstream ss; ss << R"(#include #include "env.h" #include "node_main_instance.h" #include "v8.h" // This file is generated by tools/snapshot. Do not edit. namespace node { static const char blob_data[] = { )"; WriteVector(&ss, data->blob.data, data->blob.raw_size); ss << R"(}; static const int blob_size = )" << data->blob.raw_size << R"(; static v8::StartupData blob = { blob_data, blob_size }; )"; ss << R"(v8::StartupData* NodeMainInstance::GetEmbeddedSnapshotBlob() { return &blob; } static const std::vector isolate_data_indices { )"; WriteVector(&ss, data->isolate_data_indices.data(), data->isolate_data_indices.size()); ss << R"(}; const std::vector* NodeMainInstance::GetIsolateDataIndices() { return &isolate_data_indices; } static const EnvSerializeInfo env_info )" << data->env_info << R"(; const EnvSerializeInfo* NodeMainInstance::GetEnvSerializeInfo() { return &env_info; } } // namespace node )"; return ss.str(); } void SnapshotBuilder::Generate(SnapshotData* out, const std::vector args, const std::vector exec_args) { Isolate* isolate = Isolate::Allocate(); isolate->SetCaptureStackTraceForUncaughtExceptions( true, 10, v8::StackTrace::StackTraceOptions::kDetailed); per_process::v8_platform.Platform()->RegisterIsolate(isolate, uv_default_loop()); std::unique_ptr main_instance; std::string result; { const std::vector& external_references = NodeMainInstance::CollectExternalReferences(); SnapshotCreator creator(isolate, external_references.data()); Environment* env; { main_instance = NodeMainInstance::Create(isolate, uv_default_loop(), per_process::v8_platform.Platform(), args, exec_args); HandleScope scope(isolate); creator.SetDefaultContext(Context::New(isolate)); out->isolate_data_indices = main_instance->isolate_data()->Serialize(&creator); // Run the per-context scripts Local context; { TryCatch bootstrapCatch(isolate); context = NewContext(isolate); if (bootstrapCatch.HasCaught()) { PrintCaughtException(isolate, context, bootstrapCatch); abort(); } } Context::Scope context_scope(context); // Create the environment env = new Environment(main_instance->isolate_data(), context, args, exec_args, nullptr, node::EnvironmentFlags::kDefaultFlags, {}); // Run scripts in lib/internal/bootstrap/ { TryCatch bootstrapCatch(isolate); v8::MaybeLocal result = env->RunBootstrapping(); if (bootstrapCatch.HasCaught()) { PrintCaughtException(isolate, context, bootstrapCatch); } result.ToLocalChecked(); } if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) { env->PrintAllBaseObjects(); printf("Environment = %p\n", env); } // Serialize the native states out->env_info = env->Serialize(&creator); // Serialize the context size_t index = creator.AddContext( context, {SerializeNodeContextInternalFields, env}); CHECK_EQ(index, NodeMainInstance::kNodeContextIndex); } // Must be out of HandleScope out->blob = creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear); CHECK(out->blob.CanBeRehashed()); // Must be done while the snapshot creator isolate is entered i.e. the // creator is still alive. FreeEnvironment(env); main_instance->Dispose(); } per_process::v8_platform.Platform()->UnregisterIsolate(isolate); } std::string SnapshotBuilder::Generate( const std::vector args, const std::vector exec_args) { SnapshotData data; Generate(&data, args, exec_args); std::string result = FormatBlob(&data); delete[] data.blob.data; return result; } SnapshotableObject::SnapshotableObject(Environment* env, Local wrap, EmbedderObjectType type) : BaseObject(env, wrap), type_(type) { } const char* SnapshotableObject::GetTypeNameChars() const { switch (type_) { #define V(PropertyName, NativeTypeName) \ case EmbedderObjectType::k_##PropertyName: { \ return NativeTypeName::type_name.c_str(); \ } SERIALIZABLE_OBJECT_TYPES(V) #undef V default: { UNREACHABLE(); } } } bool IsSnapshotableType(FastStringKey key) { #define V(PropertyName, NativeTypeName) \ if (key == NativeTypeName::type_name) { \ return true; \ } SERIALIZABLE_OBJECT_TYPES(V) #undef V return false; } void DeserializeNodeInternalFields(Local holder, int index, StartupData payload, void* env) { per_process::Debug(DebugCategory::MKSNAPSHOT, "Deserialize internal field %d of %p, size=%d\n", static_cast(index), (*holder), static_cast(payload.raw_size)); if (payload.raw_size == 0) { holder->SetAlignedPointerInInternalField(index, nullptr); return; } Environment* env_ptr = static_cast(env); const InternalFieldInfo* info = reinterpret_cast(payload.data); switch (info->type) { #define V(PropertyName, NativeTypeName) \ case EmbedderObjectType::k_##PropertyName: { \ per_process::Debug(DebugCategory::MKSNAPSHOT, \ "Object %p is %s\n", \ (*holder), \ NativeTypeName::type_name.c_str()); \ env_ptr->EnqueueDeserializeRequest( \ NativeTypeName::Deserialize, holder, index, info->Copy()); \ break; \ } SERIALIZABLE_OBJECT_TYPES(V) #undef V default: { UNREACHABLE(); } } } StartupData SerializeNodeContextInternalFields(Local holder, int index, void* env) { per_process::Debug(DebugCategory::MKSNAPSHOT, "Serialize internal field, index=%d, holder=%p\n", static_cast(index), *holder); void* ptr = holder->GetAlignedPointerFromInternalField(BaseObject::kSlot); if (ptr == nullptr) { return StartupData{nullptr, 0}; } DCHECK(static_cast(ptr)->is_snapshotable()); SnapshotableObject* obj = static_cast(ptr); per_process::Debug(DebugCategory::MKSNAPSHOT, "Object %p is %s, ", *holder, obj->GetTypeNameChars()); InternalFieldInfo* info = obj->Serialize(index); per_process::Debug(DebugCategory::MKSNAPSHOT, "payload size=%d\n", static_cast(info->length)); return StartupData{reinterpret_cast(info), static_cast(info->length)}; } void SerializeBindingData(Environment* env, SnapshotCreator* creator, EnvSerializeInfo* info) { size_t i = 0; env->ForEachBindingData([&](FastStringKey key, BaseObjectPtr binding) { per_process::Debug(DebugCategory::MKSNAPSHOT, "Serialize binding %i, %p, type=%s\n", static_cast(i), *(binding->object()), key.c_str()); if (IsSnapshotableType(key)) { size_t index = creator->AddData(env->context(), binding->object()); per_process::Debug(DebugCategory::MKSNAPSHOT, "Serialized with index=%d\n", static_cast(index)); info->bindings.push_back({key.c_str(), i, index}); SnapshotableObject* ptr = static_cast(binding.get()); ptr->PrepareForSerialization(env->context(), creator); } else { UNREACHABLE(); } i++; }); } } // namespace node