diff options
-rw-r--r-- | node.gyp | 1 | ||||
-rw-r--r-- | src/env.cc | 2 | ||||
-rw-r--r-- | src/node_dir.cc | 7 | ||||
-rw-r--r-- | src/node_dir.h | 13 | ||||
-rw-r--r-- | src/node_file-inl.h | 283 | ||||
-rw-r--r-- | src/node_file.cc | 92 | ||||
-rw-r--r-- | src/node_file.h | 382 | ||||
-rw-r--r-- | src/node_stat_watcher.cc | 2 | ||||
-rw-r--r-- | src/req_wrap-inl.h | 2 | ||||
-rw-r--r-- | src/req_wrap.h | 5 |
10 files changed, 462 insertions, 327 deletions
@@ -613,6 +613,7 @@ 'src/node_dir.h', 'src/node_errors.h', 'src/node_file.h', + 'src/node_file-inl.h', 'src/node_http2.h', 'src/node_http2_state.h', 'src/node_i18n.h', diff --git a/src/env.cc b/src/env.cc index dc99e4bd532..bf3420f826f 100644 --- a/src/env.cc +++ b/src/env.cc @@ -11,6 +11,7 @@ #include "node_process.h" #include "node_v8_platform-inl.h" #include "node_worker.h" +#include "req_wrap-inl.h" #include "tracing/agent.h" #include "tracing/traced_value.h" #include "util-inl.h" @@ -35,6 +36,7 @@ using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::MaybeLocal; using v8::NewStringType; using v8::Number; using v8::Object; diff --git a/src/node_dir.cc b/src/node_dir.cc index 9d145131beb..37285928fbd 100644 --- a/src/node_dir.cc +++ b/src/node_dir.cc @@ -1,10 +1,11 @@ #include "node_dir.h" +#include "node_file-inl.h" #include "node_process.h" +#include "memory_tracker-inl.h" #include "util.h" #include "tracing/trace_event.h" -#include "req_wrap-inl.h" #include "string_bytes.h" #include <fcntl.h> @@ -85,6 +86,10 @@ DirHandle::~DirHandle() { CHECK(closed_); // We have to be closed at the point } +void DirHandle::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackFieldWithSize("dir", sizeof(*dir_)); +} + // Close the directory handle if it hasn't already been closed. A process // warning will be emitted using a SetImmediate to avoid calling back to // JS during GC. If closing the fd fails at this point, a fatal exception diff --git a/src/node_dir.h b/src/node_dir.h index ae6d0eb170d..caef7a5d309 100644 --- a/src/node_dir.h +++ b/src/node_dir.h @@ -4,8 +4,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "node_file.h" -#include "node.h" -#include "req_wrap-inl.h" namespace node { @@ -20,16 +18,13 @@ class DirHandle : public AsyncWrap { ~DirHandle() override; static void New(const v8::FunctionCallbackInfo<v8::Value>& args); - static void Open(const v8::FunctionCallbackInfo<Value>& args); - static void Read(const v8::FunctionCallbackInfo<Value>& args); - static void Close(const v8::FunctionCallbackInfo<Value>& args); + static void Open(const v8::FunctionCallbackInfo<v8::Value>& args); + static void Read(const v8::FunctionCallbackInfo<v8::Value>& args); + static void Close(const v8::FunctionCallbackInfo<v8::Value>& args); inline uv_dir_t* dir() { return dir_; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackFieldWithSize("dir", sizeof(*dir_)); - } - + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(DirHandle) SET_SELF_SIZE(DirHandle) diff --git a/src/node_file-inl.h b/src/node_file-inl.h new file mode 100644 index 00000000000..390f6c74157 --- /dev/null +++ b/src/node_file-inl.h @@ -0,0 +1,283 @@ +#ifndef SRC_NODE_FILE_INL_H_ +#define SRC_NODE_FILE_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_file.h" +#include "req_wrap-inl.h" + +namespace node { +namespace fs { + +FSContinuationData::FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb) + : done_cb_(done_cb), req_(req), mode_(mode) { +} + +void FSContinuationData::PushPath(std::string&& path) { + paths_.emplace_back(std::move(path)); +} + +void FSContinuationData::PushPath(const std::string& path) { + paths_.push_back(path); +} + +std::string FSContinuationData::PopPath() { + CHECK_GT(paths_.size(), 0); + std::string path = std::move(paths_.back()); + paths_.pop_back(); + return path; +} + +void FSContinuationData::Done(int result) { + req_->result = result; + done_cb_(req_); +} + +FSReqBase::FSReqBase(Environment* env, + v8::Local<v8::Object> req, + AsyncWrap::ProviderType type, + bool use_bigint) + : ReqWrap(env, req, type), use_bigint_(use_bigint) { +} + +void FSReqBase::Init(const char* syscall, + const char* data, + size_t len, + enum encoding encoding) { + syscall_ = syscall; + encoding_ = encoding; + + if (data != nullptr) { + CHECK(!has_data_); + buffer_.AllocateSufficientStorage(len + 1); + buffer_.SetLengthAndZeroTerminate(len); + memcpy(*buffer_, data, len); + has_data_ = true; + } +} + +FSReqBase::FSReqBuffer& +FSReqBase::Init(const char* syscall, size_t len, enum encoding encoding) { + syscall_ = syscall; + encoding_ = encoding; + + buffer_.AllocateSufficientStorage(len + 1); + has_data_ = false; // so that the data does not show up in error messages + return buffer_; +} + +FSReqCallback::FSReqCallback(Environment* env, + v8::Local<v8::Object> req, bool use_bigint) + : FSReqBase(env, req, AsyncWrap::PROVIDER_FSREQCALLBACK, use_bigint) {} + +template <typename NativeT, typename V8T> +void FillStatsArray(AliasedBufferBase<NativeT, V8T>* fields, + const uv_stat_t* s, + const size_t offset) { +#define SET_FIELD_WITH_STAT(stat_offset, stat) \ + fields->SetValue(offset + static_cast<size_t>(FsStatsOffset::stat_offset), \ + static_cast<NativeT>(stat)) + +#define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \ + /* NOLINTNEXTLINE(runtime/int) */ \ + SET_FIELD_WITH_STAT(stat_offset, static_cast<unsigned long>(stat)) + + SET_FIELD_WITH_STAT(kDev, s->st_dev); + SET_FIELD_WITH_STAT(kMode, s->st_mode); + SET_FIELD_WITH_STAT(kNlink, s->st_nlink); + SET_FIELD_WITH_STAT(kUid, s->st_uid); + SET_FIELD_WITH_STAT(kGid, s->st_gid); + SET_FIELD_WITH_STAT(kRdev, s->st_rdev); + SET_FIELD_WITH_STAT(kBlkSize, s->st_blksize); + SET_FIELD_WITH_STAT(kIno, s->st_ino); + SET_FIELD_WITH_STAT(kSize, s->st_size); + SET_FIELD_WITH_STAT(kBlocks, s->st_blocks); + + SET_FIELD_WITH_TIME_STAT(kATimeSec, s->st_atim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kATimeNsec, s->st_atim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kMTimeSec, s->st_mtim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kMTimeNsec, s->st_mtim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kCTimeSec, s->st_ctim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kCTimeNsec, s->st_ctim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kBirthTimeSec, s->st_birthtim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kBirthTimeNsec, s->st_birthtim.tv_nsec); + +#undef SET_FIELD_WITH_TIME_STAT +#undef SET_FIELD_WITH_STAT +} + +v8::Local<v8::Value> FillGlobalStatsArray(Environment* env, + const bool use_bigint, + const uv_stat_t* s, + const bool second) { + const ptrdiff_t offset = + second ? static_cast<ptrdiff_t>(FsStatsOffset::kFsStatsFieldsNumber) : 0; + if (use_bigint) { + auto* const arr = env->fs_stats_field_bigint_array(); + FillStatsArray(arr, s, offset); + return arr->GetJSArray(); + } else { + auto* const arr = env->fs_stats_field_array(); + FillStatsArray(arr, s, offset); + return arr->GetJSArray(); + } +} + +template <typename AliasedBufferT> +FSReqPromise<AliasedBufferT>* +FSReqPromise<AliasedBufferT>::New(Environment* env, bool use_bigint) { + v8::Local<v8::Object> obj; + if (!env->fsreqpromise_constructor_template() + ->NewInstance(env->context()) + .ToLocal(&obj)) { + return nullptr; + } + v8::Local<v8::Promise::Resolver> resolver; + if (!v8::Promise::Resolver::New(env->context()).ToLocal(&resolver) || + obj->Set(env->context(), env->promise_string(), resolver).IsNothing()) { + return nullptr; + } + return new FSReqPromise(env, obj, use_bigint); +} + +template <typename AliasedBufferT> +FSReqPromise<AliasedBufferT>::~FSReqPromise() { + // Validate that the promise was explicitly resolved or rejected. + CHECK(finished_); +} + +template <typename AliasedBufferT> +FSReqPromise<AliasedBufferT>::FSReqPromise( + Environment* env, + v8::Local<v8::Object> obj, + bool use_bigint) + : FSReqBase(env, obj, AsyncWrap::PROVIDER_FSREQPROMISE, use_bigint), + stats_field_array_( + env->isolate(), + static_cast<size_t>(FsStatsOffset::kFsStatsFieldsNumber)) {} + +template <typename AliasedBufferT> +void FSReqPromise<AliasedBufferT>::Reject(v8::Local<v8::Value> reject) { + finished_ = true; + v8::HandleScope scope(env()->isolate()); + InternalCallbackScope callback_scope(this); + v8::Local<v8::Value> value = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local<v8::Promise::Resolver> resolver = value.As<v8::Promise::Resolver>(); + USE(resolver->Reject(env()->context(), reject).FromJust()); +} + +template <typename AliasedBufferT> +void FSReqPromise<AliasedBufferT>::Resolve(v8::Local<v8::Value> value) { + finished_ = true; + v8::HandleScope scope(env()->isolate()); + InternalCallbackScope callback_scope(this); + v8::Local<v8::Value> val = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local<v8::Promise::Resolver> resolver = val.As<v8::Promise::Resolver>(); + USE(resolver->Resolve(env()->context(), value).FromJust()); +} + +template <typename AliasedBufferT> +void FSReqPromise<AliasedBufferT>::ResolveStat(const uv_stat_t* stat) { + FillStatsArray(&stats_field_array_, stat); + Resolve(stats_field_array_.GetJSArray()); +} + +template <typename AliasedBufferT> +void FSReqPromise<AliasedBufferT>::SetReturnValue( + const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::Local<v8::Value> val = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local<v8::Promise::Resolver> resolver = val.As<v8::Promise::Resolver>(); + args.GetReturnValue().Set(resolver->GetPromise()); +} + +template <typename AliasedBufferT> +void FSReqPromise<AliasedBufferT>::MemoryInfo(MemoryTracker* tracker) const { + FSReqBase::MemoryInfo(tracker); + tracker->TrackField("stats_field_array", stats_field_array_); +} + +FSReqBase* GetReqWrap(Environment* env, v8::Local<v8::Value> value, + bool use_bigint) { + if (value->IsObject()) { + return Unwrap<FSReqBase>(value.As<v8::Object>()); + } else if (value->StrictEquals(env->fs_use_promises_symbol())) { + if (use_bigint) { + return FSReqPromise<AliasedBigUint64Array>::New(env, use_bigint); + } else { + return FSReqPromise<AliasedFloat64Array>::New(env, use_bigint); + } + } + return nullptr; +} + +// Returns nullptr if the operation fails from the start. +template <typename Func, typename... Args> +FSReqBase* AsyncDestCall(Environment* env, FSReqBase* req_wrap, + const v8::FunctionCallbackInfo<v8::Value>& args, + const char* syscall, const char* dest, + size_t len, enum encoding enc, uv_fs_cb after, + Func fn, Args... fn_args) { + CHECK_NOT_NULL(req_wrap); + req_wrap->Init(syscall, dest, len, enc); + int err = req_wrap->Dispatch(fn, fn_args..., after); + if (err < 0) { + uv_fs_t* uv_req = req_wrap->req(); + uv_req->result = err; + uv_req->path = nullptr; + after(uv_req); // after may delete req_wrap if there is an error + req_wrap = nullptr; + } else { + req_wrap->SetReturnValue(args); + } + + return req_wrap; +} + +// Returns nullptr if the operation fails from the start. +template <typename Func, typename... Args> +FSReqBase* AsyncCall(Environment* env, + FSReqBase* req_wrap, + const v8::FunctionCallbackInfo<v8::Value>& args, + const char* syscall, enum encoding enc, + uv_fs_cb after, Func fn, Args... fn_args) { + return AsyncDestCall(env, req_wrap, args, + syscall, nullptr, 0, enc, + after, fn, fn_args...); +} + +// Template counterpart of SYNC_CALL, except that it only puts +// the error number and the syscall in the context instead of +// creating an error in the C++ land. +// ctx must be checked using value->IsObject() before being passed. +template <typename Func, typename... Args> +int SyncCall(Environment* env, v8::Local<v8::Value> ctx, + FSReqWrapSync* req_wrap, const char* syscall, + Func fn, Args... args) { + env->PrintSyncTrace(); + int err = fn(env->event_loop(), &(req_wrap->req), args..., nullptr); + if (err < 0) { + v8::Local<v8::Context> context = env->context(); + v8::Local<v8::Object> ctx_obj = ctx.As<v8::Object>(); + v8::Isolate* isolate = env->isolate(); + ctx_obj->Set(context, + env->errno_string(), + v8::Integer::New(isolate, err)).Check(); + ctx_obj->Set(context, + env->syscall_string(), + OneByteString(isolate, syscall)).Check(); + } + return err; +} + +} // namespace fs +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_FILE_INL_H_ diff --git a/src/node_file.cc b/src/node_file.cc index 48b382986c0..577b9c7c417 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -18,7 +18,8 @@ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node_file.h" +#include "node_file.h" // NOLINT(build/include_inline) +#include "node_file-inl.h" #include "aliased_buffer.h" #include "memory_tracker-inl.h" #include "node_buffer.h" @@ -107,6 +108,19 @@ inline int64_t GetOffset(Local<Value> value) { // functions, and thus does not wrap them properly. typedef void(*uv_fs_callback_t)(uv_fs_t*); + +void FSContinuationData::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("paths", paths_); +} + +FileHandleReadWrap::~FileHandleReadWrap() {} + +FSReqBase::~FSReqBase() {} + +void FSReqBase::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("continuation_data", continuation_data_); +} + // The FileHandle object wraps a file descriptor and will close it on garbage // collection if necessary. If that happens, a process warning will be // emitted (or a fatal exception will occur if the fd cannot be closed.) @@ -156,6 +170,16 @@ FileHandle::~FileHandle() { CHECK(closed_); // We have to be closed at the point } +int FileHandle::DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle) { + return UV_ENOSYS; // Not implemented (yet). +} + +void FileHandle::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("current_read", current_read_); +} // Close the file descriptor if it hasn't already been closed. A process // warning will be emitted using a SetImmediate to avoid calling back to @@ -225,12 +249,34 @@ FileHandle* FileHandle::CloseReq::file_handle() { return Unwrap<FileHandle>(obj); } +FileHandle::CloseReq::CloseReq(Environment* env, + Local<Object> obj, + Local<Promise> promise, + Local<Value> ref) + : ReqWrap(env, obj, AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) { + promise_.Reset(env->isolate(), promise); + ref_.Reset(env->isolate(), ref); +} + +FileHandle::CloseReq::~CloseReq() { + uv_fs_req_cleanup(req()); + promise_.Reset(); + ref_.Reset(); +} + +void FileHandle::CloseReq::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("promise", promise_); + tracker->TrackField("ref", ref_); +} + + + // Closes this FileHandle asynchronously and returns a Promise that will be // resolved when the callback is invoked, or rejects with a UVException if // there was a problem closing the fd. This is the preferred mechanism for // closing the FD object even tho the object will attempt to close // automatically on gc. -inline MaybeLocal<Promise> FileHandle::ClosePromise() { +MaybeLocal<Promise> FileHandle::ClosePromise() { Isolate* isolate = env()->isolate(); EscapableHandleScope scope(isolate); Local<Context> context = env()->context(); @@ -1157,13 +1203,13 @@ int MKDirpSync(uv_loop_t* loop, FSContinuationData continuation_data(req, mode, cb); continuation_data.PushPath(std::move(path)); - while (continuation_data.paths.size() > 0) { + while (continuation_data.paths().size() > 0) { std::string next_path = continuation_data.PopPath(); int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, nullptr); while (true) { switch (err) { case 0: - if (continuation_data.paths.size() == 0) { + if (continuation_data.paths().size() == 0) { return 0; } break; @@ -1173,7 +1219,7 @@ int MKDirpSync(uv_loop_t* loop, if (dirname != next_path) { continuation_data.PushPath(std::move(next_path)); continuation_data.PushPath(std::move(dirname)); - } else if (continuation_data.paths.size() == 0) { + } else if (continuation_data.paths().size() == 0) { err = UV_EEXIST; continue; } @@ -1188,7 +1234,7 @@ int MKDirpSync(uv_loop_t* loop, err = uv_fs_stat(loop, req, next_path.c_str(), nullptr); if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) { uv_fs_req_cleanup(req); - if (orig_err == UV_EEXIST && continuation_data.paths.size() > 0) { + if (orig_err == UV_EEXIST && continuation_data.paths().size() > 0) { return UV_ENOTDIR; } return UV_EEXIST; @@ -1211,14 +1257,14 @@ int MKDirpAsync(uv_loop_t* loop, uv_fs_cb cb) { FSReqBase* req_wrap = FSReqBase::from_req(req); // on the first iteration of algorithm, stash state information. - if (req_wrap->continuation_data == nullptr) { - req_wrap->continuation_data = - std::make_unique<FSContinuationData>(req, mode, cb); - req_wrap->continuation_data->PushPath(std::move(path)); + if (req_wrap->continuation_data() == nullptr) { + req_wrap->set_continuation_data( + std::make_unique<FSContinuationData>(req, mode, cb)); + req_wrap->continuation_data()->PushPath(std::move(path)); } // on each iteration of algorithm, mkdir directory on top of stack. - std::string next_path = req_wrap->continuation_data->PopPath(); + std::string next_path = req_wrap->continuation_data()->PopPath(); int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, uv_fs_callback_t{[](uv_fs_t* req) { FSReqBase* req_wrap = FSReqBase::from_req(req); @@ -1230,12 +1276,12 @@ int MKDirpAsync(uv_loop_t* loop, while (true) { switch (err) { case 0: { - if (req_wrap->continuation_data->paths.size() == 0) { - req_wrap->continuation_data->Done(0); + if (req_wrap->continuation_data()->paths().size() == 0) { + req_wrap->continuation_data()->Done(0); } else { uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); } break; } @@ -1243,19 +1289,19 @@ int MKDirpAsync(uv_loop_t* loop, std::string dirname = path.substr(0, path.find_last_of(kPathSeparator)); if (dirname != path) { - req_wrap->continuation_data->PushPath(std::move(path)); - req_wrap->continuation_data->PushPath(std::move(dirname)); - } else if (req_wrap->continuation_data->paths.size() == 0) { + req_wrap->continuation_data()->PushPath(std::move(path)); + req_wrap->continuation_data()->PushPath(std::move(dirname)); + } else if (req_wrap->continuation_data()->paths().size() == 0) { err = UV_EEXIST; continue; } uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); break; } case UV_EPERM: { - req_wrap->continuation_data->Done(err); + req_wrap->continuation_data()->Done(err); break; } default: @@ -1267,14 +1313,14 @@ int MKDirpAsync(uv_loop_t* loop, FSReqBase* req_wrap = FSReqBase::from_req(req); int err = req->result; if (reinterpret_cast<intptr_t>(req->data) == UV_EEXIST && - req_wrap->continuation_data->paths.size() > 0) { + req_wrap->continuation_data()->paths().size() > 0) { if (err == 0 && S_ISDIR(req->statbuf.st_mode)) { Environment* env = req_wrap->env(); uv_loop_t* loop = env->event_loop(); std::string path = req->path; uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); return; } err = UV_ENOTDIR; @@ -1282,9 +1328,9 @@ int MKDirpAsync(uv_loop_t* loop, // verify that the path pointed to is actually a directory. if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) err = UV_EEXIST; uv_fs_req_cleanup(req); - req_wrap->continuation_data->Done(err); + req_wrap->continuation_data()->Done(err); }}); - if (err < 0) req_wrap->continuation_data->Done(err); + if (err < 0) req_wrap->continuation_data()->Done(err); break; } break; diff --git a/src/node_file.h b/src/node_file.h index 84f4032cc2f..1042baaf8f7 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -6,113 +6,70 @@ #include "node.h" #include "aliased_buffer.h" #include "stream_base.h" -#include "memory_tracker-inl.h" -#include "req_wrap-inl.h" #include <iostream> namespace node { - -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::HandleScope; -using v8::Local; -using v8::MaybeLocal; -using v8::Object; -using v8::Promise; -using v8::Undefined; -using v8::Value; - namespace fs { // structure used to store state during a complex operation, e.g., mkdirp. class FSContinuationData : public MemoryRetainer { public: - FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb) - : req(req), mode(mode), done_cb(done_cb) { - } - - uv_fs_t* req; - int mode; - std::vector<std::string> paths{}; + inline FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb); - void PushPath(std::string&& path) { - paths.emplace_back(std::move(path)); - } - - void PushPath(const std::string& path) { - paths.push_back(path); - } + inline void PushPath(std::string&& path); + inline void PushPath(const std::string& path); + inline std::string PopPath(); + inline void Done(int result); - std::string PopPath() { - CHECK_GT(paths.size(), 0); - std::string path = std::move(paths.back()); - paths.pop_back(); - return path; - } - - void Done(int result) { - req->result = result; - done_cb(req); - } - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("paths", paths); - } + int mode() const { return mode_; } + const std::vector<std::string>& paths() const { return paths_; } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FSContinuationData) SET_SELF_SIZE(FSContinuationData) private: - uv_fs_cb done_cb; + uv_fs_cb done_cb_; + uv_fs_t* req_; + int mode_; + std::vector<std::string> paths_; }; class FSReqBase : public ReqWrap<uv_fs_t> { public: typedef MaybeStackBuffer<char, 64> FSReqBuffer; - std::unique_ptr<FSContinuationData> continuation_data = nullptr; - - FSReqBase(Environment* env, Local<Object> req, AsyncWrap::ProviderType type, - bool use_bigint) - : ReqWrap(env, req, type), use_bigint_(use_bigint) { - } - void Init(const char* syscall, - const char* data, - size_t len, - enum encoding encoding) { - syscall_ = syscall; - encoding_ = encoding; - - if (data != nullptr) { - CHECK(!has_data_); - buffer_.AllocateSufficientStorage(len + 1); - buffer_.SetLengthAndZeroTerminate(len); - memcpy(*buffer_, data, len); - has_data_ = true; - } - } - - FSReqBuffer& Init(const char* syscall, size_t len, - enum encoding encoding) { - syscall_ = syscall; - encoding_ = encoding; - - buffer_.AllocateSufficientStorage(len + 1); - has_data_ = false; // so that the data does not show up in error messages - return buffer_; - } - - virtual void Reject(Local<Value> reject) = 0; - virtual void Resolve(Local<Value> value) = 0; + inline FSReqBase(Environment* env, + v8::Local<v8::Object> req, + AsyncWrap::ProviderType type, + bool use_bigint); + ~FSReqBase() override; + + inline void Init(const char* syscall, + const char* data, + size_t len, + enum encoding encoding); + inline FSReqBuffer& Init(const char* syscall, size_t len, + enum encoding encoding); + + virtual void Reject(v8::Local<v8::Value> reject) = 0; + virtual void Resolve(v8::Local<v8::Value> value) = 0; virtual void ResolveStat(const uv_stat_t* stat) = 0; - virtual void SetReturnValue(const FunctionCallbackInfo<Value>& args) = 0; + virtual void SetReturnValue( + const v8::FunctionCallbackInfo<v8::Value>& args) = 0; const char* syscall() const { return syscall_; } const char* data() const { return has_data_ ? *buffer_ : nullptr; } enum encoding encoding() const { return encoding_; } - bool use_bigint() const { return use_bigint_; } + FSContinuationData* continuation_data() const { + return continuation_data_.get(); + } + void set_continuation_data(std::unique_ptr<FSContinuationData> data) { + continuation_data_ = std::move(data); + } + static FSReqBase* from_req(uv_fs_t* req) { return static_cast<FSReqBase*>(ReqWrap::from_req(req)); } @@ -120,7 +77,10 @@ class FSReqBase : public ReqWrap<uv_fs_t> { FSReqBase(const FSReqBase&) = delete; FSReqBase& operator=(const FSReqBase&) = delete; + void MemoryInfo(MemoryTracker* tracker) const override; + private: + std::unique_ptr<FSContinuationData> continuation_data_; enum encoding encoding_ = UTF8; bool has_data_ = false; const char* syscall_ = nullptr; @@ -131,19 +91,16 @@ class FSReqBase : public ReqWrap<uv_fs_t> { FSReqBuffer buffer_; }; -class FSReqCallback : public FSReqBase { +class FSReqCallback final : public FSReqBase { public: - FSReqCallback(Environment* env, Local<Object> req, bool use_bigint) - : FSReqBase(env, req, AsyncWrap::PROVIDER_FSREQCALLBACK, use_bigint) { } + inline FSReqCallback(Environment* env, + v8::Local<v8::Object> req, + bool use_bigint); - void Reject(Local<Value> reject) override; - void Resolve(Local<Value> value) override; + void Reject(v8::Local<v8::Value> reject) override; + void Resolve(v8::Local<v8::Value> value) override; void ResolveStat(const uv_stat_t* stat) override; - void SetReturnValue(const FunctionCallbackInfo<Value>& args) override; - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("continuation_data", continuation_data); - } + void SetReturnValue(const v8::FunctionCallbackInfo<v8::Value>& args) override; SET_MEMORY_INFO_NAME(FSReqCallback) SET_SELF_SIZE(FSReqCallback) @@ -153,120 +110,27 @@ class FSReqCallback : public FSReqBase { }; template <typename NativeT, typename V8T> -constexpr void FillStatsArray(AliasedBufferBase<NativeT, V8T>* fields, - const uv_stat_t* s, - const size_t offset = 0) { -#define SET_FIELD_WITH_STAT(stat_offset, stat) \ - fields->SetValue(offset + static_cast<size_t>(FsStatsOffset::stat_offset), \ - static_cast<NativeT>(stat)) - -#define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \ - /* NOLINTNEXTLINE(runtime/int) */ \ - SET_FIELD_WITH_STAT(stat_offset, static_cast<unsigned long>(stat)) - - SET_FIELD_WITH_STAT(kDev, s->st_dev); - SET_FIELD_WITH_STAT(kMode, s->st_mode); - SET_FIELD_WITH_STAT(kNlink, s->st_nlink); - SET_FIELD_WITH_STAT(kUid, s->st_uid); - SET_FIELD_WITH_STAT(kGid, s->st_gid); - SET_FIELD_WITH_STAT(kRdev, s->st_rdev); - SET_FIELD_WITH_STAT(kBlkSize, s->st_blksize); - SET_FIELD_WITH_STAT(kIno, s->st_ino); - SET_FIELD_WITH_STAT(kSize, s->st_size); - SET_FIELD_WITH_STAT(kBlocks, s->st_blocks); - - SET_FIELD_WITH_TIME_STAT(kATimeSec, s->st_atim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kATimeNsec, s->st_atim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kMTimeSec, s->st_mtim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kMTimeNsec, s->st_mtim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kCTimeSec, s->st_ctim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kCTimeNsec, s->st_ctim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kBirthTimeSec, s->st_birthtim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kBirthTimeNsec, s->st_birthtim.tv_nsec); - -#undef SET_FIELD_WITH_TIME_STAT -#undef SET_FIELD_WITH_STAT -} - -inline Local<Value> FillGlobalStatsArray(Environment* env, - const bool use_bigint, - const uv_stat_t* s, - const bool second = false) { - const ptrdiff_t offset = - second ? static_cast<ptrdiff_t>(FsStatsOffset::kFsStatsFieldsNumber) : 0; - if (use_bigint) { - auto* const arr = env->fs_stats_field_bigint_array(); - FillStatsArray(arr, s, offset); - return arr->GetJSArray(); - } else { - auto* const arr = env->fs_stats_field_array(); - FillStatsArray(arr, s, offset); - return arr->GetJSArray(); - } -} +void FillStatsArray(AliasedBufferBase<NativeT, V8T>* fields, + const uv_stat_t* s, + const size_t offset = 0); + +inline v8::Local<v8::Value> FillGlobalStatsArray(Environment* env, + const bool use_bigint, + const uv_stat_t* s, + const bool second = false); template <typename AliasedBufferT> -class FSReqPromise : public FSReqBase { +class FSReqPromise final : public FSReqBase { public: - static FSReqPromise* New(Environment* env, bool use_bigint) { - v8::Local<Object> obj; - if (!env->fsreqpromise_constructor_template() - ->NewInstance(env->context()) - .ToLocal(&obj)) { - return nullptr; - } - v8::Local<v8::Promise::Resolver> resolver; - if (!v8::Promise::Resolver::New(env->context()).ToLocal(&resolver) || - obj->Set(env->context(), env->promise_string(), resolver).IsNothing()) { - return nullptr; - } - return new FSReqPromise(env, obj, use_bigint); - } - - ~FSReqPromise() override { - // Validate that the promise was explicitly resolved or rejected. - CHECK(finished_); - } - - void Reject(Local<Value> reject) override { - finished_ = true; - HandleScope scope(env()->isolate()); - InternalCallbackScope callback_scope(this); - Local<Value> value = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local<Promise::Resolver> resolver = value.As<Promise::Resolver>(); - USE(resolver->Reject(env()->context(), reject).FromJust()); - } - - void Resolve(Local<Value> value) override { - finished_ = true; - HandleScope scope(env()->isolate()); - InternalCallbackScope callback_scope(this); - Local<Value> val = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local<Promise::Resolver> resolver = val.As<Promise::Resolver>(); - USE(resolver->Resolve(env()->context(), value).FromJust()); - } - - void ResolveStat(const uv_stat_t* stat) override { - FillStatsArray(&stats_field_array_, stat); - Resolve(stats_field_array_.GetJSArray()); - } - - void SetReturnValue(const FunctionCallbackInfo<Value>& args) override { - Local<Value> val = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local<Promise::Resolver> resolver = val.As<Promise::Resolver>(); - args.GetReturnValue().Set(resolver->GetPromise()); - } + static inline FSReqPromise* New(Environment* env, bool use_bigint); + inline ~FSReqPromise() override; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("stats_field_array", stats_field_array_); - tracker->TrackField("continuation_data", continuation_data); - } + inline void Reject(v8::Local<v8::Value> reject) override; + inline void Resolve(v8::Local<v8::Value> value) override; + inline void ResolveStat(const uv_stat_t* stat) override; + inline void SetReturnValue( + const v8::FunctionCallbackInfo<v8::Value>& args) override; + inline void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FSReqPromise) SET_SELF_SIZE(FSReqPromise) @@ -277,17 +141,15 @@ class FSReqPromise : public FSReqBase { FSReqPromise& operator=(const FSReqPromise&&) = delete; private: - FSReqPromise(Environment* env, v8::Local<v8::Object> obj, bool use_bigint) - : FSReqBase(env, obj, AsyncWrap::PROVIDER_FSREQPROMISE, use_bigint), - stats_field_array_( - env->isolate(), - static_cast<size_t>(FsStatsOffset::kFsStatsFieldsNumber)) {} + inline FSReqPromise(Environment* env, + v8::Local<v8::Object> obj, + bool use_bigint); bool finished_ = false; AliasedBufferT stats_field_array_; }; -class FSReqAfterScope { +class FSReqAfterScope final { public: FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req); ~FSReqAfterScope(); @@ -304,17 +166,18 @@ class FSReqAfterScope { private: FSReqBase* wrap_ = nullptr; uv_fs_t* req_ = nullptr; - HandleScope handle_scope_; - Context::Scope context_scope_; + v8::HandleScope handle_scope_; + v8::Context::Scope context_scope_; }; class FileHandle; // A request wrap specifically for uv_fs_read()s scheduled for reading // from a FileHandle. -class FileHandleReadWrap : public ReqWrap<uv_fs_t> { +class FileHandleReadWrap final : public ReqWrap<uv_fs_t> { public: FileHandleReadWrap(FileHandle* handle, v8::Local<v8::Object> obj); + ~FileHandleReadWrap() override; static inline FileHandleReadWrap* from_req(uv_fs_t* req) { return static_cast<FileHandleReadWrap*>(ReqWrap::from_req(req)); @@ -333,7 +196,7 @@ class FileHandleReadWrap : public ReqWrap<uv_fs_t> { // A wrapper for a file descriptor that will automatically close the fd when // the object is garbage collected -class FileHandle : public AsyncWrap, public StreamBase { +class FileHandle final : public AsyncWrap, public StreamBase { public: static FileHandle* New(Environment* env, int fd, @@ -346,10 +209,10 @@ class FileHandle : public AsyncWrap, public StreamBase { // Will asynchronously close the FD and return a Promise that will // be resolved once closing is complete. - static void Close(const FunctionCallbackInfo<Value>& args); + static void Close(const v8::FunctionCallbackInfo<v8::Value>& args); // Releases ownership of the FD. - static void ReleaseFD(const FunctionCallbackInfo<Value>& args); + static void ReleaseFD(const v8::FunctionCallbackInfo<v8::Value>& args); // StreamBase interface: int ReadStart() override; @@ -366,13 +229,9 @@ class FileHandle : public AsyncWrap, public StreamBase { int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, - uv_stream_t* send_handle) override { - return UV_ENOSYS; // Not implemented (yet). - } + uv_stream_t* send_handle) override; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("current_read", current_read_); - } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FileHandle) SET_SELF_SIZE(FileHandle) @@ -389,36 +248,24 @@ class FileHandle : public AsyncWrap, public StreamBase { void Close(); void AfterClose(); - class CloseReq : public ReqWrap<uv_fs_t> { + class CloseReq final : public ReqWrap<uv_fs_t> { public: CloseReq(Environment* env, - Local<Object> obj, - Local<Promise> promise, - Local<Value> ref) - : ReqWrap(env, obj, AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) { - promise_.Reset(env->isolate(), promise); - ref_.Reset(env->isolate(), ref); - } - - ~CloseReq() override { - uv_fs_req_cleanup(req()); - promise_.Reset(); - ref_.Reset(); - } + v8::Local<v8::Object> obj, + v8::Local<v8::Promise> promise, + v8::Local<v8::Value> ref); + ~CloseReq() override; FileHandle* file_handle(); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("promise", promise_); - tracker->TrackField("ref", ref_); - } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(CloseReq) SET_SELF_SIZE(CloseReq) void Resolve(); - void Reject(Local<Value> reason); + void Reject(v8::Local<v8::Value> reason); static CloseReq* from_req(uv_fs_t* req) { return static_cast<CloseReq*>(ReqWrap::from_req(req)); @@ -430,12 +277,12 @@ class FileHandle : public AsyncWrap, public StreamBase { CloseReq& operator=(const CloseReq&&) = delete; private: - v8::Global<Promise> promise_{}; - v8::Global<Value> ref_{}; + v8::Global<v8::Promise> promise_{}; + v8::Global<v8::Value> ref_{}; }; // Asynchronous close - inline MaybeLocal<Promise> ClosePromise(); + v8::MaybeLocal<v8::Promise> ClosePromise(); int fd_; bool closing_ = false; @@ -467,53 +314,23 @@ class FSReqWrapSync { // that nullptr indicates a synchronous call, rather than a failure. // Failure conditions should be disambiguated and handled appropriately. inline FSReqBase* GetReqWrap(Environment* env, v8::Local<v8::Value> value, - bool use_bigint = false) { - if (value->IsObject()) { - return Unwrap<FSReqBase>(value.As<Object>()); - } else if (value->StrictEquals(env->fs_use_promises_symbol())) { - if (use_bigint) { - return FSReqPromise<AliasedBigUint64Array>::New(env, use_bigint); - } else { - return FSReqPromise<AliasedFloat64Array>::New(env, use_bigint); - } - } - return nullptr; -} + bool use_bigint = false); // Returns nullptr if the operation fails from the start. template <typename Func, typename... Args> inline FSReqBase* AsyncDestCall(Environment* env, FSReqBase* req_wrap, - const v8::FunctionCallbackInfo<Value>& args, + const v8::FunctionCallbackInfo<v8::Value>& args, const char* syscall, const char* dest, size_t len, enum encoding enc, uv_fs_cb after, - Func fn, Args... fn_args) { - CHECK_NOT_NULL(req_wrap); - req_wrap->Init(syscall, dest, len, enc); - int err = req_wrap->Dispatch(fn, fn_args..., after); - if (err < 0) { - uv_fs_t* uv_req = req_wrap->req(); - uv_req->result = err; - uv_req->path = nullptr; - after(uv_req); // after may delete req_wrap if there is an error - req_wrap = nullptr; - } else { - req_wrap->SetReturnValue(args); - } - - return req_wrap; -} + Func fn, Args... fn_args); // Returns nullptr if the operation fails from the start. template <typename Func, typename... Args> inline FSReqBase* AsyncCall(Environment* env, FSReqBase* req_wrap, - const v8::FunctionCallbackInfo<Value>& args, + const v8::FunctionCallbackInfo<v8::Value>& args, const char* syscall, enum encoding enc, - uv_fs_cb after, Func fn, Args... fn_args) { - return AsyncDestCall(env, req_wrap, args, - syscall, nullptr, 0, enc, - after, fn, fn_args...); -} + uv_fs_cb after, Func fn, Args... fn_args); // Template counterpart of SYNC_CALL, except that it only puts // the error number and the syscall in the context instead of @@ -522,22 +339,7 @@ inline FSReqBase* AsyncCall(Environment* env, template <typename Func, typename... Args> inline int SyncCall(Environment* env, v8::Local<v8::Value> ctx, FSReqWrapSync* req_wrap, const char* syscall, - Func fn, Args... args) { - env->PrintSyncTrace(); - int err = fn(env->event_loop(), &(req_wrap->req), args..., nullptr); - if (err < 0) { - v8::Local<Context> context = env->context(); - v8::Local<Object> ctx_obj = ctx.As<v8::Object>(); - v8::Isolate* isolate = env->isolate(); - ctx_obj->Set(context, - env->errno_string(), - v8::Integer::New(isolate, err)).Check(); - ctx_obj->Set(context, - env->syscall_string(), - OneByteString(isolate, syscall)).Check(); - } - return err; -} + Func fn, Args... args); } // namespace fs diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index ae30825cbbd..a3f4ef8f505 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -23,7 +23,7 @@ #include "node_stat_watcher.h" #include "async_wrap-inl.h" #include "env.h" -#include "node_file.h" +#include "node_file-inl.h" #include "util-inl.h" #include <cstring> diff --git a/src/req_wrap-inl.h b/src/req_wrap-inl.h index 4fa4d0cf217..c905b605cbd 100644 --- a/src/req_wrap-inl.h +++ b/src/req_wrap-inl.h @@ -120,7 +120,7 @@ struct MakeLibuvRequestCallback<ReqT, void(*)(ReqT*, Args...)> { using F = void(*)(ReqT* req, Args... args); static void Wrapper(ReqT* req, Args... args) { - ReqWrap<ReqT>* req_wrap = ContainerOf(&ReqWrap<ReqT>::req_, req); + ReqWrap<ReqT>* req_wrap = ReqWrap<ReqT>::from_req(req); req_wrap->env()->DecreaseWaitingRequestCounter(); F original_callback = reinterpret_cast<F>(req_wrap->original_callback_); original_callback(req, args...); diff --git a/src/req_wrap.h b/src/req_wrap.h index 36eeb1cbc24..5d7fcb42d01 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -50,9 +50,10 @@ class ReqWrap : public AsyncWrap, public ReqWrapBase { private: friend int GenDebugSymbols(); - template <typename ReqT, typename U> - friend struct MakeLibuvRequestCallback; + // Adding `friend struct MakeLibuvRequestCallback` is not enough anymore + // for some reason. Consider this private. + public: typedef void (*callback_t)(); callback_t original_callback_ = nullptr; |