Skip to content

Commit c22dc4f

Browse files
joyeecheungjuanarbol
authored andcommitted
src: iterate over base objects to prepare for snapshot
Instead of iterating over the bindings, iterate over the base objects that are snapshottable. This allows us to snapshot base objects that are not bindings. In addition this refactors the InternalFieldInfo class to eliminate potential undefined behaviors, and renames it to InternalFieldInfoBase. The {de}serialize callbacks now expect a InternalFieldInfo struct nested in Snapshotable classes that can be used to carry serialization data around. This allows us to create structs inheriting from InternalFieldInfo for Snapshotable objects that need custom fields. PR-URL: #44192 Refs: #37476 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 4e1c200 commit c22dc4f

13 files changed

+139
-116
lines changed

src/env-inl.h

-11
Original file line numberDiff line numberDiff line change
@@ -801,17 +801,6 @@ void Environment::ForEachBaseObject(T&& iterator) {
801801
cleanup_queue_.ForEachBaseObject(std::forward<T>(iterator));
802802
}
803803

804-
template <typename T>
805-
void Environment::ForEachBindingData(T&& iterator) {
806-
BindingDataStore* map = static_cast<BindingDataStore*>(
807-
context()->GetAlignedPointerFromEmbedderData(
808-
ContextEmbedderIndex::kBindingListIndex));
809-
DCHECK_NOT_NULL(map);
810-
for (auto& it : *map) {
811-
iterator(it.first, it.second);
812-
}
813-
}
814-
815804
void Environment::modify_base_object_count(int64_t delta) {
816805
base_object_count_ += delta;
817806
}

src/env.cc

+8-5
Original file line numberDiff line numberDiff line change
@@ -1664,7 +1664,6 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
16641664
EnvSerializeInfo info;
16651665
Local<Context> ctx = context();
16661666

