Skip to content

Commit

Permalink
src: support fs and v8 binding data in snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
joyeecheung committed Jan 28, 2021
1 parent 4e833b6 commit e9d9076
Show file tree
Hide file tree
Showing 33 changed files with 694 additions and 193 deletions.
14 changes: 9 additions & 5 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const {

const pathModule = require('path');
const { isArrayBufferView } = require('internal/util/types');

// We need to get the statValues from the binding at the callsite since
// it's re-initialized after deserialization.

const binding = internalBinding('fs');
const { Buffer } = require('buffer');
const {
Expand All @@ -82,7 +86,7 @@ const {
uvException
} = require('internal/errors');

const { FSReqCallback, statValues } = binding;
const { FSReqCallback } = binding;
const { toPathIfFileURL } = require('internal/url');
const internalUtil = require('internal/util');
const {
Expand Down Expand Up @@ -1781,8 +1785,8 @@ function realpathSync(p, options) {

// Continue if not a symlink, break if a pipe/socket
if (knownHard[base] || cache?.get(base) === base) {
if (isFileType(statValues, S_IFIFO) ||
isFileType(statValues, S_IFSOCK)) {
if (isFileType(binding.statValues, S_IFIFO) ||
isFileType(binding.statValues, S_IFSOCK)) {
break;
}
continue;
Expand Down Expand Up @@ -1924,8 +1928,8 @@ function realpath(p, options, callback) {

// Continue if not a symlink, break if a pipe/socket
if (knownHard[base]) {
if (isFileType(statValues, S_IFIFO) ||
isFileType(statValues, S_IFSOCK)) {
if (isFileType(binding.statValues, S_IFIFO) ||
isFileType(binding.statValues, S_IFSOCK)) {
return callback(null, encodeRealpathResult(p, options));
}
return process.nextTick(LOOP);
Expand Down
3 changes: 3 additions & 0 deletions lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@ process.emitWarning = emitWarning;
// Note: only after this point are the timers effective
}

require('fs');
internalBinding('v8');

function setupPrepareStackTrace() {
const {
setEnhanceStackForFatalException,
Expand Down
15 changes: 8 additions & 7 deletions lib/v8.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ function getHeapSnapshot() {
return new HeapSnapshotStream(handle);
}

// We need to get the buffer from the binding at the callsite since
// it's re-initialized after deserialization.
const binding = internalBinding('v8');

const {
cachedDataVersionTag,
setFlagsFromString: _setFlagsFromString,
heapStatisticsBuffer,
heapSpaceStatisticsBuffer,
heapCodeStatisticsBuffer,
updateHeapStatisticsBuffer,
updateHeapSpaceStatisticsBuffer,
updateHeapCodeStatisticsBuffer,
Expand Down Expand Up @@ -106,7 +107,7 @@ const {
kCodeAndMetadataSizeIndex,
kBytecodeAndMetadataSizeIndex,
kExternalScriptSourceSizeIndex
} = internalBinding('v8');
} = binding;

const kNumberOfHeapSpaces = kHeapSpaces.length;

Expand All @@ -116,7 +117,7 @@ function setFlagsFromString(flags) {
}

function getHeapStatistics() {
const buffer = heapStatisticsBuffer;
const buffer = binding.heapStatisticsBuffer;

updateHeapStatisticsBuffer();

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

function getHeapSpaceStatistics() {
const heapSpaceStatistics = new Array(kNumberOfHeapSpaces);
const buffer = heapSpaceStatisticsBuffer;
const buffer = binding.heapSpaceStatisticsBuffer;

for (let i = 0; i < kNumberOfHeapSpaces; i++) {
updateHeapSpaceStatisticsBuffer(i);
Expand All @@ -154,7 +155,7 @@ function getHeapSpaceStatistics() {
}

function getHeapCodeStatistics() {
const buffer = heapCodeStatisticsBuffer;
const buffer = binding.heapCodeStatisticsBuffer;

updateHeapCodeStatisticsBuffer();
return {
Expand Down
3 changes: 3 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@
'src/node_report_module.cc',
'src/node_report_utils.cc',
'src/node_serdes.cc',
'src/node_serializable.cc',
'src/node_sockaddr.cc',
'src/node_stat_watcher.cc',
'src/node_symbols.cc',
Expand Down Expand Up @@ -743,12 +744,14 @@
'src/node_report.h',
'src/node_revert.h',
'src/node_root_certs.h',
'src/node_serializable.h',
'src/node_sockaddr.h',
'src/node_sockaddr-inl.h',
'src/node_stat_watcher.h',
'src/node_union_bytes.h',
'src/node_url.h',
'src/node_version.h',
'src/node_v8.h',
'src/node_v8_platform-inl.h',
'src/node_wasi.h',
'src/node_watchdog.h',
Expand Down
4 changes: 2 additions & 2 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ that state is through the use of `Environment::AddBindingData`, which gives
binding functions access to an object for storing such state.
That object is always a [`BaseObject`][].
Its class needs to have a static `binding_data_name` field based on a
Its class needs to have a static `type_name` field based on a
constant string, in order to disambiguate it from other classes of this type,
and which could e.g. match the binding’s name (in the example above, that would
be `cares_wrap`).
Expand All @@ -433,7 +433,7 @@ class BindingData : public BaseObject {
public:
BindingData(Environment* env, Local<Object> obj) : BaseObject(env, obj) {}
static constexpr FastStringKey binding_data_name { "http_parser" };
static constexpr FastStringKey type_name { "http_parser" };
std::vector<char> parser_buffer;
bool parser_buffer_in_use = false;
Expand Down
5 changes: 5 additions & 0 deletions src/aliased_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ class AliasedBufferBase {
return js_array_.Get(isolate_);
}

void Release() {
DCHECK_NULL(index_);
js_array_.Reset();
}

/**
* Get the underlying v8::ArrayBuffer underlying the TypedArray and
* overlaying the native buffer
Expand Down
4 changes: 4 additions & 0 deletions src/base_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ class BaseObject : public MemoryRetainer {

virtual inline void OnGCCollect();

bool is_serializable() const { return is_serializable_; }
void set_is_serializable(bool val) { is_serializable_ = val; }

private:
v8::Local<v8::Object> WrappedObject() const override;
bool IsRootNode() const override;
Expand Down Expand Up @@ -206,6 +209,7 @@ class BaseObject : public MemoryRetainer {

Environment* env_;
PointerData* pointer_data_ = nullptr;
bool is_serializable_ = false;
};

// Global alias for FromJSObject() to avoid churn.
Expand Down
15 changes: 13 additions & 2 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ inline T* Environment::GetBindingData(v8::Local<v8::Context> context) {
context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kBindingListIndex));
DCHECK_NOT_NULL(map);
auto it = map->find(T::binding_data_name);
auto it = map->find(T::type_name);
if (UNLIKELY(it == map->end())) return nullptr;
T* result = static_cast<T*>(it->second.get());
DCHECK_NOT_NULL(result);
Expand All @@ -377,7 +377,7 @@ inline T* Environment::AddBindingData(
context->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kBindingListIndex));
DCHECK_NOT_NULL(map);
auto result = map->emplace(T::binding_data_name, item);
auto result = map->emplace(T::type_name, item);
CHECK(result.second);
DCHECK_EQ(GetBindingData<T>(context), item.get());
return item.get();
Expand Down Expand Up @@ -1081,6 +1081,17 @@ void Environment::ForEachBaseObject(T&& iterator) {
}
}

template <typename T>
void Environment::ForEachBindingData(T&& iterator) {
BindingDataStore* map = static_cast<BindingDataStore*>(
context()->GetAlignedPointerFromEmbedderData(
ContextEmbedderIndex::kBindingListIndex));
DCHECK_NOT_NULL(map);
for (auto& it : *map) {
iterator(it.first, it.second);
}
}

void Environment::modify_base_object_count(int64_t delta) {
base_object_count_ += delta;
}
Expand Down
26 changes: 26 additions & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
#include "node_buffer.h"
#include "node_context_data.h"
#include "node_errors.h"
#include "node_file.h"
#include "node_internals.h"
#include "node_options-inl.h"
#include "node_process.h"
#include "node_v8.h"
#include "node_v8_platform-inl.h"
#include "node_worker.h"
#include "req_wrap-inl.h"
Expand Down Expand Up @@ -1258,6 +1260,7 @@ EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
EnvSerializeInfo info;
Local<Context> ctx = context();

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

std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
output << "{\n"
<< "// -- bindings begins --\n"
<< i.bindings << ",\n"
<< "// -- bindings ends --\n"
<< "// -- native_modules begins --\n"
<< i.native_modules << ",\n"
<< "// -- native_modules ends --\n"
Expand All @@ -1349,9 +1355,29 @@ std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) {
return output;
}

void Environment::EnqueueDeserializeRequest(DeserializeRequest request) {
deserialize_requests_.push_back(std::move(request));
}

void Environment::RunDeserializeRequests() {
HandleScope scope(isolate());
Local<Context> ctx = context();
Isolate* is = isolate();
while (!deserialize_requests_.empty()) {
DeserializeRequest request(std::move(deserialize_requests_.front()));
deserialize_requests_.pop_front();
Local<Object> holder = request.holder.Get(is);
request.cb(ctx, holder, request.info);
request.holder.Reset(); // unnecessary?
request.info->Delete();
}
}

void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
Local<Context> ctx = context();

RunDeserializeRequests();

native_modules_in_snapshot = info->native_modules;
async_hooks_.Deserialize(ctx);
immediate_info_.Deserialize(ctx);
Expand Down
22 changes: 22 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "node_main_instance.h"
#include "node_options.h"
#include "node_perf_common.h"
#include "node_serializable.h"
#include "req_wrap.h"
#include "util.h"
#include "uv.h"
Expand Down Expand Up @@ -936,8 +937,22 @@ struct PropInfo {
SnapshotIndex index; // In the snapshot
};

typedef void (*DeserializeRequestCallback)(v8::Local<v8::Context>,
v8::Local<v8::Object> holder,
InternalFieldInfo* info);
struct DeserializeRequest {
DeserializeRequestCallback cb;
v8::Global<v8::Object> holder;
InternalFieldInfo* info; // Owned by the request

// Move constructor
DeserializeRequest(DeserializeRequest&& other) = default;
};

struct EnvSerializeInfo {
std::vector<PropInfo> bindings;
std::vector<std::string> native_modules;

AsyncHooks::SerializeInfo async_hooks;
TickInfo::SerializeInfo tick_info;
ImmediateInfo::SerializeInfo immediate_info;
Expand Down Expand Up @@ -971,6 +986,8 @@ class Environment : public MemoryRetainer {

void PrintAllBaseObjects();
void VerifyNoStrongBaseObjects();
void EnqueueDeserializeRequest(DeserializeRequest request);
void RunDeserializeRequests();
// Should be called before InitializeInspector()
void InitializeDiagnostics();

Expand Down Expand Up @@ -1407,6 +1424,9 @@ class Environment : public MemoryRetainer {
void AddUnmanagedFd(int fd);
void RemoveUnmanagedFd(int fd);

template <typename T>
void ForEachBindingData(T&& iterator);

private:
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
const char* errmsg);
Expand Down Expand Up @@ -1495,6 +1515,8 @@ class Environment : public MemoryRetainer {
bool is_in_inspector_console_call_ = false;
#endif

std::list<DeserializeRequest> deserialize_requests_;

// handle_wrap_queue_ and req_wrap_queue_ needs to be at a fixed offset from
// the start of the class because it is used by
// src/node_postmortem_metadata.cc to calculate offsets and generate debug
Expand Down
9 changes: 9 additions & 0 deletions src/node_dir.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "node_dir.h"
#include "node_external_reference.h"
#include "node_file-inl.h"
#include "node_process.h"
#include "memory_tracker-inl.h"
Expand Down Expand Up @@ -362,8 +363,16 @@ void Initialize(Local<Object> target,
env->set_dir_instance_template(dirt);
}

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(OpenDir);
registry->Register(DirHandle::New);
registry->Register(DirHandle::Read);
registry->Register(DirHandle::Close);
}

} // namespace fs_dir

} // end namespace node

NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs_dir, node::fs_dir::Initialize)
NODE_MODULE_EXTERNAL_REFERENCE(fs_dir, node::fs_dir::RegisterExternalReferences)
3 changes: 3 additions & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class ExternalReferenceRegistry {
V(credentials) \
V(env_var) \
V(errors) \
V(fs) \
V(fs_dir) \
V(handle_wrap) \
V(messaging) \
V(native_module) \
Expand All @@ -65,6 +67,7 @@ class ExternalReferenceRegistry {
V(trace_events) \
V(timers) \
V(types) \
V(v8) \
V(worker)

#if NODE_HAVE_I18N_SUPPORT
Expand Down
Loading

0 comments on commit e9d9076

Please sign in to comment.