mirror of
https://github.com/nodejs/node.git
synced 2025-12-28 07:50:41 +00:00
This patch: - Moves the snapshot building code to src/ so that we can reuse it later when generating custom snapshots from an entry point accepted by the node binary. - Create a SnapshotData struct that incorporates all the data useful for a snapshot blob, including both the V8 data and the Node.js data. PR-URL: https://github.com/nodejs/node/pull/38902 Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
299 lines
9.7 KiB
C++
299 lines
9.7 KiB
C++
|
|
#include "node_snapshotable.h"
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#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 <typename T>
|
|
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 <cstddef>
|
|
#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<size_t> isolate_data_indices {
|
|
)";
|
|
WriteVector(&ss,
|
|
data->isolate_data_indices.data(),
|
|
data->isolate_data_indices.size());
|
|
ss << R"(};
|
|
|
|
const std::vector<size_t>* 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<std::string> args,
|
|
const std::vector<std::string> 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<NodeMainInstance> main_instance;
|
|
std::string result;
|
|
|
|
{
|
|
const std::vector<intptr_t>& 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> 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<Value> 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<std::string> args,
|
|
const std::vector<std::string> 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<Object> 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<Object> holder,
|
|
int index,
|
|
StartupData payload,
|
|
void* env) {
|
|
per_process::Debug(DebugCategory::MKSNAPSHOT,
|
|
"Deserialize internal field %d of %p, size=%d\n",
|
|
static_cast<int>(index),
|
|
(*holder),
|
|
static_cast<int>(payload.raw_size));
|
|
if (payload.raw_size == 0) {
|
|
holder->SetAlignedPointerInInternalField(index, nullptr);
|
|
return;
|
|
}
|
|
|
|
Environment* env_ptr = static_cast<Environment*>(env);
|
|
const InternalFieldInfo* info =
|
|
reinterpret_cast<const InternalFieldInfo*>(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<Object> holder,
|
|
int index,
|
|
void* env) {
|
|
per_process::Debug(DebugCategory::MKSNAPSHOT,
|
|
"Serialize internal field, index=%d, holder=%p\n",
|
|
static_cast<int>(index),
|
|
*holder);
|
|
void* ptr = holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
|
|
if (ptr == nullptr) {
|
|
return StartupData{nullptr, 0};
|
|
}
|
|
|
|
DCHECK(static_cast<BaseObject*>(ptr)->is_snapshotable());
|
|
SnapshotableObject* obj = static_cast<SnapshotableObject*>(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<int>(info->length));
|
|
return StartupData{reinterpret_cast<const char*>(info),
|
|
static_cast<int>(info->length)};
|
|
}
|
|
|
|
void SerializeBindingData(Environment* env,
|
|
SnapshotCreator* creator,
|
|
EnvSerializeInfo* info) {
|
|
size_t i = 0;
|
|
env->ForEachBindingData([&](FastStringKey key,
|
|
BaseObjectPtr<BaseObject> binding) {
|
|
per_process::Debug(DebugCategory::MKSNAPSHOT,
|
|
"Serialize binding %i, %p, type=%s\n",
|
|
static_cast<int>(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<int>(index));
|
|
info->bindings.push_back({key.c_str(), i, index});
|
|
SnapshotableObject* ptr = static_cast<SnapshotableObject*>(binding.get());
|
|
ptr->PrepareForSerialization(env->context(), creator);
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
i++;
|
|
});
|
|
}
|
|
|
|
} // namespace node
|