mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Put the hrtime backing store in the process methods binding data so that it can be integrated into the snapshot builder. For now we simply discard the contents of the hrtime buffer during serialization and create new buffers upon deserialization because the contents are only useful in a synchronous call. This also moves the helper function for creating V8 FastAPI methods into `Environment::SetFastMethod()` for code reuse. The v8::CFunction need to be created before the Environment is created so that we have the CTypeInfo address available for external reference registration. PR-URL: https://github.com/nodejs/node/pull/40649 Refs: https://github.com/nodejs/node/issues/35711 Refs: https://github.com/nodejs/node/issues/37476 Reviewed-By: James M Snell <jasnell@gmail.com>
316 lines
10 KiB
C++
316 lines
10 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_blob.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_process.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);
|
|
|
|
// We must be able to rehash the blob when we restore it or otherwise
|
|
// the hash seed would be fixed by V8, introducing a vulnerability.
|
|
CHECK(out->blob.CanBeRehashed());
|
|
|
|
// We cannot resurrect the handles from the snapshot, so make sure that
|
|
// no handles are left open in the environment after the blob is created
|
|
// (which should trigger a GC and close all handles that can be closed).
|
|
if (!env->req_wrap_queue()->IsEmpty()
|
|
|| !env->handle_wrap_queue()->IsEmpty()
|
|
|| per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
|
|
PrintLibuvHandleInformation(env->event_loop(), stderr);
|
|
}
|
|
CHECK(env->req_wrap_queue()->IsEmpty());
|
|
CHECK(env->handle_wrap_queue()->IsEmpty());
|
|
|
|
// 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
|