1667-
SerializeBindingData(this, creator, &info);
16681667
// Currently all modules are compiled without cache in builtin snapshot
16691668
// builder.
16701669
info.builtins = std::vector<std::string>(builtins_without_cache.begin(),
@@ -1692,6 +1691,10 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
16921691
ENVIRONMENT_STRONG_PERSISTENT_VALUES(V)
16931692
#undef V
16941693

1694+
// Do this after other creator->AddData() calls so that Snapshotable objects
1695+
// can use 0 to indicate that a SnapshotIndex is invalid.
1696+
SerializeSnapshotableObjects(this, creator, &info);
1697+
16951698
info.context = creator->AddData(ctx, context());
16961699
return info;
16971700
}
@@ -1724,9 +1727,9 @@ std::ostream& operator<<(std::ostream& output,
17241727

17251728
std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
17261729
output << "{\n"
1727-
<< "// -- bindings begins --\n"
1728-
<< i.bindings << ",\n"
1729-
<< "// -- bindings ends --\n"
1730+
<< "// -- native_objects begins --\n"
1731+
<< i.native_objects << ",\n"
1732+
<< "// -- native_objects ends --\n"
17301733
<< "// -- builtins begins --\n"
17311734
<< i.builtins << ",\n"
17321735
<< "// -- builtins ends --\n"
@@ -1753,7 +1756,7 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
17531756
void Environment::EnqueueDeserializeRequest(DeserializeRequestCallback cb,
17541757
Local<Object> holder,
17551758
int index,
1756-
InternalFieldInfo* info) {
1759+
InternalFieldInfoBase* info) {
17571760
DCHECK_EQ(index, BaseObject::kEmbedderType);
17581761
DeserializeRequest request{cb, {isolate(), holder}, index, info};
17591762
deserialize_requests_.push_back(std::move(request));

src/env.h

+5-8
Original file line numberDiff line numberDiff line change
@@ -926,19 +926,19 @@ class ShouldNotAbortOnUncaughtScope {
926926
typedef void (*DeserializeRequestCallback)(v8::Local<v8::Context> context,
927927
v8::Local<v8::Object> holder,
928928
int index,
929-
InternalFieldInfo* info);
929+
InternalFieldInfoBase* info);
930930
struct DeserializeRequest {
931931
DeserializeRequestCallback cb;
932932
v8::Global<v8::Object> holder;
933933
int index;
934-
InternalFieldInfo* info = nullptr; // Owned by the request
934+
InternalFieldInfoBase* info = nullptr; // Owned by the request
935935

936936
// Move constructor
937937
DeserializeRequest(DeserializeRequest&& other) = default;
938938
};
939939

940940
struct EnvSerializeInfo {
941-
std::vector<PropInfo> bindings;
941+
std::vector<PropInfo> native_objects;
942942
std::vector<std::string> builtins;
943943
AsyncHooks::SerializeInfo async_hooks;
944944
TickInfo::SerializeInfo tick_info;
@@ -1033,7 +1033,7 @@ class Environment : public MemoryRetainer {
10331033
void EnqueueDeserializeRequest(DeserializeRequestCallback cb,
10341034
v8::Local<v8::Object> holder,
10351035
int index,
1036-
InternalFieldInfo* info);
1036+
InternalFieldInfoBase* info);
10371037
void RunDeserializeRequests();
10381038
// Should be called before InitializeInspector()
10391039
void InitializeDiagnostics();
@@ -1455,7 +1455,7 @@ class Environment : public MemoryRetainer {
14551455
void RemoveUnmanagedFd(int fd);
14561456

14571457
template <typename T>
1458-
void ForEachBindingData(T&& iterator);
1458+
void ForEachBaseObject(T&& iterator);
14591459

14601460
inline void set_heap_snapshot_near_heap_limit(uint32_t limit);
14611461
inline bool is_in_heapsnapshot_heap_limit_callback() const;
@@ -1615,9 +1615,6 @@ class Environment : public MemoryRetainer {
16151615
std::function<void(Environment*, int)> process_exit_handler_ {
16161616
DefaultProcessExitHandler };
16171617

1618-
template <typename T>
1619-
void ForEachBaseObject(T&& iterator);
1620-
16211618
#define V(PropertyName, TypeName) v8::Global<TypeName> PropertyName ## _;
16221619
ENVIRONMENT_STRONG_PERSISTENT_VALUES(V)
16231620
#undef V

src/node_blob.cc

+12-10
Original file line numberDiff line numberDiff line change
@@ -460,11 +460,10 @@ BlobBindingData::StoredDataObject BlobBindingData::get_data_object(
460460
return entry->second;
461461
}
462462

463-
void BlobBindingData::Deserialize(
464-
Local<Context> context,
465-
Local<Object> holder,
466-
int index,
467-
InternalFieldInfo* info) {
463+
void BlobBindingData::Deserialize(Local<Context> context,
464+
Local<Object> holder,
465+
int index,
466+
InternalFieldInfoBase* info) {
468467
DCHECK_EQ(index, BaseObject::kEmbedderType);
469468
HandleScope scope(context->GetIsolate());
470469
Environment* env = Environment::GetCurrent(context);
@@ -473,15 +472,18 @@ void BlobBindingData::Deserialize(
473472
CHECK_NOT_NULL(binding);
474473
}
475474

476-
void BlobBindingData::PrepareForSerialization(
477-
Local<Context> context,
478-
v8::SnapshotCreator* creator) {
475+
bool BlobBindingData::PrepareForSerialization(Local<Context> context,
476+
v8::SnapshotCreator* creator) {
479477
// Stored blob objects are not actually persisted.
478+
// Return true because we need to maintain the reference to the binding from
479+
// JS land.
480+
return true;
480481
}
481482

482-
InternalFieldInfo* BlobBindingData::Serialize(int index) {
483+
InternalFieldInfoBase* BlobBindingData::Serialize(int index) {
483484
DCHECK_EQ(index, BaseObject::kEmbedderType);
484-
InternalFieldInfo* info = InternalFieldInfo::New(type());
485+
InternalFieldInfo* info =
486+
InternalFieldInfoBase::New<InternalFieldInfo>(type());
485487
return info;
486488
}
487489

src/node_blob.h

+2
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ class BlobBindingData : public SnapshotableObject {
143143
public:
144144
explicit BlobBindingData(Environment* env, v8::Local<v8::Object> wrap);
145145

146+
using InternalFieldInfo = InternalFieldInfoBase;
147+
146148
SERIALIZABLE_OBJECT_METHODS()
147149

148150
static constexpr FastStringKey type_name{"node::BlobBindingData"};

src/node_file.cc

+8-4
Original file line numberDiff line numberDiff line change
@@ -2566,26 +2566,30 @@ BindingData::BindingData(Environment* env, v8::Local<v8::Object> wrap)
25662566
void BindingData::Deserialize(Local<Context> context,
25672567
Local<Object> holder,
25682568
int index,
2569-
InternalFieldInfo* info) {
2569+
InternalFieldInfoBase* info) {
25702570
DCHECK_EQ(index, BaseObject::kEmbedderType);
25712571
HandleScope scope(context->GetIsolate());
25722572
Environment* env = Environment::GetCurrent(context);
25732573
BindingData* binding = env->AddBindingData<BindingData>(context, holder);
25742574
CHECK_NOT_NULL(binding);
25752575
}
25762576

2577-
void BindingData::PrepareForSerialization(Local<Context> context,
2577+
bool BindingData::PrepareForSerialization(Local<Context> context,
25782578
v8::SnapshotCreator* creator) {
25792579
CHECK(file_handle_read_wrap_freelist.empty());
25802580
// We'll just re-initialize the buffers in the constructor since their
25812581
// contents can be thrown away once consumed in the previous call.
25822582
stats_field_array.Release();
25832583
stats_field_bigint_array.Release();
2584+
// Return true because we need to maintain the reference to the binding from
2585+
// JS land.
2586+
return true;
25842587
}
25852588

2586-
InternalFieldInfo* BindingData::Serialize(int index) {
2589+
InternalFieldInfoBase* BindingData::Serialize(int index) {
25872590
DCHECK_EQ(index, BaseObject::kEmbedderType);
2588-
InternalFieldInfo* info = InternalFieldInfo::New(type());
2591+
InternalFieldInfo* info =
2592+
InternalFieldInfoBase::New<InternalFieldInfo>(type());
25892593
return info;
25902594
}
25912595

src/node_file.h

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class BindingData : public SnapshotableObject {
2323
std::vector<BaseObjectPtr<FileHandleReadWrap>>
2424
file_handle_read_wrap_freelist;
2525

26+
using InternalFieldInfo = InternalFieldInfoBase;
2627
SERIALIZABLE_OBJECT_METHODS()
2728
static constexpr FastStringKey type_name{"node::fs::BindingData"};
2829
static constexpr EmbedderObjectType type_int =

src/node_process.h

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class BindingData : public SnapshotableObject {
5050
void AddMethods();
5151
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
5252

53+
using InternalFieldInfo = InternalFieldInfoBase;
54+
5355
SERIALIZABLE_OBJECT_METHODS()
5456
static constexpr FastStringKey type_name{"node::process::BindingData"};
5557
static constexpr EmbedderObjectType type_int =

src/node_process_methods.cc

+8-4
Original file line numberDiff line numberDiff line change
@@ -524,23 +524,27 @@ void BindingData::SlowNumber(const v8::FunctionCallbackInfo<v8::Value>& args) {
524524
NumberImpl(FromJSObject<BindingData>(args.Holder()));
525525
}
526526

527-
void BindingData::PrepareForSerialization(Local<Context> context,
527+
bool BindingData::PrepareForSerialization(Local<Context> context,
528528
v8::SnapshotCreator* creator) {
529529
// It's not worth keeping.
530530
// Release it, we will recreate it when the instance is dehydrated.
531531
array_buffer_.Reset();
532+
// Return true because we need to maintain the reference to the binding from
533+
// JS land.
534+
return true;
532535
}
533536

534-
InternalFieldInfo* BindingData::Serialize(int index) {
537+
InternalFieldInfoBase* BindingData::Serialize(int index) {
535538
DCHECK_EQ(index, BaseObject::kEmbedderType);
536-
InternalFieldInfo* info = InternalFieldInfo::New(type());
539+
InternalFieldInfo* info =
540+
InternalFieldInfoBase::New<InternalFieldInfo>(type());
537541
return info;
538542
}
539543

540544
void BindingData::Deserialize(Local<Context> context,
541545
Local<Object> holder,
542546
int index,
543-
InternalFieldInfo* info) {
547+
InternalFieldInfoBase* info) {
544548
DCHECK_EQ(index, BaseObject::kEmbedderType);
545549
v8::HandleScope scope(context->GetIsolate());
546550
Environment* env = Environment::GetCurrent(context);

src/node_snapshotable.cc

+46-40
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ template <>
640640
EnvSerializeInfo FileReader::Read() {
641641
per_process::Debug(DebugCategory::MKSNAPSHOT, "Read<EnvSerializeInfo>()\n");
642642
EnvSerializeInfo result;
643-
result.bindings = ReadVector<PropInfo>();
643+
result.native_objects = ReadVector<PropInfo>();
644644
result.builtins = ReadVector<std::string>();
645645
result.async_hooks = Read<AsyncHooks::SerializeInfo>();
646646
result.tick_info = Read<TickInfo::SerializeInfo>();
@@ -663,7 +663,7 @@ size_t FileWriter::Write(const EnvSerializeInfo& data) {
663663
}
664664

665665
// Use += here to ensure order of evaluation.
666-
size_t written_total = WriteVector<PropInfo>(data.bindings);
666+
size_t written_total = WriteVector<PropInfo>(data.native_objects);
667667
written_total += WriteVector<std::string>(data.builtins);
668668
written_total += Write<AsyncHooks::SerializeInfo>(data.async_hooks);
669669
written_total += Write<TickInfo::SerializeInfo>(data.tick_info);
@@ -1209,17 +1209,6 @@ const char* SnapshotableObject::GetTypeNameChars() const {
12091209
}
12101210
}
12111211

1212-
bool IsSnapshotableType(FastStringKey key) {
1213-
#define V(PropertyName, NativeTypeName) \
1214-
if (key == NativeTypeName::type_name) { \
1215-
return true; \
1216-
}
1217-
SERIALIZABLE_OBJECT_TYPES(V)
1218-
#undef V
1219-
1220-
return false;
1221-
}
1222-
12231212
void DeserializeNodeInternalFields(Local<Object> holder,
12241213
int index,
12251214
StartupData payload,
@@ -1242,10 +1231,10 @@ void DeserializeNodeInternalFields(Local<Object> holder,
12421231
DCHECK_EQ(index, BaseObject::kEmbedderType);
12431232

12441233
Environment* env_ptr = static_cast<Environment*>(env);
1245-
const InternalFieldInfo* info =
1246-
reinterpret_cast<const InternalFieldInfo*>(payload.data);
1234+
const InternalFieldInfoBase* info =
1235+
reinterpret_cast<const InternalFieldInfoBase*>(payload.data);
12471236
// TODO(joyeecheung): we can add a constant kNodeEmbedderId to the
1248-
// beginning of every InternalFieldInfo to ensure that we don't
1237+
// beginning of every InternalFieldInfoBase to ensure that we don't
12491238
// step on payloads that were not serialized by Node.js.
12501239
switch (info->type) {
12511240
#define V(PropertyName, NativeTypeName) \
@@ -1255,12 +1244,25 @@ void DeserializeNodeInternalFields(Local<Object> holder,
12551244
(*holder), \
12561245
NativeTypeName::type_name.c_str()); \
12571246
env_ptr->EnqueueDeserializeRequest( \
1258-
NativeTypeName::Deserialize, holder, index, info->Copy()); \
1247+
NativeTypeName::Deserialize, \
1248+
holder, \
1249+
index, \
1250+
info->Copy<NativeTypeName::InternalFieldInfo>()); \
12591251
break; \
12601252
}
12611253
SERIALIZABLE_OBJECT_TYPES(V)
12621254
#undef V
1263-
default: { UNREACHABLE(); }
1255+
default: {
1256+
// This should only be reachable during development when trying to
1257+
// deserialize a snapshot blob built by a version of Node.js that
1258+
// has more recognizable EmbedderObjectTypes than the deserializing
1259+
// Node.js binary.
1260+
fprintf(stderr,
1261+
"Unknown embedder object type %" PRIu8 ", possibly caused by "
1262+
"mismatched Node.js versions\n",
1263+
static_cast<uint8_t>(info->type));
1264+
ABORT();
1265+
}
12641266
}
12651267
}
12661268

@@ -1293,17 +1295,17 @@ StartupData SerializeNodeContextInternalFields(Local<Object> holder,
12931295
static_cast<int>(index),
12941296
*holder);
12951297

1296-
void* binding_ptr =
1298+
void* native_ptr =
12971299
holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
1298-
per_process::Debug(DebugCategory::MKSNAPSHOT, "binding = %p\n", binding_ptr);
1299-
DCHECK(static_cast<BaseObject*>(binding_ptr)->is_snapshotable());
1300-
SnapshotableObject* obj = static_cast<SnapshotableObject*>(binding_ptr);
1300+
per_process::Debug(DebugCategory::MKSNAPSHOT, "native = %p\n", native_ptr);
1301+
DCHECK(static_cast<BaseObject*>(native_ptr)->is_snapshotable());
1302+
SnapshotableObject* obj = static_cast<SnapshotableObject*>(native_ptr);
13011303

13021304
per_process::Debug(DebugCategory::MKSNAPSHOT,
13031305
"Object %p is %s, ",
13041306
*holder,
13051307
obj->GetTypeNameChars());
1306-
InternalFieldInfo* info = obj->Serialize(index);
1308+
InternalFieldInfoBase* info = obj->Serialize(index);
13071309

13081310
per_process::Debug(DebugCategory::MKSNAPSHOT,
13091311
"payload size=%d\n",
@@ -1312,31 +1314,35 @@ StartupData SerializeNodeContextInternalFields(Local<Object> holder,
13121314
static_cast<int>(info->length)};
13131315
}
13141316

1315-
void SerializeBindingData(Environment* env,
1316-
SnapshotCreator* creator,
1317-
EnvSerializeInfo* info) {
1317+
void SerializeSnapshotableObjects(Environment* env,
1318+
SnapshotCreator* creator,
1319+
EnvSerializeInfo* info) {
13181320
uint32_t i = 0;
1319-
env->ForEachBindingData([&](FastStringKey key,
1320-
BaseObjectPtr<BaseObject> binding) {
1321+
env->ForEachBaseObject([&](BaseObject* obj) {
1322+
// If there are any BaseObjects that are not snapshotable left
1323+
// during context serialization, V8 would crash due to unregistered
1324+
// global handles and print detailed information about them.
1325+
if (!obj->is_snapshotable()) {
1326+
return;
1327+
}
1328+
SnapshotableObject* ptr = static_cast<SnapshotableObject*>(obj);
1329+
1330+
const char* type_name = ptr->GetTypeNameChars();
13211331
per_process::Debug(DebugCategory::MKSNAPSHOT,
1322-
"Serialize binding %i (%p), object=%p, type=%s\n",
1332+
"Serialize snapshotable object %i (%p), "
1333+
"object=%p, type=%s\n",
13231334
static_cast<int>(i),
1324-
binding.get(),
1325-
*(binding->object()),
1326-
key.c_str());
1335+
ptr,
1336+
*(ptr->object()),
1337+
type_name);
13271338

1328-
if (IsSnapshotableType(key)) {
1329-
SnapshotIndex index = creator->AddData(env->context(), binding->object());
1339+
if (ptr->PrepareForSerialization(env->context(), creator)) {
1340+
SnapshotIndex index = creator->AddData(env->context(), obj->object());
13301341
per_process::Debug(DebugCategory::MKSNAPSHOT,
13311342
"Serialized with index=%d\n",
13321343
static_cast<int>(index));
1333-
info->bindings.push_back({key.c_str(), i, index});
1334-
SnapshotableObject* ptr = static_cast<SnapshotableObject*>(binding.get());
1335-
ptr->PrepareForSerialization(env->context(), creator);
1336-
} else {
1337-
UNREACHABLE();
1344+
info->native_objects.push_back({type_name, i, index});
13381345
}
1339-
13401346
i++;
13411347
});
13421348
}

0 commit comments

Comments
 (0)