From 59861bac0e8ea6e58bbb3babd9902586bc95a524 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 8 Feb 2021 22:13:37 +0800 Subject: [PATCH] bootstrap: include fs module into the builtin snapshot PR-URL: https://github.com/nodejs/node/pull/36943 Fixes: https://github.com/nodejs/node/issues/35930 Refs: https://github.com/nodejs/node/issues/35711 Reviewed-By: James M Snell --- lib/fs.js | 14 +++-- lib/internal/bootstrap/node.js | 3 + src/node_dir.cc | 9 +++ src/node_external_reference.h | 2 + src/node_file.cc | 104 ++++++++++++++++++++++++++++++--- src/node_file.h | 15 +++-- src/node_snapshotable.h | 3 +- src/node_stat_watcher.cc | 8 ++- src/node_stat_watcher.h | 2 + src/stream_base.cc | 28 ++++++++- src/stream_base.h | 3 +- 11 files changed, 165 insertions(+), 26 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 2bb629d58af081..31aff87d4bec86 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -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 { @@ -81,7 +85,7 @@ const { uvException } = require('internal/errors'); -const { FSReqCallback, statValues } = binding; +const { FSReqCallback } = binding; const { toPathIfFileURL } = require('internal/url'); const internalUtil = require('internal/util'); const { @@ -1772,8 +1776,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; @@ -1915,8 +1919,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); diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 8077c462983154..5b6474d017d4eb 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -277,6 +277,9 @@ process.emitWarning = emitWarning; // Note: only after this point are the timers effective } +// Preload modules so that they are included in the builtin snapshot. +require('fs'); + function setupPrepareStackTrace() { const { setEnhanceStackForFatalException, diff --git a/src/node_dir.cc b/src/node_dir.cc index d94d78f560bf39..b103c08262a5e8 100644 --- a/src/node_dir.cc +++ b/src/node_dir.cc @@ -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" @@ -364,8 +365,16 @@ void Initialize(Local 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) diff --git a/src/node_external_reference.h b/src/node_external_reference.h index 0544979dd9a6f1..71d155cdb92caf 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -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) \ diff --git a/src/node_file.cc b/src/node_file.cc index 31494e8d2e8370..0eb882b49a88bf 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -23,6 +23,7 @@ #include "aliased_buffer.h" #include "memory_tracker-inl.h" #include "node_buffer.h" +#include "node_external_reference.h" #include "node_process.h" #include "node_stat_watcher.h" #include "util-inl.h" @@ -2398,6 +2399,47 @@ void BindingData::MemoryInfo(MemoryTracker* tracker) const { file_handle_read_wrap_freelist); } +BindingData::BindingData(Environment* env, v8::Local wrap) + : SnapshotableObject(env, wrap, type_int), + stats_field_array(env->isolate(), kFsStatsBufferLength), + stats_field_bigint_array(env->isolate(), kFsStatsBufferLength) { + wrap->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "statValues"), + stats_field_array.GetJSArray()) + .Check(); + + wrap->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "bigintStatValues"), + stats_field_bigint_array.GetJSArray()) + .Check(); +} + +void BindingData::Deserialize(Local context, + Local holder, + int index, + InternalFieldInfo* info) { + DCHECK_EQ(index, BaseObject::kSlot); + HandleScope scope(context->GetIsolate()); + Environment* env = Environment::GetCurrent(context); + BindingData* binding = env->AddBindingData(context, holder); + CHECK_NOT_NULL(binding); +} + +void BindingData::PrepareForSerialization(Local context, + v8::SnapshotCreator* creator) { + CHECK(file_handle_read_wrap_freelist.empty()); + // We'll just re-initialize the buffers in the constructor since their + // contents can be thrown away once consumed in the previous call. + stats_field_array.Release(); + stats_field_bigint_array.Release(); +} + +InternalFieldInfo* BindingData::Serialize(int index) { + DCHECK_EQ(index, BaseObject::kSlot); + InternalFieldInfo* info = InternalFieldInfo::New(type()); + return info; +} + // TODO(addaleax): Remove once we're on C++17. constexpr FastStringKey BindingData::type_name; @@ -2461,14 +2503,6 @@ void Initialize(Local target, static_cast(FsStatsOffset::kFsStatsFieldsNumber))) .Check(); - target->Set(context, - FIXED_ONE_BYTE_STRING(isolate, "statValues"), - binding_data->stats_field_array.GetJSArray()).Check(); - - target->Set(context, - FIXED_ONE_BYTE_STRING(isolate, "bigintStatValues"), - binding_data->stats_field_bigint_array.GetJSArray()).Check(); - StatWatcher::Initialize(env, target); // Create FunctionTemplate for FSReqCallback @@ -2532,8 +2566,62 @@ void Initialize(Local target, BindingData* FSReqBase::binding_data() { return binding_data_.get(); } + +void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(Access); + StatWatcher::RegisterExternalReferences(registry); + + registry->Register(Close); + registry->Register(Open); + registry->Register(OpenFileHandle); + registry->Register(Read); + registry->Register(ReadBuffers); + registry->Register(Fdatasync); + registry->Register(Fsync); + registry->Register(Rename); + registry->Register(FTruncate); + registry->Register(RMDir); + registry->Register(MKDir); + registry->Register(ReadDir); + registry->Register(InternalModuleReadJSON); + registry->Register(InternalModuleStat); + registry->Register(Stat); + registry->Register(LStat); + registry->Register(FStat); + registry->Register(Link); + registry->Register(Symlink); + registry->Register(ReadLink); + registry->Register(Unlink); + registry->Register(WriteBuffer); + registry->Register(WriteBuffers); + registry->Register(WriteString); + registry->Register(RealPath); + registry->Register(CopyFile); + + registry->Register(Chmod); + registry->Register(FChmod); + // registry->Register(LChmod); + + registry->Register(Chown); + registry->Register(FChown); + registry->Register(LChown); + + registry->Register(UTimes); + registry->Register(FUTimes); + registry->Register(LUTimes); + + registry->Register(Mkdtemp); + registry->Register(NewFSReqCallback); + + registry->Register(FileHandle::New); + registry->Register(FileHandle::Close); + registry->Register(FileHandle::ReleaseFD); + StreamBase::RegisterExternalReferences(registry); +} + } // namespace fs } // end namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs, node::fs::Initialize) +NODE_MODULE_EXTERNAL_REFERENCE(fs, node::fs::RegisterExternalReferences) diff --git a/src/node_file.h b/src/node_file.h index 9e652dc7a4afa4..f1515a3e25d6d1 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -3,23 +3,19 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "node.h" #include "aliased_buffer.h" #include "node_messaging.h" +#include "node_snapshotable.h" #include "stream_base.h" -#include namespace node { namespace fs { class FileHandleReadWrap; -class BindingData : public BaseObject { +class BindingData : public SnapshotableObject { public: - explicit BindingData(Environment* env, v8::Local wrap) - : BaseObject(env, wrap), - stats_field_array(env->isolate(), kFsStatsBufferLength), - stats_field_bigint_array(env->isolate(), kFsStatsBufferLength) {} + explicit BindingData(Environment* env, v8::Local wrap); AliasedFloat64Array stats_field_array; AliasedBigUint64Array stats_field_bigint_array; @@ -27,7 +23,10 @@ class BindingData : public BaseObject { std::vector> file_handle_read_wrap_freelist; - static constexpr FastStringKey type_name { "fs" }; + SERIALIZABLE_OBJECT_METHODS() + static constexpr FastStringKey type_name{"node::fs::BindingData"}; + static constexpr EmbedderObjectType type_int = + EmbedderObjectType::k_fs_binding_data; void MemoryInfo(MemoryTracker* tracker) const override; SET_SELF_SIZE(BindingData) diff --git a/src/node_snapshotable.h b/src/node_snapshotable.h index 94a5a7a03ca731..d0fbce800a63ec 100644 --- a/src/node_snapshotable.h +++ b/src/node_snapshotable.h @@ -12,7 +12,8 @@ namespace node { class Environment; struct EnvSerializeInfo; -#define SERIALIZABLE_OBJECT_TYPES(V) +#define SERIALIZABLE_OBJECT_TYPES(V) \ + V(fs_binding_data, fs::BindingData) enum class EmbedderObjectType : uint8_t { k_default = 0, diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index 344ea6bb7ea2e6..b9f7903a2fdcb6 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -19,10 +19,11 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "memory_tracker-inl.h" #include "node_stat_watcher.h" #include "async_wrap-inl.h" #include "env-inl.h" +#include "memory_tracker-inl.h" +#include "node_external_reference.h" #include "node_file-inl.h" #include "util-inl.h" @@ -55,6 +56,11 @@ void StatWatcher::Initialize(Environment* env, Local target) { env->SetConstructorFunction(target, "StatWatcher", t); } +void StatWatcher::RegisterExternalReferences( + ExternalReferenceRegistry* registry) { + registry->Register(StatWatcher::New); + registry->Register(StatWatcher::Start); +} StatWatcher::StatWatcher(fs::BindingData* binding_data, Local wrap, diff --git a/src/node_stat_watcher.h b/src/node_stat_watcher.h index c1d6ccce69bdbb..7efd22fdfdb841 100644 --- a/src/node_stat_watcher.h +++ b/src/node_stat_watcher.h @@ -35,10 +35,12 @@ class BindingData; } class Environment; +class ExternalReferenceRegistry; class StatWatcher : public HandleWrap { public: static void Initialize(Environment* env, v8::Local target); + static void RegisterExternalReferences(ExternalReferenceRegistry* registry); protected: StatWatcher(fs::BindingData* binding_data, diff --git a/src/stream_base.cc b/src/stream_base.cc index 87781efb0e8111..925dc3f152e550 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -3,11 +3,12 @@ #include "stream_wrap.h" #include "allocated_buffer-inl.h" +#include "env-inl.h" +#include "js_stream.h" #include "node.h" #include "node_buffer.h" #include "node_errors.h" -#include "env-inl.h" -#include "js_stream.h" +#include "node_external_reference.h" #include "string_bytes.h" #include "util-inl.h" #include "v8.h" @@ -423,6 +424,29 @@ void StreamBase::AddMethods(Environment* env, Local t) { &Value::IsFunction>); } +void StreamBase::RegisterExternalReferences( + ExternalReferenceRegistry* registry) { + registry->Register(GetFD); + registry->Register(GetExternal); + registry->Register(GetBytesRead); + registry->Register(GetBytesWritten); + registry->Register(JSMethod<&StreamBase::ReadStartJS>); + registry->Register(JSMethod<&StreamBase::ReadStopJS>); + registry->Register(JSMethod<&StreamBase::Shutdown>); + registry->Register(JSMethod<&StreamBase::UseUserBuffer>); + registry->Register(JSMethod<&StreamBase::Writev>); + registry->Register(JSMethod<&StreamBase::WriteBuffer>); + registry->Register(JSMethod<&StreamBase::WriteString>); + registry->Register(JSMethod<&StreamBase::WriteString>); + registry->Register(JSMethod<&StreamBase::WriteString>); + registry->Register(JSMethod<&StreamBase::WriteString>); + registry->Register( + BaseObject::InternalFieldGet); + registry->Register( + BaseObject::InternalFieldSet); +} + void StreamBase::GetFD(const FunctionCallbackInfo& args) { // Mimic implementation of StreamBase::GetFD() and UDPWrap::GetFD(). StreamBase* wrap = StreamBase::FromObject(args.This().As()); diff --git a/src/stream_base.h b/src/stream_base.h index a5680ba8860d49..e0da891501ac85 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -19,6 +19,7 @@ class ShutdownWrap; class WriteWrap; class StreamBase; class StreamResource; +class ExternalReferenceRegistry; struct StreamWriteResult { bool async; @@ -308,7 +309,7 @@ class StreamBase : public StreamResource { static void AddMethods(Environment* env, v8::Local target); - + static void RegisterExternalReferences(ExternalReferenceRegistry* registry); virtual bool IsAlive() = 0; virtual bool IsClosing() = 0; virtual bool IsIPCPipe();