Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: serialize both BaseObject slots #48996

Merged
merged 1 commit into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/encoding_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ bool BindingData::PrepareForSerialization(Local<Context> context,
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info = internal_field_info_;
internal_field_info_ = nullptr;
return info;
Expand All @@ -72,7 +72,7 @@ void BindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
v8::HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
// Recreate the buffer in the constructor.
Expand Down
3 changes: 2 additions & 1 deletion src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "node_options-inl.h"
#include "node_process-inl.h"
#include "node_shadow_realm.h"
#include "node_snapshotable.h"
#include "node_v8_platform-inl.h"
#include "node_worker.h"
#include "req_wrap-inl.h"
Expand Down Expand Up @@ -1735,7 +1736,7 @@ void Environment::EnqueueDeserializeRequest(DeserializeRequestCallback cb,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
DeserializeRequest request{cb, {isolate(), holder}, index, info};
deserialize_requests_.push_back(std::move(request));
}
Expand Down
4 changes: 2 additions & 2 deletions src/node_blob.cc
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ void BlobBindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
BlobBindingData* binding = realm->AddBindingData<BlobBindingData>(holder);
Expand All @@ -548,7 +548,7 @@ bool BlobBindingData::PrepareForSerialization(Local<Context> context,
}

InternalFieldInfoBase* BlobBindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info =
InternalFieldInfoBase::New<InternalFieldInfo>(type());
return info;
Expand Down
4 changes: 2 additions & 2 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,7 @@ void BindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
InternalFieldInfo* casted_info = static_cast<InternalFieldInfo*>(info);
Expand Down Expand Up @@ -3141,7 +3141,7 @@ bool BindingData::PrepareForSerialization(Local<Context> context,
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info = internal_field_info_;
internal_field_info_ = nullptr;
return info;
Expand Down
4 changes: 2 additions & 2 deletions src/node_process_methods.cc
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ bool BindingData::PrepareForSerialization(Local<Context> context,
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info =
InternalFieldInfoBase::New<InternalFieldInfo>(type());
return info;
Expand All @@ -562,7 +562,7 @@ void BindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
v8::HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
// Recreate the buffer in the constructor.
Expand Down
80 changes: 56 additions & 24 deletions src/node_snapshotable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1107,25 +1107,33 @@ std::string SnapshotableObject::GetTypeName() const {
void DeserializeNodeInternalFields(Local<Object> holder,
int index,
StartupData payload,
void* env) {
void* callback_data) {
if (payload.raw_size == 0) {
holder->SetAlignedPointerInInternalField(index, nullptr);
return;
}

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);
Environment* env = static_cast<Environment*>(callback_data);

// To deserialize the first field, check the type and re-tag the object.
if (index == BaseObject::kEmbedderType) {
int size = sizeof(EmbedderTypeInfo);
DCHECK_EQ(payload.raw_size, size);
EmbedderTypeInfo read_data;
memcpy(&read_data, payload.data, size);
// For now we only support non-cppgc objects.
CHECK_EQ(read_data.mode, EmbedderTypeInfo::MemoryMode::kBaseObject);
BaseObject::TagBaseObject(env->isolate_data(), holder);
return;
}

DCHECK_EQ(index, BaseObject::kEmbedderType);

Environment* env_ptr = static_cast<Environment*>(env);
// To deserialize the second field, enqueue a deserialize request.
DCHECK_IS_SNAPSHOT_SLOT(index);
const InternalFieldInfoBase* info =
reinterpret_cast<const InternalFieldInfoBase*>(payload.data);
// TODO(joyeecheung): we can add a constant kNodeEmbedderId to the
Expand All @@ -1138,7 +1146,7 @@ void DeserializeNodeInternalFields(Local<Object> holder,
"Object %p is %s\n", \
(*holder), \
#NativeTypeName); \
env_ptr->EnqueueDeserializeRequest( \
env->EnqueueDeserializeRequest( \
NativeTypeName::Deserialize, \
holder, \
index, \
Expand All @@ -1164,28 +1172,52 @@ void DeserializeNodeInternalFields(Local<Object> holder,
StartupData SerializeNodeContextInternalFields(Local<Object> holder,
int index,
void* callback_data) {
// We only do one serialization for the kEmbedderType slot, the result
// contains everything necessary for deserializing the entire object,
// including the fields whose index is bigger than kEmbedderType
// (most importantly, BaseObject::kSlot).
// For Node.js this design is enough for all the native binding that are
// serializable.
// For the moment we do not set any internal fields in ArrayBuffer
// or ArrayBufferViews, so just return nullptr.
if (holder->IsArrayBuffer() || holder->IsArrayBufferView()) {
CHECK_NULL(holder->GetAlignedPointerFromInternalField(index));
return StartupData{nullptr, 0};
}

// Use the V8 convention and serialize unknown objects verbatim.
Environment* env = static_cast<Environment*>(callback_data);
if (index != BaseObject::kEmbedderType ||
!BaseObject::IsBaseObject(env->isolate_data(), holder)) {
if (!BaseObject::IsBaseObject(env->isolate_data(), holder)) {
per_process::Debug(DebugCategory::MKSNAPSHOT,
"Serialize unknown object, index=%d, holder=%p\n",
static_cast<int>(index),
*holder);
return StartupData{nullptr, 0};
}

per_process::Debug(DebugCategory::MKSNAPSHOT,
"Serialize internal field, index=%d, holder=%p\n",
"Serialize BaseObject, index=%d, holder=%p\n",
static_cast<int>(index),
*holder);

void* native_ptr =
holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
per_process::Debug(DebugCategory::MKSNAPSHOT, "native = %p\n", native_ptr);
DCHECK(static_cast<BaseObject*>(native_ptr)->is_snapshotable());
SnapshotableObject* obj = static_cast<SnapshotableObject*>(native_ptr);
BaseObject* object_ptr = static_cast<BaseObject*>(
holder->GetAlignedPointerFromInternalField(BaseObject::kSlot));
// If the native object is already set to null, ignore it.
if (object_ptr == nullptr) {
return StartupData{nullptr, 0};
}

DCHECK(object_ptr->is_snapshotable());
SnapshotableObject* obj = static_cast<SnapshotableObject*>(object_ptr);

// To serialize the type field, save data in a EmbedderTypeInfo.
if (index == BaseObject::kEmbedderType) {
int size = sizeof(EmbedderTypeInfo);
char* data = new char[size];
// We need to use placement new because V8 calls delete[] on the returned
// data.
// TODO(joyeecheung): support cppgc objects.
new (data) EmbedderTypeInfo(obj->type(),
EmbedderTypeInfo::MemoryMode::kBaseObject);
return StartupData{data, size};
}

// To serialize the slot field, invoke Serialize() method on the object.
DCHECK_IS_SNAPSHOT_SLOT(index);

per_process::Debug(DebugCategory::MKSNAPSHOT,
"Object %p is %s, ",
Expand Down Expand Up @@ -1341,7 +1373,7 @@ bool BindingData::PrepareForSerialization(Local<Context> context,
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info = internal_field_info_;
internal_field_info_ = nullptr;
return info;
Expand All @@ -1351,7 +1383,7 @@ void BindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
v8::HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
// Recreate the buffer in the constructor.
Expand Down
10 changes: 10 additions & 0 deletions src/node_snapshotable.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ struct InternalFieldInfoBase {
InternalFieldInfoBase() = default;
};

struct EmbedderTypeInfo {
enum class MemoryMode : uint8_t { kBaseObject, kCppGC };
EmbedderTypeInfo(EmbedderObjectType t, MemoryMode m) : type(t), mode(m) {}
EmbedderTypeInfo() = default;
EmbedderObjectType type;
MemoryMode mode;
};

// An interface for snapshotable native objects to inherit from.
// Use the SERIALIZABLE_OBJECT_METHODS() macro in the class to define
// the following methods to implement:
Expand Down Expand Up @@ -123,6 +131,8 @@ void SerializeSnapshotableObjects(Realm* realm,
v8::SnapshotCreator* creator,
RealmSerializeInfo* info);

#define DCHECK_IS_SNAPSHOT_SLOT(index) DCHECK_EQ(index, BaseObject::kSlot)

namespace mksnapshot {
class BindingData : public SnapshotableObject {
public:
Expand Down
4 changes: 2 additions & 2 deletions src/node_url.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ bool BindingData::PrepareForSerialization(v8::Local<v8::Context> context,
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info =
InternalFieldInfoBase::New<InternalFieldInfo>(type());
return info;
Expand All @@ -64,7 +64,7 @@ void BindingData::Deserialize(v8::Local<v8::Context> context,
v8::Local<v8::Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
v8::HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
BindingData* binding = realm->AddBindingData<BindingData>(holder);
Expand Down
4 changes: 2 additions & 2 deletions src/node_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ bool WeakReference::PrepareForSerialization(Local<Context> context,
}

InternalFieldInfoBase* WeakReference::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info =
InternalFieldInfoBase::New<InternalFieldInfo>(type());
info->target = target_index_;
Expand All @@ -238,7 +238,7 @@ void WeakReference::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
HandleScope scope(context->GetIsolate());

InternalFieldInfo* weak_info = reinterpret_cast<InternalFieldInfo*>(info);
Expand Down
4 changes: 2 additions & 2 deletions src/node_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ void BindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
// Recreate the buffer in the constructor.
Expand All @@ -163,7 +163,7 @@ void BindingData::Deserialize(Local<Context> context,
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info = internal_field_info_;
internal_field_info_ = nullptr;
return info;
Expand Down
4 changes: 2 additions & 2 deletions src/timers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ bool BindingData::PrepareForSerialization(Local<Context> context,
}

InternalFieldInfoBase* BindingData::Serialize(int index) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
InternalFieldInfo* info =
InternalFieldInfoBase::New<InternalFieldInfo>(type());
return info;
Expand All @@ -104,7 +104,7 @@ void BindingData::Deserialize(Local<Context> context,
Local<Object> holder,
int index,
InternalFieldInfoBase* info) {
DCHECK_EQ(index, BaseObject::kEmbedderType);
DCHECK_IS_SNAPSHOT_SLOT(index);
v8::HandleScope scope(context->GetIsolate());
Realm* realm = Realm::GetCurrent(context);
// Recreate the buffer in the constructor.
Expand Down