Skip to content

Commit 3c8290c

Browse files
committed
src: support serialization of binding data
This patch adds the SnapshotableObject interface. Native objects supporting serialization can inherit from it, implementing PrepareForSerialization(), Serialize() and Deserialize() to control how the native states should be serialized and deserialized. See doc: https://docs.google.com/document/d/15bu038I36oILq5t4Qju1sS2nKudVB6NSGWz00oD48Q8/edit PR-URL: #36943 Fixes: #35930 Refs: #35711 Reviewed-By: James M Snell <[email protected]>
1 parent 05286b9 commit 3c8290c

File tree

7 files changed

+283
-12
lines changed

7 files changed

+283
-12
lines changed

Diff for: src/aliased_buffer.h

+5
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,11 @@ class AliasedBufferBase {
198198
return js_array_.Get(isolate_);
199199
}
200200

201+
void Release() {
202+
DCHECK_NULL(index_);
203+
js_array_.Reset();
204+
}
205+
201206
/**
202207
* Get the underlying v8::ArrayBuffer underlying the TypedArray and
203208
* overlaying the native buffer

Diff for: src/base_object.h

+4
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ class BaseObject : public MemoryRetainer {
158158

159159
virtual inline void OnGCCollect();
160160

161+
bool is_snapshotable() const { return is_snapshotable_; }
162+
void set_is_snapshotable(bool val) { is_snapshotable_ = val; }
163+
161164
private:
162165
v8::Local<v8::Object> WrappedObject() const override;
163166
bool IsRootNode() const override;
@@ -206,6 +209,7 @@ class BaseObject : public MemoryRetainer {
206209

207210
Environment* env_;
208211
PointerData* pointer_data_ = nullptr;
212+
bool is_snapshotable_ = false;
209213
};
210214

211215
// Global alias for FromJSObject() to avoid churn.

Diff for: src/env-inl.h

+11
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,17 @@ void Environment::ForEachBaseObject(T&& iterator) {
10851085
}
10861086
}
10871087

1088+
template <typename T>
1089+
void Environment::ForEachBindingData(T&& iterator) {
1090+
BindingDataStore* map = static_cast<BindingDataStore*>(
1091+
context()->GetAlignedPointerFromEmbedderData(
1092+
ContextEmbedderIndex::kBindingListIndex));
1093+
DCHECK_NOT_NULL(map);
1094+
for (auto& it : *map) {
1095+
iterator(it.first, it.second);
1096+
}
1097+
}
1098+
10881099
void Environment::modify_base_object_count(int64_t delta) {
10891100
base_object_count_ += delta;
10901101
}

Diff for: src/env.cc

+28
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
12511251
EnvSerializeInfo info;
12521252
Local<Context> ctx = context();
12531253

1254+
SerializeBindingData(this, creator, &info);
12541255
// Currently all modules are compiled without cache in builtin snapshot
12551256
// builder.
12561257
info.native_modules = std::vector<std::string>(
@@ -1317,6 +1318,9 @@ std::ostream& operator<<(std::ostream& output,
13171318

13181319
std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
13191320
output << "{\n"
1321+
<< "// -- bindings begins --\n"
1322+
<< i.bindings << ",\n"
1323+
<< "// -- bindings ends --\n"
13201324
<< "// -- native_modules begins --\n"
13211325
<< i.native_modules << ",\n"
13221326
<< "// -- native_modules ends --\n"
@@ -1342,9 +1346,33 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
13421346
return output;
13431347
}
13441348

1349+
void Environment::EnqueueDeserializeRequest(DeserializeRequestCallback cb,
1350+
Local<Object> holder,
1351+
int index,
1352+
InternalFieldInfo* info) {
1353+
DeserializeRequest request{cb, {isolate(), holder}, index, info};
1354+
deserialize_requests_.push_back(std::move(request));
1355+
}
1356+
1357+
void Environment::RunDeserializeRequests() {
1358+
HandleScope scope(isolate());
1359+
Local<Context> ctx = context();
1360+
Isolate* is = isolate();
1361+
while (!deserialize_requests_.empty()) {
1362+
DeserializeRequest request(std::move(deserialize_requests_.front()));
1363+
deserialize_requests_.pop_front();
1364+
Local<Object> holder = request.holder.Get(is);
1365+
request.cb(ctx, holder, request.index, request.info);
1366+
request.holder.Reset();
1367+
request.info->Delete();
1368+
}
1369+
}
1370+
13451371
void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
13461372
Local<Context> ctx = context();
13471373

1374+
RunDeserializeRequests();
1375+
13481376
native_modules_in_snapshot = info->native_modules;
13491377
async_hooks_.Deserialize(ctx);
13501378
immediate_info_.Deserialize(ctx);

Diff for: src/env.h

+26
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "node_main_instance.h"
3838
#include "node_options.h"
3939
#include "node_perf_common.h"
40+
#include "node_snapshotable.h"
4041
#include "req_wrap.h"
4142
#include "util.h"
4243
#include "uv.h"
@@ -899,7 +900,22 @@ struct PropInfo {
899900
SnapshotIndex index; // In the snapshot
900901
};
901902

903+
typedef void (*DeserializeRequestCallback)(v8::Local<v8::Context> context,
904+
v8::Local<v8::Object> holder,
905+
int index,
906+
InternalFieldInfo* info);
907+
struct DeserializeRequest {
908+
DeserializeRequestCallback cb;
909+
v8::Global<v8::Object> holder;
910+
int index;
911+
InternalFieldInfo* info = nullptr; // Owned by the request
912+
913+
// Move constructor
914+
DeserializeRequest(DeserializeRequest&& other) = default;
915+
};
916+
902917
struct EnvSerializeInfo {
918+
std::vector<PropInfo> bindings;
903919
std::vector<std::string> native_modules;
904920
AsyncHooks::SerializeInfo async_hooks;
905921
TickInfo::SerializeInfo tick_info;
@@ -934,6 +950,11 @@ class Environment : public MemoryRetainer {
934950

935951
void PrintAllBaseObjects();
936952
void VerifyNoStrongBaseObjects();
953+
void EnqueueDeserializeRequest(DeserializeRequestCallback cb,
954+
v8::Local<v8::Object> holder,
955+
int index,
956+
InternalFieldInfo* info);
957+
void RunDeserializeRequests();
937958
// Should be called before InitializeInspector()
938959
void InitializeDiagnostics();
939960

@@ -1371,6 +1392,9 @@ class Environment : public MemoryRetainer {
13711392
void AddUnmanagedFd(int fd);
13721393
void RemoveUnmanagedFd(int fd);
13731394

1395+
template <typename T>
1396+
void ForEachBindingData(T&& iterator);
1397+
13741398
private:
13751399
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
13761400
const char* errmsg);
@@ -1459,6 +1483,8 @@ class Environment : public MemoryRetainer {
14591483
bool is_in_inspector_console_call_ = false;
14601484
#endif
14611485

1486+
std::list<DeserializeRequest> deserialize_requests_;
1487+
14621488
// handle_wrap_queue_ and req_wrap_queue_ needs to be at a fixed offset from
14631489
// the start of the class because it is used by
14641490
// src/node_postmortem_metadata.cc to calculate offsets and generate debug

Diff for: src/node_snapshotable.cc

+106-11
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,134 @@
11

22
#include "node_snapshotable.h"
33
#include "base_object-inl.h"
4+
#include "debug_utils-inl.h"
5+
#include "node_file.h"
6+
#include "node_v8.h"
47

58
namespace node {
69

710
using v8::Local;
811
using v8::Object;
12+
using v8::SnapshotCreator;
913
using v8::StartupData;
1014

15+
SnapshotableObject::SnapshotableObject(Environment* env,
16+
Local<Object> wrap,
17+
EmbedderObjectType type)
18+
: BaseObject(env, wrap), type_(type) {
19+
set_is_snapshotable(true);
20+
}
21+
22+
const char* SnapshotableObject::GetTypeNameChars() const {
23+
switch (type_) {
24+
#define V(PropertyName, NativeTypeName) \
25+
case EmbedderObjectType::k_##PropertyName: { \
26+
return NativeTypeName::type_name.c_str(); \
27+
}
28+
SERIALIZABLE_OBJECT_TYPES(V)
29+
#undef V
30+
default: { UNREACHABLE(); }
31+
}
32+
}
33+
34+
bool IsSnapshotableType(FastStringKey key) {
35+
#define V(PropertyName, NativeTypeName) \
36+
if (key == NativeTypeName::type_name) { \
37+
return true; \
38+
}
39+
SERIALIZABLE_OBJECT_TYPES(V)
40+
#undef V
41+
42+
return false;
43+
}
44+
1145
void DeserializeNodeInternalFields(Local<Object> holder,
1246
int index,
13-
v8::StartupData payload,
47+
StartupData payload,
1448
void* env) {
49+
per_process::Debug(DebugCategory::MKSNAPSHOT,
50+
"Deserialize internal field %d of %p, size=%d\n",
51+
static_cast<int>(index),
52+
(*holder),
53+
static_cast<int>(payload.raw_size));
1554
if (payload.raw_size == 0) {
1655
holder->SetAlignedPointerInInternalField(index, nullptr);
1756
return;
1857
}
19-
// No embedder object in the builtin snapshot yet.
20-
UNREACHABLE();
58+
59+
Environment* env_ptr = static_cast<Environment*>(env);
60+
const InternalFieldInfo* info =
61+
reinterpret_cast<const InternalFieldInfo*>(payload.data);
62+
63+
switch (info->type) {
64+
#define V(PropertyName, NativeTypeName) \
65+
case EmbedderObjectType::k_##PropertyName: { \
66+
per_process::Debug(DebugCategory::MKSNAPSHOT, \
67+
"Object %p is %s\n", \
68+
(*holder), \
69+
NativeTypeName::type_name.c_str()); \
70+
env_ptr->EnqueueDeserializeRequest( \
71+
NativeTypeName::Deserialize, holder, index, info->Copy()); \
72+
break; \
73+
}
74+
SERIALIZABLE_OBJECT_TYPES(V)
75+
#undef V
76+
default: { UNREACHABLE(); }
77+
}
2178
}
2279

2380
StartupData SerializeNodeContextInternalFields(Local<Object> holder,
2481
int index,
2582
void* env) {
26-
void* ptr = holder->GetAlignedPointerFromInternalField(index);
27-
if (ptr == nullptr || ptr == env) {
28-
return StartupData{nullptr, 0};
29-
}
30-
if (ptr == env && index == ContextEmbedderIndex::kEnvironment) {
83+
per_process::Debug(DebugCategory::MKSNAPSHOT,
84+
"Serialize internal field, index=%d, holder=%p\n",
85+
static_cast<int>(index),
86+
*holder);
87+
void* ptr = holder->GetAlignedPointerFromInternalField(BaseObject::kSlot);
88+
if (ptr == nullptr) {
3189
return StartupData{nullptr, 0};
3290
}
3391

34-
// No embedder objects in the builtin snapshot yet.
35-
UNREACHABLE();
36-
return StartupData{nullptr, 0};
92+
DCHECK(static_cast<BaseObject*>(ptr)->is_snapshotable());
93+
SnapshotableObject* obj = static_cast<SnapshotableObject*>(ptr);
94+
per_process::Debug(DebugCategory::MKSNAPSHOT,
95+
"Object %p is %s, ",
96+
*holder,
97+
obj->GetTypeNameChars());
98+
InternalFieldInfo* info = obj->Serialize(index);
99+
per_process::Debug(DebugCategory::MKSNAPSHOT,
100+
"payload size=%d\n",
101+
static_cast<int>(info->length));
102+
return StartupData{reinterpret_cast<const char*>(info),
103+
static_cast<int>(info->length)};
104+
}
105+
106+
void SerializeBindingData(Environment* env,
107+
SnapshotCreator* creator,
108+
EnvSerializeInfo* info) {
109+
size_t i = 0;
110+
env->ForEachBindingData([&](FastStringKey key,
111+
BaseObjectPtr<BaseObject> binding) {
112+
per_process::Debug(DebugCategory::MKSNAPSHOT,
113+
"Serialize binding %i, %p, type=%s\n",
114+
static_cast<int>(i),
115+
*(binding->object()),
116+
key.c_str());
117+
118+
if (IsSnapshotableType(key)) {
119+
size_t index = creator->AddData(env->context(), binding->object());
120+
per_process::Debug(DebugCategory::MKSNAPSHOT,
121+
"Serialized with index=%d\n",
122+
static_cast<int>(index));
123+
info->bindings.push_back({key.c_str(), i, index});
124+
SnapshotableObject* ptr = static_cast<SnapshotableObject*>(binding.get());
125+
ptr->PrepareForSerialization(env->context(), creator);
126+
} else {
127+
UNREACHABLE();
128+
}
129+
130+
i++;
131+
});
37132
}
38133

39134
} // namespace node

0 commit comments

Comments
 (0)