summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremiah Senkpiel <fishrock123@rocketmail.com>2019-08-27 17:14:27 -0700
committerJeremiah Senkpiel <fishrock123@rocketmail.com>2019-10-08 10:34:48 -0700
commitcbd8d715b2286e5726e6988921f5c870cbf74127 (patch)
tree20c878fd9d7882f2a5b43718a0b3daad4a9a0255 /src
parent064e111515185529b6f943dc66929440557fd609 (diff)
downloadandroid-node-v8-cbd8d715b2286e5726e6988921f5c870cbf74127.tar.gz
android-node-v8-cbd8d715b2286e5726e6988921f5c870cbf74127.tar.bz2
android-node-v8-cbd8d715b2286e5726e6988921f5c870cbf74127.zip
fs: introduce `opendir()` and `fs.Dir`
This adds long-requested methods for asynchronously interacting and iterating through directory entries by using `uv_fs_opendir`, `uv_fs_readdir`, and `uv_fs_closedir`. `fs.opendir()` and friends return an `fs.Dir`, which contains methods for doing reads and cleanup. `fs.Dir` also has the async iterator symbol exposed. The `read()` method and friends only return `fs.Dirent`s for this API. Having a entry type or doing a `stat` call is deemed to be necessary in the majority of cases, so just returning dirents seems like the logical choice for a new api. Reading when there are no more entries returns `null` instead of a dirent. However the async iterator hides that (and does automatic cleanup). The code lives in separate files from the rest of fs, this is done partially to prevent over-pollution of those (already very large) files, but also in the case of js allows loading into `fsPromises`. Due to async_hooks, this introduces a new handle type of `DIRHANDLE`. This PR does not attempt to make complete optimization of this feature. Notable future improvements include: - Moving promise work into C++ land like FileHandle. - Possibly adding `readv()` to do multi-entry directory reads. - Aliasing `fs.readdir` to `fs.scandir` and doing a deprecation. Refs: https://github.com/nodejs/node-v0.x-archive/issues/388 Refs: https://github.com/nodejs/node/issues/583 Refs: https://github.com/libuv/libuv/pull/2057 PR-URL: https://github.com/nodejs/node/pull/29349 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: David Carlier <devnexen@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/async_wrap.h1
-rw-r--r--src/env.h1
-rw-r--r--src/node_binding.cc1
-rw-r--r--src/node_dir.cc350
-rw-r--r--src/node_dir.h60
-rw-r--r--src/node_file.cc90
-rw-r--r--src/node_file.h89
7 files changed, 502 insertions, 90 deletions
diff --git a/src/async_wrap.h b/src/async_wrap.h
index 876bd0c341..2651b5a054 100644
--- a/src/async_wrap.h
+++ b/src/async_wrap.h
@@ -33,6 +33,7 @@ namespace node {
#define NODE_ASYNC_NON_CRYPTO_PROVIDER_TYPES(V) \
V(NONE) \
+ V(DIRHANDLE) \
V(DNSCHANNEL) \
V(ELDHISTOGRAM) \
V(FILEHANDLE) \
diff --git a/src/env.h b/src/env.h
index 534b888e2c..df0df0b99c 100644
--- a/src/env.h
+++ b/src/env.h
@@ -382,6 +382,7 @@ constexpr size_t kFsStatsBufferLength =
V(async_wrap_ctor_template, v8::FunctionTemplate) \
V(async_wrap_object_ctor_template, v8::FunctionTemplate) \
V(compiled_fn_entry_template, v8::ObjectTemplate) \
+ V(dir_instance_template, v8::ObjectTemplate) \
V(fd_constructor_template, v8::ObjectTemplate) \
V(fdclose_constructor_template, v8::ObjectTemplate) \
V(filehandlereadwrap_template, v8::ObjectTemplate) \
diff --git a/src/node_binding.cc b/src/node_binding.cc
index 2deefefb65..f0a148a495 100644
--- a/src/node_binding.cc
+++ b/src/node_binding.cc
@@ -51,6 +51,7 @@
V(domain) \
V(errors) \
V(fs) \
+ V(fs_dir) \
V(fs_event_wrap) \
V(heap_utils) \
V(http2) \
diff --git a/src/node_dir.cc b/src/node_dir.cc
new file mode 100644
index 0000000000..c9df7e67e8
--- /dev/null
+++ b/src/node_dir.cc
@@ -0,0 +1,350 @@
+#include "node_dir.h"
+#include "node_process.h"
+#include "util.h"
+
+#include "tracing/trace_event.h"
+
+#include "req_wrap-inl.h"
+#include "string_bytes.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <cstring>
+#include <cerrno>
+#include <climits>
+
+#include <memory>
+
+namespace node {
+
+namespace fs_dir {
+
+using fs::FSReqAfterScope;
+using fs::FSReqBase;
+using fs::FSReqWrapSync;
+using fs::GetReqWrap;
+
+using v8::Array;
+using v8::Context;
+using v8::Function;
+using v8::FunctionCallbackInfo;
+using v8::FunctionTemplate;
+using v8::HandleScope;
+using v8::Integer;
+using v8::Isolate;
+using v8::Local;
+using v8::MaybeLocal;
+using v8::Null;
+using v8::Object;
+using v8::ObjectTemplate;
+using v8::String;
+using v8::Value;
+
+#define TRACE_NAME(name) "fs_dir.sync." #name
+#define GET_TRACE_ENABLED \
+ (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \
+ (TRACING_CATEGORY_NODE2(fs_dir, sync)) != 0)
+#define FS_DIR_SYNC_TRACE_BEGIN(syscall, ...) \
+ if (GET_TRACE_ENABLED) \
+ TRACE_EVENT_BEGIN(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall), \
+ ##__VA_ARGS__);
+#define FS_DIR_SYNC_TRACE_END(syscall, ...) \
+ if (GET_TRACE_ENABLED) \
+ TRACE_EVENT_END(TRACING_CATEGORY_NODE2(fs_dir, sync), TRACE_NAME(syscall), \
+ ##__VA_ARGS__);
+
+DirHandle::DirHandle(Environment* env, Local<Object> obj, uv_dir_t* dir)
+ : AsyncWrap(env, obj, AsyncWrap::PROVIDER_DIRHANDLE),
+ dir_(dir) {
+ MakeWeak();
+
+ dir_->nentries = 1;
+ dir_->dirents = &dirent_;
+}
+
+DirHandle* DirHandle::New(Environment* env, uv_dir_t* dir) {
+ Local<Object> obj;
+ if (!env->dir_instance_template()
+ ->NewInstance(env->context())
+ .ToLocal(&obj)) {
+ return nullptr;
+ }
+
+ return new DirHandle(env, obj, dir);
+}
+
+void DirHandle::New(const FunctionCallbackInfo<Value>& args) {
+ CHECK(args.IsConstructCall());
+}
+
+DirHandle::~DirHandle() {
+ CHECK(!closing_); // We should not be deleting while explicitly closing!
+ GCClose(); // Close synchronously and emit warning
+ CHECK(closed_); // We have to be closed at the point
+}
+
+// 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
+// will crash the process immediately.
+inline void DirHandle::GCClose() {
+ if (closed_) return;
+ uv_fs_t req;
+ int ret = uv_fs_closedir(nullptr, &req, dir_, nullptr);
+ uv_fs_req_cleanup(&req);
+ closing_ = false;
+ closed_ = true;
+
+ struct err_detail { int ret; };
+
+ err_detail detail { ret };
+
+ if (ret < 0) {
+ // Do not unref this
+ env()->SetImmediate([detail](Environment* env) {
+ char msg[70];
+ snprintf(msg, arraysize(msg),
+ "Closing directory handle on garbage collection failed");
+ // This exception will end up being fatal for the process because
+ // it is being thrown from within the SetImmediate handler and
+ // there is no JS stack to bubble it to. In other words, tearing
+ // down the process is the only reasonable thing we can do here.
+ HandleScope handle_scope(env->isolate());
+ env->ThrowUVException(detail.ret, "close", msg);
+ });
+ return;
+ }
+
+ // If the close was successful, we still want to emit a process warning
+ // to notify that the file descriptor was gc'd. We want to be noisy about
+ // this because not explicitly closing the DirHandle is a bug.
+
+ env()->SetUnrefImmediate([](Environment* env) {
+ ProcessEmitWarning(env,
+ "Closing directory handle on garbage collection");
+ });
+}
+
+void AfterClose(uv_fs_t* req) {
+ FSReqBase* req_wrap = FSReqBase::from_req(req);
+ FSReqAfterScope after(req_wrap, req);
+
+ if (after.Proceed())
+ req_wrap->Resolve(Undefined(req_wrap->env()->isolate()));
+}
+
+void DirHandle::Close(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+
+ const int argc = args.Length();
+ CHECK_GE(argc, 1);
+
+ DirHandle* dir;
+ ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
+
+ dir->closing_ = false;
+ dir->closed_ = true;
+
+ FSReqBase* req_wrap_async = GetReqWrap(env, args[0]);
+ if (req_wrap_async != nullptr) { // close(req)
+ AsyncCall(env, req_wrap_async, args, "closedir", UTF8, AfterClose,
+ uv_fs_closedir, dir->dir());
+ } else { // close(undefined, ctx)
+ CHECK_EQ(argc, 2);
+ FSReqWrapSync req_wrap_sync;
+ FS_DIR_SYNC_TRACE_BEGIN(closedir);
+ SyncCall(env, args[1], &req_wrap_sync, "closedir", uv_fs_closedir,
+ dir->dir());
+ FS_DIR_SYNC_TRACE_END(closedir);
+ }
+}
+
+void AfterDirReadSingle(uv_fs_t* req) {
+ FSReqBase* req_wrap = FSReqBase::from_req(req);
+ FSReqAfterScope after(req_wrap, req);
+
+ if (!after.Proceed()) {
+ return;
+ }
+
+ Environment* env = req_wrap->env();
+ Isolate* isolate = env->isolate();
+ Local<Value> error;
+
+ if (req->result == 0) {
+ // Done
+ Local<Value> done = Null(isolate);
+ req_wrap->Resolve(done);
+ return;
+ }
+
+ uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
+ req->ptr = nullptr;
+
+ // Single entries are returned without an array wrapper
+ const uv_dirent_t& ent = dir->dirents[0];
+
+ MaybeLocal<Value> filename =
+ StringBytes::Encode(isolate,
+ ent.name,
+ req_wrap->encoding(),
+ &error);
+ if (filename.IsEmpty())
+ return req_wrap->Reject(error);
+
+
+ Local<Array> result = Array::New(isolate, 2);
+ result->Set(env->context(),
+ 0,
+ filename.ToLocalChecked()).FromJust();
+ result->Set(env->context(),
+ 1,
+ Integer::New(isolate, ent.type)).FromJust();
+ req_wrap->Resolve(result);
+}
+
+
+void DirHandle::Read(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ Isolate* isolate = env->isolate();
+
+ const int argc = args.Length();
+ CHECK_GE(argc, 2);
+
+ const enum encoding encoding = ParseEncoding(isolate, args[0], UTF8);
+
+ DirHandle* dir;
+ ASSIGN_OR_RETURN_UNWRAP(&dir, args.Holder());
+
+ FSReqBase* req_wrap_async = static_cast<FSReqBase*>(GetReqWrap(env, args[1]));
+ if (req_wrap_async != nullptr) { // dir.read(encoding, req)
+ AsyncCall(env, req_wrap_async, args, "readdir", encoding,
+ AfterDirReadSingle, uv_fs_readdir, dir->dir());
+ } else { // dir.read(encoding, undefined, ctx)
+ CHECK_EQ(argc, 3);
+ FSReqWrapSync req_wrap_sync;
+ FS_DIR_SYNC_TRACE_BEGIN(readdir);
+ int err = SyncCall(env, args[2], &req_wrap_sync, "readdir", uv_fs_readdir,
+ dir->dir());
+ FS_DIR_SYNC_TRACE_END(readdir);
+ if (err < 0) {
+ return; // syscall failed, no need to continue, error info is in ctx
+ }
+
+ if (req_wrap_sync.req.result == 0) {
+ // Done
+ Local<Value> done = Null(isolate);
+ args.GetReturnValue().Set(done);
+ return;
+ }
+
+ CHECK_GE(req_wrap_sync.req.result, 0);
+ const uv_dirent_t& ent = dir->dir()->dirents[0];
+
+ Local<Value> error;
+ MaybeLocal<Value> filename =
+ StringBytes::Encode(isolate,
+ ent.name,
+ encoding,
+ &error);
+ if (filename.IsEmpty()) {
+ Local<Object> ctx = args[2].As<Object>();
+ ctx->Set(env->context(), env->error_string(), error).FromJust();
+ return;
+ }
+
+ Local<Array> result = Array::New(isolate, 2);
+ result->Set(env->context(),
+ 0,
+ filename.ToLocalChecked()).FromJust();
+ result->Set(env->context(),
+ 1,
+ Integer::New(isolate, ent.type)).FromJust();
+ args.GetReturnValue().Set(result);
+ }
+}
+
+void AfterOpenDir(uv_fs_t* req) {
+ FSReqBase* req_wrap = FSReqBase::from_req(req);
+ FSReqAfterScope after(req_wrap, req);
+
+ if (!after.Proceed()) {
+ return;
+ }
+
+ Environment* env = req_wrap->env();
+ Local<Value> error;
+
+ uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
+ DirHandle* handle = DirHandle::New(env, dir);
+
+ req_wrap->Resolve(handle->object().As<Value>());
+}
+
+static void OpenDir(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ Isolate* isolate = env->isolate();
+
+ const int argc = args.Length();
+ CHECK_GE(argc, 3);
+
+ BufferValue path(isolate, args[0]);
+ CHECK_NOT_NULL(*path);
+
+ const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);
+
+ FSReqBase* req_wrap_async = static_cast<FSReqBase*>(GetReqWrap(env, args[2]));
+ if (req_wrap_async != nullptr) { // openDir(path, encoding, req)
+ AsyncCall(env, req_wrap_async, args, "opendir", encoding, AfterOpenDir,
+ uv_fs_opendir, *path);
+ } else { // openDir(path, encoding, undefined, ctx)
+ CHECK_EQ(argc, 4);
+ FSReqWrapSync req_wrap_sync;
+ FS_DIR_SYNC_TRACE_BEGIN(opendir);
+ int result = SyncCall(env, args[3], &req_wrap_sync, "opendir",
+ uv_fs_opendir, *path);
+ FS_DIR_SYNC_TRACE_END(opendir);
+ if (result < 0) {
+ return; // syscall failed, no need to continue, error info is in ctx
+ }
+
+ uv_fs_t* req = &req_wrap_sync.req;
+ uv_dir_t* dir = static_cast<uv_dir_t*>(req->ptr);
+ DirHandle* handle = DirHandle::New(env, dir);
+
+ args.GetReturnValue().Set(handle->object().As<Value>());
+ }
+}
+
+void Initialize(Local<Object> target,
+ Local<Value> unused,
+ Local<Context> context,
+ void* priv) {
+ Environment* env = Environment::GetCurrent(context);
+ Isolate* isolate = env->isolate();
+
+ env->SetMethod(target, "opendir", OpenDir);
+
+ // Create FunctionTemplate for DirHandle
+ Local<FunctionTemplate> dir = env->NewFunctionTemplate(DirHandle::New);
+ dir->Inherit(AsyncWrap::GetConstructorTemplate(env));
+ env->SetProtoMethod(dir, "read", DirHandle::Read);
+ env->SetProtoMethod(dir, "close", DirHandle::Close);
+ Local<ObjectTemplate> dirt = dir->InstanceTemplate();
+ dirt->SetInternalFieldCount(DirHandle::kDirHandleFieldCount);
+ Local<String> handleString =
+ FIXED_ONE_BYTE_STRING(isolate, "DirHandle");
+ dir->SetClassName(handleString);
+ target
+ ->Set(context, handleString,
+ dir->GetFunction(env->context()).ToLocalChecked())
+ .FromJust();
+ env->set_dir_instance_template(dirt);
+}
+
+} // namespace fs_dir
+
+} // end namespace node
+
+NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs_dir, node::fs_dir::Initialize)
diff --git a/src/node_dir.h b/src/node_dir.h
new file mode 100644
index 0000000000..e099fe5510
--- /dev/null
+++ b/src/node_dir.h
@@ -0,0 +1,60 @@
+#ifndef SRC_NODE_DIR_H_
+#define SRC_NODE_DIR_H_
+
+#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#include "node_file.h"
+#include "node.h"
+#include "req_wrap-inl.h"
+
+namespace node {
+
+namespace fs_dir {
+
+// Needed to propagate `uv_dir_t`.
+class DirHandle : public AsyncWrap {
+ public:
+ static constexpr int kDirHandleFieldCount = 1;
+
+ static DirHandle* New(Environment* env, uv_dir_t* dir);
+ ~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);
+
+ inline uv_dir_t* dir() { return dir_; }
+ AsyncWrap* GetAsyncWrap() { return this; }
+
+ void MemoryInfo(MemoryTracker* tracker) const override {
+ tracker->TrackFieldWithSize("dir", sizeof(*dir_));
+ }
+
+ SET_MEMORY_INFO_NAME(DirHandle)
+ SET_SELF_SIZE(DirHandle)
+
+ DirHandle(const DirHandle&) = delete;
+ DirHandle& operator=(const DirHandle&) = delete;
+ DirHandle(const DirHandle&&) = delete;
+ DirHandle& operator=(const DirHandle&&) = delete;
+
+ private:
+ DirHandle(Environment* env, v8::Local<v8::Object> obj, uv_dir_t* dir);
+
+ // Synchronous close that emits a warning
+ void GCClose();
+
+ uv_dir_t* dir_;
+ uv_dirent_t dirent_;
+ bool closing_ = false;
+ bool closed_ = false;
+};
+
+} // namespace fs_dir
+
+} // namespace node
+
+#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#endif // SRC_NODE_DIR_H_
diff --git a/src/node_file.cc b/src/node_file.cc
index 195757f43f..8b6a90989f 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -52,11 +52,9 @@ namespace node {
namespace fs {
using v8::Array;
-using v8::BigUint64Array;
using v8::Context;
using v8::DontDelete;
using v8::EscapableHandleScope;
-using v8::Float64Array;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
@@ -678,94 +676,6 @@ void AfterScanDirWithTypes(uv_fs_t* req) {
req_wrap->Resolve(result);
}
-
-// This class is only used on sync fs calls.
-// For async calls FSReqCallback is used.
-class FSReqWrapSync {
- public:
- FSReqWrapSync() = default;
- ~FSReqWrapSync() { uv_fs_req_cleanup(&req); }
- uv_fs_t req;
-
- FSReqWrapSync(const FSReqWrapSync&) = delete;
- FSReqWrapSync& operator=(const FSReqWrapSync&) = delete;
-};
-
-// Returns nullptr if the operation fails from the start.
-template <typename Func, typename... Args>
-inline FSReqBase* AsyncDestCall(Environment* env,
- FSReqBase* req_wrap,
- const FunctionCallbackInfo<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>
-inline FSReqBase* AsyncCall(Environment* env,
- FSReqBase* req_wrap,
- const FunctionCallbackInfo<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>
-inline int SyncCall(Environment* env, Local<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) {
- Local<Context> context = env->context();
- Local<Object> ctx_obj = ctx.As<Object>();
- Isolate* isolate = env->isolate();
- ctx_obj->Set(context,
- env->errno_string(),
- Integer::New(isolate, err)).Check();
- ctx_obj->Set(context,
- env->syscall_string(),
- OneByteString(isolate, syscall)).Check();
- }
- return err;
-}
-
-// TODO(addaleax): Currently, callers check the return value and assume
-// that nullptr indicates a synchronous call, rather than a failure.
-// Failure conditions should be disambiguated and handled appropriately.
-inline FSReqBase* GetReqWrap(Environment* env, Local<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;
-}
-
void Access(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
diff --git a/src/node_file.h b/src/node_file.h
index 2ea5af025d..84f4032cc2 100644
--- a/src/node_file.h
+++ b/src/node_file.h
@@ -4,7 +4,9 @@
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include "node.h"
+#include "aliased_buffer.h"
#include "stream_base.h"
+#include "memory_tracker-inl.h"
#include "req_wrap-inl.h"
#include <iostream>
@@ -450,6 +452,93 @@ int MKDirpSync(uv_loop_t* loop,
const std::string& path,
int mode,
uv_fs_cb cb = nullptr);
+
+class FSReqWrapSync {
+ public:
+ FSReqWrapSync() = default;
+ ~FSReqWrapSync() { uv_fs_req_cleanup(&req); }
+ uv_fs_t req;
+
+ FSReqWrapSync(const FSReqWrapSync&) = delete;
+ FSReqWrapSync& operator=(const FSReqWrapSync&) = delete;
+};
+
+// TODO(addaleax): Currently, callers check the return value and assume
+// 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;
+}
+
+// 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 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>
+inline FSReqBase* AsyncCall(Environment* env,
+ FSReqBase* req_wrap,
+ const v8::FunctionCallbackInfo<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>
+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;
+}
+
} // namespace fs
} // namespace node