diff options
author | Jeremiah Senkpiel <fishrock123@rocketmail.com> | 2019-08-27 17:14:27 -0700 |
---|---|---|
committer | Jeremiah Senkpiel <fishrock123@rocketmail.com> | 2019-10-08 10:34:48 -0700 |
commit | cbd8d715b2286e5726e6988921f5c870cbf74127 (patch) | |
tree | 20c878fd9d7882f2a5b43718a0b3daad4a9a0255 /src | |
parent | 064e111515185529b6f943dc66929440557fd609 (diff) | |
download | android-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.h | 1 | ||||
-rw-r--r-- | src/env.h | 1 | ||||
-rw-r--r-- | src/node_binding.cc | 1 | ||||
-rw-r--r-- | src/node_dir.cc | 350 | ||||
-rw-r--r-- | src/node_dir.h | 60 | ||||
-rw-r--r-- | src/node_file.cc | 90 | ||||
-rw-r--r-- | src/node_file.h | 89 |
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) \ @@ -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 |