Skip to content

Commit e9d9076

Browse files
committed
src: support fs and v8 binding data in snapshot
1 parent 4e833b6 commit e9d9076

33 files changed

+694
-193
lines changed

lib/fs.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ const {
6767

6868
const pathModule = require('path');
6969
const { isArrayBufferView } = require('internal/util/types');
70+
71+
// We need to get the statValues from the binding at the callsite since
72+
// it's re-initialized after deserialization.
73+
7074
const binding = internalBinding('fs');
7175
const { Buffer } = require('buffer');
7276
const {
@@ -82,7 +86,7 @@ const {
8286
uvException
8387
} = require('internal/errors');
8488

85-
const { FSReqCallback, statValues } = binding;
89+
const { FSReqCallback } = binding;
8690
const { toPathIfFileURL } = require('internal/url');
8791
const internalUtil = require('internal/util');
8892
const {
@@ -1781,8 +1785,8 @@ function realpathSync(p, options) {
17811785

17821786
// Continue if not a symlink, break if a pipe/socket
17831787
if (knownHard[base] || cache?.get(base) === base) {
1784-
if (isFileType(statValues, S_IFIFO) ||
1785-
isFileType(statValues, S_IFSOCK)) {
1788+
if (isFileType(binding.statValues, S_IFIFO) ||
1789+
isFileType(binding.statValues, S_IFSOCK)) {
17861790
break;
17871791
}
17881792
continue;
@@ -1924,8 +1928,8 @@ function realpath(p, options, callback) {
19241928

19251929
// Continue if not a symlink, break if a pipe/socket
19261930
if (knownHard[base]) {
1927-
if (isFileType(statValues, S_IFIFO) ||
1928-
isFileType(statValues, S_IFSOCK)) {
1931+
if (isFileType(binding.statValues, S_IFIFO) ||
1932+
isFileType(binding.statValues, S_IFSOCK)) {
19291933
return callback(null, encodeRealpathResult(p, options));
19301934
}
19311935
return process.nextTick(LOOP);

lib/internal/bootstrap/node.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ process.emitWarning = emitWarning;
347347
// Note: only after this point are the timers effective
348348
}
349349

350+
require('fs');
351+
internalBinding('v8');
352+
350353
function setupPrepareStackTrace() {
351354
const {
352355
setEnhanceStackForFatalException,

lib/v8.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,13 @@ function getHeapSnapshot() {
7272
return new HeapSnapshotStream(handle);
7373
}
7474

75+
// We need to get the buffer from the binding at the callsite since
76+
// it's re-initialized after deserialization.
77+
const binding = internalBinding('v8');
78+
7579
const {
7680
cachedDataVersionTag,
7781
setFlagsFromString: _setFlagsFromString,
78-
heapStatisticsBuffer,
79-
heapSpaceStatisticsBuffer,
80-
heapCodeStatisticsBuffer,
8182
updateHeapStatisticsBuffer,
8283
updateHeapSpaceStatisticsBuffer,
8384
updateHeapCodeStatisticsBuffer,
@@ -106,7 +107,7 @@ const {
106107
kCodeAndMetadataSizeIndex,
107108
kBytecodeAndMetadataSizeIndex,
108109
kExternalScriptSourceSizeIndex
109-
} = internalBinding('v8');
110+
} = binding;
110111

111112
const kNumberOfHeapSpaces = kHeapSpaces.length;
112113

@@ -116,7 +117,7 @@ function setFlagsFromString(flags) {
116117
}
117118

118119
function getHeapStatistics() {
119-
const buffer = heapStatisticsBuffer;
120+
const buffer = binding.heapStatisticsBuffer;
120121

121122
updateHeapStatisticsBuffer();
122123

@@ -137,7 +138,7 @@ function getHeapStatistics() {
137138

138139
function getHeapSpaceStatistics() {
139140
const heapSpaceStatistics = new Array(kNumberOfHeapSpaces);
140-
const buffer = heapSpaceStatisticsBuffer;
141+
const buffer = binding.heapSpaceStatisticsBuffer;
141142

142143
for (let i = 0; i < kNumberOfHeapSpaces; i++) {
143144
updateHeapSpaceStatisticsBuffer(i);
@@ -154,7 +155,7 @@ function getHeapSpaceStatistics() {
154155
}
155156

156157
function getHeapCodeStatistics() {
157-
const buffer = heapCodeStatisticsBuffer;
158+
const buffer = binding.heapCodeStatisticsBuffer;
158159

159160
updateHeapCodeStatisticsBuffer();
160161
return {

node.gyp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@
641641
'src/node_report_module.cc',
642642
'src/node_report_utils.cc',
643643
'src/node_serdes.cc',
644+
'src/node_serializable.cc',
644645
'src/node_sockaddr.cc',
645646
'src/node_stat_watcher.cc',
646647
'src/node_symbols.cc',
@@ -743,12 +744,14 @@
743744
'src/node_report.h',
744745
'src/node_revert.h',
745746
'src/node_root_certs.h',
747+
'src/node_serializable.h',
746748
'src/node_sockaddr.h',
747749
'src/node_sockaddr-inl.h',
748750
'src/node_stat_watcher.h',
749751
'src/node_union_bytes.h',
750752
'src/node_url.h',
751753
'src/node_version.h',
754+
'src/node_v8.h',
752755
'src/node_v8_platform-inl.h',
753756
'src/node_wasi.h',
754757
'src/node_watchdog.h',

src/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ that state is through the use of `Environment::AddBindingData`, which gives
422422
binding functions access to an object for storing such state.
423423
That object is always a [`BaseObject`][].
424424
425-
Its class needs to have a static `binding_data_name` field based on a
425+
Its class needs to have a static `type_name` field based on a
426426
constant string, in order to disambiguate it from other classes of this type,
427427
and which could e.g. match the binding’s name (in the example above, that would
428428
be `cares_wrap`).
@@ -433,7 +433,7 @@ class BindingData : public BaseObject {
433433
public:
434434
BindingData(Environment* env, Local<Object> obj) : BaseObject(env, obj) {}
435435
436-
static constexpr FastStringKey binding_data_name { "http_parser" };
436+
static constexpr FastStringKey type_name { "http_parser" };
437437
438438
std::vector<char> parser_buffer;
439439
bool parser_buffer_in_use = false;

src/aliased_buffer.h

Lines changed: 5 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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_serializable() const { return is_serializable_; }
162+
void set_is_serializable(bool val) { is_serializable_ = 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_serializable_ = false;
209213
};
210214

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

src/env-inl.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ inline T* Environment::GetBindingData(v8::Local<v8::Context> context) {
358358
context->GetAlignedPointerFromEmbedderData(
359359
ContextEmbedderIndex::kBindingListIndex));
360360
DCHECK_NOT_NULL(map);
361-
auto it = map->find(T::binding_data_name);
361+
auto it = map->find(T::type_name);
362362
if (UNLIKELY(it == map->end())) return nullptr;
363363
T* result = static_cast<T*>(it->second.get());
364364
DCHECK_NOT_NULL(result);
@@ -377,7 +377,7 @@ inline T* Environment::AddBindingData(
377377
context->GetAlignedPointerFromEmbedderData(
378378
ContextEmbedderIndex::kBindingListIndex));
379379
DCHECK_NOT_NULL(map);
380-
auto result = map->emplace(T::binding_data_name, item);
380+
auto result = map->emplace(T::type_name, item);
381381
CHECK(result.second);
382382
DCHECK_EQ(GetBindingData<T>(context), item.get());
383383
return item.get();
@@ -1081,6 +1081,17 @@ void Environment::ForEachBaseObject(T&& iterator) {
10811081
}
10821082
}
10831083

1084+
template <typename T>
1085+
void Environment::ForEachBindingData(T&& iterator) {
1086+
BindingDataStore* map = static_cast<BindingDataStore*>(
1087+
context()->GetAlignedPointerFromEmbedderData(
1088+
ContextEmbedderIndex::kBindingListIndex));
1089+
DCHECK_NOT_NULL(map);
1090+
for (auto& it : *map) {
1091+
iterator(it.first, it.second);
1092+
}
1093+
}
1094+
10841095
void Environment::modify_base_object_count(int64_t delta) {
10851096
base_object_count_ += delta;
10861097
}

src/env.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
#include "node_buffer.h"
99
#include "node_context_data.h"
1010
#include "node_errors.h"
11+
#include "node_file.h"
1112
#include "node_internals.h"
1213
#include "node_options-inl.h"
1314
#include "node_process.h"
15+
#include "node_v8.h"
1416
#include "node_v8_platform-inl.h"
1517
#include "node_worker.h"
1618
#include "req_wrap-inl.h"
@@ -1258,6 +1260,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
12581260
EnvSerializeInfo info;
12591261
Local<Context> ctx = context();
12601262

1263+
SerializeBindingData(this, creator, &info);
12611264
// Currently all modules are compiled without cache in builtin snapshot
12621265
// builder.
12631266
info.native_modules = std::vector<std::string>(
@@ -1324,6 +1327,9 @@ std::ostream& operator<<(std::ostream& output,
13241327

13251328
std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
13261329
output << "{\n"
1330+
<< "// -- bindings begins --\n"
1331+
<< i.bindings << ",\n"
1332+
<< "// -- bindings ends --\n"
13271333
<< "// -- native_modules begins --\n"
13281334
<< i.native_modules << ",\n"
13291335
<< "// -- native_modules ends --\n"
@@ -1349,9 +1355,29 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
13491355
return output;
13501356
}
13511357

1358+
void Environment::EnqueueDeserializeRequest(DeserializeRequest request) {
1359+
deserialize_requests_.push_back(std::move(request));
1360+
}
1361+
1362+
void Environment::RunDeserializeRequests() {
1363+
HandleScope scope(isolate());
1364+
Local<Context> ctx = context();
1365+
Isolate* is = isolate();
1366+
while (!deserialize_requests_.empty()) {
1367+
DeserializeRequest request(std::move(deserialize_requests_.front()));
1368+
deserialize_requests_.pop_front();
1369+
Local<Object> holder = request.holder.Get(is);
1370+
request.cb(ctx, holder, request.info);
1371+
request.holder.Reset(); // unnecessary?
1372+
request.info->Delete();
1373+
}
1374+
}
1375+
13521376
void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
13531377
Local<Context> ctx = context();
13541378

1379+
RunDeserializeRequests();
1380+
13551381
native_modules_in_snapshot = info->native_modules;
13561382
async_hooks_.Deserialize(ctx);
13571383
immediate_info_.Deserialize(ctx);

src/env.h

Lines changed: 22 additions & 0 deletions
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_serializable.h"
4041
#include "req_wrap.h"
4142
#include "util.h"
4243
#include "uv.h"
@@ -936,8 +937,22 @@ struct PropInfo {
936937
SnapshotIndex index; // In the snapshot
937938
};
938939

940+
typedef void (*DeserializeRequestCallback)(v8::Local<v8::Context>,
941+
v8::Local<v8::Object> holder,
942+
InternalFieldInfo* info);
943+
struct DeserializeRequest {
944+
DeserializeRequestCallback cb;
945+
v8::Global<v8::Object> holder;
946+
InternalFieldInfo* info; // Owned by the request
947+
948+
// Move constructor
949+
DeserializeRequest(DeserializeRequest&& other) = default;
950+
};
951+
939952
struct EnvSerializeInfo {
953+
std::vector<PropInfo> bindings;
940954
std::vector<std::string> native_modules;
955+
941956
AsyncHooks::SerializeInfo async_hooks;
942957
TickInfo::SerializeInfo tick_info;
943958
ImmediateInfo::SerializeInfo immediate_info;
@@ -971,6 +986,8 @@ class Environment : public MemoryRetainer {
971986

972987
void PrintAllBaseObjects();
973988
void VerifyNoStrongBaseObjects();
989+
void EnqueueDeserializeRequest(DeserializeRequest request);
990+
void RunDeserializeRequests();
974991
// Should be called before InitializeInspector()
975992
void InitializeDiagnostics();
976993

@@ -1407,6 +1424,9 @@ class Environment : public MemoryRetainer {
14071424
void AddUnmanagedFd(int fd);
14081425
void RemoveUnmanagedFd(int fd);
14091426

1427+
template <typename T>
1428+
void ForEachBindingData(T&& iterator);
1429+
14101430
private:
14111431
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
14121432
const char* errmsg);
@@ -1495,6 +1515,8 @@ class Environment : public MemoryRetainer {
14951515
bool is_in_inspector_console_call_ = false;
14961516
#endif
14971517

1518+
std::list<DeserializeRequest> deserialize_requests_;
1519+
14981520
// handle_wrap_queue_ and req_wrap_queue_ needs to be at a fixed offset from
14991521
// the start of the class because it is used by
15001522
// src/node_postmortem_metadata.cc to calculate offsets and generate debug

0 commit comments

Comments
 (0)