diff options
Diffstat (limited to 'src/node_file-inl.h')
-rw-r--r-- | src/node_file-inl.h | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/src/node_file-inl.h b/src/node_file-inl.h new file mode 100644 index 0000000000..390f6c7415 --- /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_ |