lts/src/node_file.h
#ifndef SRC_NODE_FILE_H_
#define SRC_NODE_FILE_H_
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#include "node.h"
#include "aliased_buffer.h"
#include "stream_base.h"
#include <iostream>
namespace node {
namespace fs {
// structure used to store state during a complex operation, e.g., mkdirp.
class FSContinuationData : public MemoryRetainer {
public:
inline FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb);
inline void PushPath(std::string&& path);
inline void PushPath(const std::string& path);
inline std::string PopPath();
// Used by mkdirp to track the first path created:
inline void MaybeSetFirstPath(const std::string& path);
inline void Done(int result);
int mode() const { return mode_; }
const std::vector<std::string>& paths() const { return paths_; }
const std::string& first_path() const { return first_path_; }
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(FSContinuationData)
SET_SELF_SIZE(FSContinuationData)
private:
uv_fs_cb done_cb_;
uv_fs_t* req_;
int mode_;
std::vector<std::string> paths_;
std::string first_path_;
};
class FSReqBase : public ReqWrap<uv_fs_t> {
public:
typedef MaybeStackBuffer<char, 64> FSReqBuffer;
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 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));
}
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;
bool use_bigint_ = false;
// Typically, the content of buffer_ is something like a file name, so
// something around 64 bytes should be enough.
FSReqBuffer buffer_;
};
class FSReqCallback final : public FSReqBase {
public:
inline FSReqCallback(Environment* env,
v8::Local<v8::Object> req,
bool use_bigint);
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 v8::FunctionCallbackInfo<v8::Value>& args) override;
SET_MEMORY_INFO_NAME(FSReqCallback)
SET_SELF_SIZE(FSReqCallback)
FSReqCallback(const FSReqCallback&) = delete;
FSReqCallback& operator=(const FSReqCallback&) = delete;
};
template <typename NativeT, typename V8T>
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 final : public FSReqBase {
public:
static inline FSReqPromise* New(Environment* env, bool use_bigint);
inline ~FSReqPromise() override;
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)
FSReqPromise(const FSReqPromise&) = delete;
FSReqPromise& operator=(const FSReqPromise&) = delete;
FSReqPromise(const FSReqPromise&&) = delete;
FSReqPromise& operator=(const FSReqPromise&&) = delete;
private:
inline FSReqPromise(Environment* env,
v8::Local<v8::Object> obj,
bool use_bigint);
bool finished_ = false;
AliasedBufferT stats_field_array_;
};
class FSReqAfterScope final {
public:
FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req);
~FSReqAfterScope();
void Clear();
bool Proceed();
void Reject(uv_fs_t* req);
FSReqAfterScope(const FSReqAfterScope&) = delete;
FSReqAfterScope& operator=(const FSReqAfterScope&) = delete;
FSReqAfterScope(const FSReqAfterScope&&) = delete;
FSReqAfterScope& operator=(const FSReqAfterScope&&) = delete;
private:
BaseObjectPtr<FSReqBase> wrap_;
uv_fs_t* req_ = nullptr;
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 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));
}
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(FileHandleReadWrap)
SET_SELF_SIZE(FileHandleReadWrap)
private:
FileHandle* file_handle_;
uv_buf_t buffer_;
friend class FileHandle;
};
// A wrapper for a file descriptor that will automatically close the fd when
// the object is garbage collected
class FileHandle final : public AsyncWrap, public StreamBase {
public:
static FileHandle* New(Environment* env,
int fd,
v8::Local<v8::Object> obj = v8::Local<v8::Object>());
~FileHandle() override;
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
int GetFD() override { return fd_; }
// Will asynchronously close the FD and return a Promise that will
// be resolved once closing is complete.
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
// Releases ownership of the FD.
static void ReleaseFD(const v8::FunctionCallbackInfo<v8::Value>& args);
// StreamBase interface:
int ReadStart() override;
int ReadStop() override;
bool IsAlive() override { return !closed_; }
bool IsClosing() override { return closing_; }
AsyncWrap* GetAsyncWrap() override { return this; }
// In the case of file streams, shutting down corresponds to closing.
ShutdownWrap* CreateShutdownWrap(v8::Local<v8::Object> object) override;
int DoShutdown(ShutdownWrap* req_wrap) override;
int DoWrite(WriteWrap* w,
uv_buf_t* bufs,
size_t count,
uv_stream_t* send_handle) override;
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(FileHandle)
SET_SELF_SIZE(FileHandle)
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
FileHandle(const FileHandle&&) = delete;
FileHandle& operator=(const FileHandle&&) = delete;
private:
FileHandle(Environment* env, v8::Local<v8::Object> obj, int fd);
// Synchronous close that emits a warning
void Close();
void AfterClose();
class CloseReq final : public ReqWrap<uv_fs_t> {
public:
CloseReq(Environment* env,
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;
SET_MEMORY_INFO_NAME(CloseReq)
SET_SELF_SIZE(CloseReq)
void Resolve();
void Reject(v8::Local<v8::Value> reason);
static CloseReq* from_req(uv_fs_t* req) {
return static_cast<CloseReq*>(ReqWrap::from_req(req));
}
CloseReq(const CloseReq&) = delete;
CloseReq& operator=(const CloseReq&) = delete;
CloseReq(const CloseReq&&) = delete;
CloseReq& operator=(const CloseReq&&) = delete;
private:
v8::Global<v8::Promise> promise_{};
v8::Global<v8::Value> ref_{};
};
// Asynchronous close
v8::MaybeLocal<v8::Promise> ClosePromise();
int fd_;
bool closing_ = false;
bool closed_ = false;
int64_t read_offset_ = -1;
int64_t read_length_ = -1;
bool reading_ = false;
std::unique_ptr<FileHandleReadWrap> current_read_ = nullptr;
};
int MKDirpSync(uv_loop_t* loop,
uv_fs_t* req,
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;
FSContinuationData* continuation_data() const {
return continuation_data_.get();
}
void set_continuation_data(std::unique_ptr<FSContinuationData> data) {
continuation_data_ = std::move(data);
}
FSReqWrapSync(const FSReqWrapSync&) = delete;
FSReqWrapSync& operator=(const FSReqWrapSync&) = delete;
private:
std::unique_ptr<FSContinuationData> continuation_data_;
};
// 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);
// 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<v8::Value>& args,
const char* syscall, const char* dest,
size_t len, enum encoding enc, uv_fs_cb after,
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<v8::Value>& args,
const char* syscall, enum encoding enc,
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
// 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);
} // namespace fs
} // namespace node
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#endif // SRC_NODE_FILE_H_