Skip to content

Commit e400f8c

Browse files
joyeecheungtargos
authored andcommitted
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 dc3c299 commit e400f8c

7 files changed

+283
-12
lines changed

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

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.

src/env-inl.h

+11
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,17 @@ void Environment::ForEachBaseObject(T&& iterator) {
10891089
}
10901090
}
10911091

1092+
template <typename T>
1093+
void Environment::ForEachBindingData(T&& iterator) {
1094+
BindingDataStore* map = static_cast<BindingDataStore*>(
1095+
context()->GetAlignedPointerFromEmbedderData(
1096+
ContextEmbedderIndex::kBindingListIndex));
1097+
DCHECK_NOT_NULL(map);
1098+
for (auto& it : *map) {
1099+
iterator(it.first, it.second);
1100+
}
1101+
}
1102+
10921103
void Environment::modify_base_object_count(int64_t delta) {
10931104
base_object_count_ += delta;
10941105
}

src/env.cc

+28
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
12611261
EnvSerializeInfo info;
12621262
Local<Context> ctx = context();
12631263

1264+
SerializeBindingData(this, creator, &info);
12641265
// Currently all modules are compiled without cache in builtin snapshot
12651266
// builder.
12661267
info.native_modules = std::vector<std::string>(
@@ -1327,6 +1328,9 @@ std::ostream& operator<<(std::ostream& output,
13271328

13281329
std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
13291330
output << "{\n"
1331+
<< "// -- bindings begins --\n"
1332+
<< i.bindings << ",\n"
1333+
<< "// -- bindings ends --\n"
13301334
<< "// -- native_modules begins --\n"
13311335
<< i.native_modules << ",\n"
13321336
<< "// -- native_modules ends --\n"
@@ -1352,9 +1356,33 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
13521356
return output;
13531357
}
13541358

1359+
void Environment::EnqueueDeserializeRequest(DeserializeRequestCallback cb,
1360+
Local<Object> holder,
1361+
int index,
1362+
InternalFieldInfo* info) {
1363+
DeserializeRequest request{cb, {isolate(), holder}, index, info};
1364+
deserialize_requests_.push_back(std::move(request));
1365+
}
1366+
1367+
void Environment::RunDeserializeRequests() {
1368+
HandleScope scope(isolate());
1369+
Local<Context> ctx = context();
1370+
Isolate* is = isolate();
1371+
while (!deserialize_requests_.empty()) {
1372+
DeserializeRequest request(std::move(deserialize_requests_.front()));
1373+
deserialize_requests_.pop_front();
1374+
Local<Object> holder = request.holder.Get(is);
1375+
request.cb(ctx, holder, request.index, request.info);
1376+
request.holder.Reset();
1377+
request.info->Delete();
1378+
}
1379+
}
1380+
13551381
void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
13561382
Local<Context> ctx = context();
13571383

1384+
RunDeserializeRequests();
1385+
13581386
native_modules_in_snapshot = info->native_modules;
13591387
async_hooks_.Deserialize(ctx);
13601388
immediate_info_.Deserialize(ctx);

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

@@ -1374,6 +1395,9 @@ class Environment : public MemoryRetainer {
13741395
void AddUnmanagedFd(int fd);
13751396
void RemoveUnmanagedFd(int fd);
13761397

1398+
template <typename T>
1399+
void ForEachBindingData(T&& iterator);
1400+
13771401
private:
13781402
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
13791403
const char* errmsg);
@@ -1462,6 +1486,8 @@ class Environment : public MemoryRetainer {
14621486
bool is_in_inspector_console_call_ = false;
14631487
#endif
14641488

1489+
std::list<DeserializeRequest> deserialize_requests_;
1490+
14651491
// handle_wrap_queue_ and req_wrap_queue_ needs to be at a fixed offset from
14661492
// the start of the class because it is used by
14671493
// src/node_postmortem_metadata.cc to calculate offsets and generate debug

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)