summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/fs.js56
-rw-r--r--lib/internal/fs/promises.js19
-rw-r--r--lib/internal/fs/utils.js16
-rw-r--r--lib/internal/fs/watchers.js4
-rw-r--r--src/env-inl.h5
-rw-r--r--src/env.cc1
-rw-r--r--src/env.h3
-rw-r--r--src/node_file.cc59
-rw-r--r--src/node_file.h19
-rw-r--r--src/node_internals.h12
-rw-r--r--src/node_stat_watcher.cc13
-rw-r--r--src/node_stat_watcher.h3
-rw-r--r--test/parallel/test-fs-stat-bigint.js145
-rw-r--r--test/parallel/test-fs-sync-fd-leak.js2
-rw-r--r--test/parallel/test-fs-watchfile-bigint.js63
15 files changed, 341 insertions, 79 deletions
diff --git a/lib/fs.js b/lib/fs.js
index 0dd2d0d90c..f80bb6ac33 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -248,7 +248,7 @@ function readFileAfterOpen(err, fd) {
const req = new FSReqWrap();
req.oncomplete = readFileAfterStat;
req.context = context;
- binding.fstat(fd, req);
+ binding.fstat(fd, false, req);
}
function readFileAfterStat(err, stats) {
@@ -307,7 +307,7 @@ function readFile(path, options, callback) {
function tryStatSync(fd, isUserFd) {
const ctx = {};
- const stats = binding.fstat(fd, undefined, ctx);
+ const stats = binding.fstat(fd, false, undefined, ctx);
if (ctx.errno !== undefined && !isUserFd) {
fs.closeSync(fd);
throw errors.uvException(ctx);
@@ -760,55 +760,67 @@ function readdirSync(path, options) {
return result;
}
-function fstat(fd, callback) {
+function fstat(fd, options, callback) {
+ if (arguments.length < 3) {
+ callback = options;
+ options = {};
+ }
validateUint32(fd, 'fd');
- const req = new FSReqWrap();
+ const req = new FSReqWrap(options.bigint);
req.oncomplete = makeStatsCallback(callback);
- binding.fstat(fd, req);
+ binding.fstat(fd, options.bigint, req);
}
-function lstat(path, callback) {
+function lstat(path, options, callback) {
+ if (arguments.length < 3) {
+ callback = options;
+ options = {};
+ }
callback = makeStatsCallback(callback);
path = getPathFromURL(path);
validatePath(path);
- const req = new FSReqWrap();
+ const req = new FSReqWrap(options.bigint);
req.oncomplete = callback;
- binding.lstat(pathModule.toNamespacedPath(path), req);
+ binding.lstat(pathModule.toNamespacedPath(path), options.bigint, req);
}
-function stat(path, callback) {
+function stat(path, options, callback) {
+ if (arguments.length < 3) {
+ callback = options;
+ options = {};
+ }
callback = makeStatsCallback(callback);
path = getPathFromURL(path);
validatePath(path);
- const req = new FSReqWrap();
+ const req = new FSReqWrap(options.bigint);
req.oncomplete = callback;
- binding.stat(pathModule.toNamespacedPath(path), req);
+ binding.stat(pathModule.toNamespacedPath(path), options.bigint, req);
}
-function fstatSync(fd) {
+function fstatSync(fd, options = {}) {
validateUint32(fd, 'fd');
const ctx = { fd };
- const stats = binding.fstat(fd, undefined, ctx);
+ const stats = binding.fstat(fd, options.bigint, undefined, ctx);
handleErrorFromBinding(ctx);
return getStatsFromBinding(stats);
}
-function lstatSync(path) {
+function lstatSync(path, options = {}) {
path = getPathFromURL(path);
validatePath(path);
const ctx = { path };
const stats = binding.lstat(pathModule.toNamespacedPath(path),
- undefined, ctx);
+ options.bigint, undefined, ctx);
handleErrorFromBinding(ctx);
return getStatsFromBinding(stats);
}
-function statSync(path) {
+function statSync(path, options = {}) {
path = getPathFromURL(path);
validatePath(path);
const ctx = { path };
const stats = binding.stat(pathModule.toNamespacedPath(path),
- undefined, ctx);
+ options.bigint, undefined, ctx);
handleErrorFromBinding(ctx);
return getStatsFromBinding(stats);
}
@@ -1264,7 +1276,7 @@ function watchFile(filename, options, listener) {
if (stat === undefined) {
if (!watchers)
watchers = require('internal/fs/watchers');
- stat = new watchers.StatWatcher();
+ stat = new watchers.StatWatcher(options.bigint);
stat.start(filename, options.persistent, options.interval);
statWatchers.set(filename, stat);
}
@@ -1379,7 +1391,7 @@ function realpathSync(p, options) {
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
const ctx = { path: base };
- binding.lstat(pathModule.toNamespacedPath(base), undefined, ctx);
+ binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
handleErrorFromBinding(ctx);
knownHard[base] = true;
}
@@ -1421,7 +1433,7 @@ function realpathSync(p, options) {
const baseLong = pathModule.toNamespacedPath(base);
const ctx = { path: base };
- const stats = binding.lstat(baseLong, undefined, ctx);
+ const stats = binding.lstat(baseLong, false, undefined, ctx);
handleErrorFromBinding(ctx);
if (!isFileType(stats, S_IFLNK)) {
@@ -1444,7 +1456,7 @@ function realpathSync(p, options) {
}
if (linkTarget === null) {
const ctx = { path: base };
- binding.stat(baseLong, undefined, ctx);
+ binding.stat(baseLong, false, undefined, ctx);
handleErrorFromBinding(ctx);
linkTarget = binding.readlink(baseLong, undefined, undefined, ctx);
handleErrorFromBinding(ctx);
@@ -1465,7 +1477,7 @@ function realpathSync(p, options) {
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
const ctx = { path: base };
- binding.lstat(pathModule.toNamespacedPath(base), undefined, ctx);
+ binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
handleErrorFromBinding(ctx);
knownHard[base] = true;
}
diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js
index 54a69aa026..750d0d7579 100644
--- a/lib/internal/fs/promises.js
+++ b/lib/internal/fs/promises.js
@@ -81,8 +81,8 @@ class FileHandle {
return readFile(this, options);
}
- stat() {
- return fstat(this);
+ stat(options) {
+ return fstat(this, options);
}
truncate(len = 0) {
@@ -106,7 +106,6 @@ class FileHandle {
}
}
-
function validateFileHandle(handle) {
if (!(handle instanceof FileHandle))
throw new ERR_INVALID_ARG_TYPE('filehandle', 'FileHandle', handle);
@@ -127,7 +126,7 @@ async function writeFileHandle(filehandle, data, options) {
}
async function readFileHandle(filehandle, options) {
- const statFields = await binding.fstat(filehandle.fd, kUsePromises);
+ const statFields = await binding.fstat(filehandle.fd, false, kUsePromises);
let size;
if ((statFields[1/* mode */] & S_IFMT) === S_IFREG) {
@@ -318,25 +317,25 @@ async function symlink(target, path, type_) {
kUsePromises);
}
-async function fstat(handle) {
+async function fstat(handle, options = { bigint: false }) {
validateFileHandle(handle);
- const result = await binding.fstat(handle.fd, kUsePromises);
+ const result = await binding.fstat(handle.fd, options.bigint, kUsePromises);
return getStatsFromBinding(result);
}
-async function lstat(path) {
+async function lstat(path, options = { bigint: false }) {
path = getPathFromURL(path);
validatePath(path);
const result = await binding.lstat(pathModule.toNamespacedPath(path),
- kUsePromises);
+ options.bigint, kUsePromises);
return getStatsFromBinding(result);
}
-async function stat(path) {
+async function stat(path, options = { bigint: false }) {
path = getPathFromURL(path);
validatePath(path);
const result = await binding.stat(pathModule.toNamespacedPath(path),
- kUsePromises);
+ options.bigint, kUsePromises);
return getStatsFromBinding(result);
}
diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js
index a8c64e2b04..7092bbcb63 100644
--- a/lib/internal/fs/utils.js
+++ b/lib/internal/fs/utils.js
@@ -114,7 +114,7 @@ function preprocessSymlinkDestination(path, type, linkPath) {
}
function dateFromNumeric(num) {
- return new Date(num + 0.5);
+ return new Date(Number(num) + 0.5);
}
// Constructor for file stats.
@@ -155,7 +155,15 @@ function Stats(
}
Stats.prototype._checkModeProperty = function(property) {
- return ((this.mode & S_IFMT) === property);
+ if (isWindows && (property === S_IFIFO || property === S_IFBLK ||
+ property === S_IFSOCK)) {
+ return false; // Some types are not available on Windows
+ }
+ if (typeof this.mode === 'bigint') { // eslint-disable-line valid-typeof
+ // eslint-disable-next-line no-undef
+ return (this.mode & BigInt(S_IFMT)) === BigInt(property);
+ }
+ return (this.mode & S_IFMT) === property;
};
Stats.prototype.isDirectory = function() {
@@ -189,9 +197,9 @@ Stats.prototype.isSocket = function() {
function getStatsFromBinding(stats, offset = 0) {
return new Stats(stats[0 + offset], stats[1 + offset], stats[2 + offset],
stats[3 + offset], stats[4 + offset], stats[5 + offset],
- stats[6 + offset] < 0 ? undefined : stats[6 + offset],
+ isWindows ? undefined : stats[6 + offset], // blksize
stats[7 + offset], stats[8 + offset],
- stats[9 + offset] < 0 ? undefined : stats[9 + offset],
+ isWindows ? undefined : stats[9 + offset], // blocks
stats[10 + offset], stats[11 + offset],
stats[12 + offset], stats[13 + offset]);
}
diff --git a/lib/internal/fs/watchers.js b/lib/internal/fs/watchers.js
index 1007a5a136..5fd6948f28 100644
--- a/lib/internal/fs/watchers.js
+++ b/lib/internal/fs/watchers.js
@@ -21,10 +21,10 @@ function emitStop(self) {
self.emit('stop');
}
-function StatWatcher() {
+function StatWatcher(bigint) {
EventEmitter.call(this);
- this._handle = new _StatWatcher();
+ this._handle = new _StatWatcher(bigint);
// uv_fs_poll is a little more powerful than ev_stat but we curb it for
// the sake of backwards compatibility
diff --git a/src/env-inl.h b/src/env-inl.h
index 1652448955..c84fdf0bb8 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -546,6 +546,11 @@ Environment::fs_stats_field_array() {
return &fs_stats_field_array_;
}
+inline AliasedBuffer<uint64_t, v8::BigUint64Array>*
+Environment::fs_stats_field_bigint_array() {
+ return &fs_stats_field_bigint_array_;
+}
+
inline std::vector<std::unique_ptr<fs::FileHandleReadWrap>>&
Environment::file_handle_read_wrap_freelist() {
return file_handle_read_wrap_freelist_;
diff --git a/src/env.cc b/src/env.cc
index 8df59d1546..6f6e9f3920 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -116,6 +116,7 @@ Environment::Environment(IsolateData* isolate_data,
#endif
http_parser_buffer_(nullptr),
fs_stats_field_array_(isolate_, kFsStatsFieldsLength * 2),
+ fs_stats_field_bigint_array_(isolate_, kFsStatsFieldsLength * 2),
context_(context->GetIsolate(), context) {
// We'll be creating new objects so make sure we've entered the context.
v8::HandleScope handle_scope(isolate());
diff --git a/src/env.h b/src/env.h
index ee734fe920..9ad316c063 100644
--- a/src/env.h
+++ b/src/env.h
@@ -694,6 +694,8 @@ class Environment {
void set_debug_categories(const std::string& cats, bool enabled);
inline AliasedBuffer<double, v8::Float64Array>* fs_stats_field_array();
+ inline AliasedBuffer<uint64_t, v8::BigUint64Array>*
+ fs_stats_field_bigint_array();
// stat fields contains twice the number of entries because `fs.StatWatcher`
// needs room to store data for *two* `fs.Stats` instances.
@@ -914,6 +916,7 @@ class Environment {
bool debug_enabled_[static_cast<int>(DebugCategory::CATEGORY_COUNT)] = {0};
AliasedBuffer<double, v8::Float64Array> fs_stats_field_array_;
+ AliasedBuffer<uint64_t, v8::BigUint64Array> fs_stats_field_bigint_array_;
std::vector<std::unique_ptr<fs::FileHandleReadWrap>>
file_handle_read_wrap_freelist_;
diff --git a/src/node_file.cc b/src/node_file.cc
index d4f3cbfcfb..a63e90fafb 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -49,6 +49,7 @@ namespace node {
namespace fs {
using v8::Array;
+using v8::BigUint64Array;
using v8::Context;
using v8::EscapableHandleScope;
using v8::Float64Array;
@@ -413,7 +414,7 @@ void FSReqWrap::Reject(Local<Value> reject) {
}
void FSReqWrap::ResolveStat(const uv_stat_t* stat) {
- Resolve(node::FillGlobalStatsArray(env(), stat));
+ Resolve(node::FillGlobalStatsArray(env(), stat, use_bigint()));
}
void FSReqWrap::Resolve(Local<Value> value) {
@@ -433,7 +434,7 @@ void FSReqWrap::SetReturnValue(const FunctionCallbackInfo<Value>& args) {
void NewFSReqWrap(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
Environment* env = Environment::GetCurrent(args.GetIsolate());
- new FSReqWrap(env, args.This());
+ new FSReqWrap(env, args.This(), args[0]->IsTrue());
}
FSReqAfterScope::FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req)
@@ -670,11 +671,16 @@ inline int SyncCall(Environment* env, Local<Value> ctx, FSReqWrapSync* req_wrap,
return err;
}
-inline FSReqBase* GetReqWrap(Environment* env, Local<Value> value) {
+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())) {
- return new FSReqPromise<double, Float64Array>(env);
+ if (use_bigint) {
+ return new FSReqPromise<uint64_t, BigUint64Array>(env, use_bigint);
+ } else {
+ return new FSReqPromise<double, Float64Array>(env, use_bigint);
+ }
}
return nullptr;
}
@@ -825,22 +831,23 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
BufferValue path(env->isolate(), args[0]);
CHECK_NOT_NULL(*path);
- FSReqBase* req_wrap_async = GetReqWrap(env, args[1]);
- if (req_wrap_async != nullptr) { // stat(path, req)
+ bool use_bigint = args[1]->IsTrue();
+ FSReqBase* req_wrap_async = GetReqWrap(env, args[2], use_bigint);
+ if (req_wrap_async != nullptr) { // stat(path, use_bigint, req)
AsyncCall(env, req_wrap_async, args, "stat", UTF8, AfterStat,
uv_fs_stat, *path);
- } else { // stat(path, undefined, ctx)
- CHECK_EQ(argc, 3);
+ } else { // stat(path, use_bigint, undefined, ctx)
+ CHECK_EQ(argc, 4);
FSReqWrapSync req_wrap_sync;
FS_SYNC_TRACE_BEGIN(stat);
- int err = SyncCall(env, args[2], &req_wrap_sync, "stat", uv_fs_stat, *path);
+ int err = SyncCall(env, args[3], &req_wrap_sync, "stat", uv_fs_stat, *path);
FS_SYNC_TRACE_END(stat);
if (err != 0) {
return; // error info is in ctx
}
Local<Value> arr = node::FillGlobalStatsArray(env,
- static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
+ static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr), use_bigint);
args.GetReturnValue().Set(arr);
}
}
@@ -849,20 +856,21 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
const int argc = args.Length();
- CHECK_GE(argc, 2);
+ CHECK_GE(argc, 3);
BufferValue path(env->isolate(), args[0]);
CHECK_NOT_NULL(*path);
- FSReqBase* req_wrap_async = GetReqWrap(env, args[1]);
- if (req_wrap_async != nullptr) { // lstat(path, req)
+ bool use_bigint = args[1]->IsTrue();
+ FSReqBase* req_wrap_async = GetReqWrap(env, args[2], use_bigint);
+ if (req_wrap_async != nullptr) { // lstat(path, use_bigint, req)
AsyncCall(env, req_wrap_async, args, "lstat", UTF8, AfterStat,
uv_fs_lstat, *path);
- } else { // lstat(path, undefined, ctx)
- CHECK_EQ(argc, 3);
+ } else { // lstat(path, use_bigint, undefined, ctx)
+ CHECK_EQ(argc, 4);
FSReqWrapSync req_wrap_sync;
FS_SYNC_TRACE_BEGIN(lstat);
- int err = SyncCall(env, args[2], &req_wrap_sync, "lstat", uv_fs_lstat,
+ int err = SyncCall(env, args[3], &req_wrap_sync, "lstat", uv_fs_lstat,
*path);
FS_SYNC_TRACE_END(lstat);
if (err != 0) {
@@ -870,7 +878,7 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
}
Local<Value> arr = node::FillGlobalStatsArray(env,
- static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
+ static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr), use_bigint);
args.GetReturnValue().Set(arr);
}
}
@@ -884,22 +892,23 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsInt32());
int fd = args[0].As<Int32>()->Value();
- FSReqBase* req_wrap_async = GetReqWrap(env, args[1]);
- if (req_wrap_async != nullptr) { // fstat(fd, req)
+ bool use_bigint = args[1]->IsTrue();
+ FSReqBase* req_wrap_async = GetReqWrap(env, args[2], use_bigint);
+ if (req_wrap_async != nullptr) { // fstat(fd, use_bigint, req)
AsyncCall(env, req_wrap_async, args, "fstat", UTF8, AfterStat,
uv_fs_fstat, fd);
- } else { // fstat(fd, undefined, ctx)
- CHECK_EQ(argc, 3);
+ } else { // fstat(fd, use_bigint, undefined, ctx)
+ CHECK_EQ(argc, 4);
FSReqWrapSync req_wrap_sync;
FS_SYNC_TRACE_BEGIN(fstat);
- int err = SyncCall(env, args[2], &req_wrap_sync, "fstat", uv_fs_fstat, fd);
+ int err = SyncCall(env, args[3], &req_wrap_sync, "fstat", uv_fs_fstat, fd);
FS_SYNC_TRACE_END(fstat);
if (err != 0) {
return; // error info is in ctx
}
Local<Value> arr = node::FillGlobalStatsArray(env,
- static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr));
+ static_cast<const uv_stat_t*>(req_wrap_sync.req.ptr), use_bigint);
args.GetReturnValue().Set(arr);
}
}
@@ -1911,6 +1920,10 @@ void Initialize(Local<Object> target,
FIXED_ONE_BYTE_STRING(env->isolate(), "statValues"),
env->fs_stats_field_array()->GetJSArray()).FromJust();
+ target->Set(context,
+ FIXED_ONE_BYTE_STRING(env->isolate(), "bigintStatValues"),
+ env->fs_stats_field_bigint_array()->GetJSArray()).FromJust();
+
StatWatcher::Initialize(env, target);
// Create FunctionTemplate for FSReqWrap
diff --git a/src/node_file.h b/src/node_file.h
index 03e41097d5..a14a1b0f85 100644
--- a/src/node_file.h
+++ b/src/node_file.h
@@ -26,8 +26,9 @@ class FSReqBase : public ReqWrap<uv_fs_t> {
public:
typedef MaybeStackBuffer<char, 64> FSReqBuffer;
- FSReqBase(Environment* env, Local<Object> req, AsyncWrap::ProviderType type)
- : ReqWrap(env, req, type) {
+ 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,
@@ -66,11 +67,13 @@ class FSReqBase : public ReqWrap<uv_fs_t> {
enum encoding encoding() const { return encoding_; }
size_t self_size() const override { return sizeof(*this); }
+ bool use_bigint() const { return use_bigint_; }
private:
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.
@@ -81,8 +84,8 @@ class FSReqBase : public ReqWrap<uv_fs_t> {
class FSReqWrap : public FSReqBase {
public:
- FSReqWrap(Environment* env, Local<Object> req)
- : FSReqBase(env, req, AsyncWrap::PROVIDER_FSREQWRAP) { }
+ FSReqWrap(Environment* env, Local<Object> req, bool use_bigint)
+ : FSReqBase(env, req, AsyncWrap::PROVIDER_FSREQWRAP, use_bigint) { }
void Reject(Local<Value> reject) override;
void Resolve(Local<Value> value) override;
@@ -96,11 +99,12 @@ class FSReqWrap : public FSReqBase {
template <typename NativeT = double, typename V8T = v8::Float64Array>
class FSReqPromise : public FSReqBase {
public:
- explicit FSReqPromise(Environment* env)
+ explicit FSReqPromise(Environment* env, bool use_bigint)
: FSReqBase(env,
env->fsreqpromise_constructor_template()
->NewInstance(env->context()).ToLocalChecked(),
- AsyncWrap::PROVIDER_FSREQPROMISE),
+ AsyncWrap::PROVIDER_FSREQPROMISE,
+ use_bigint),
stats_field_array_(env->isolate(), env->kFsStatsFieldsLength) {
auto resolver = Promise::Resolver::New(env->context()).ToLocalChecked();
object()->Set(env->context(), env->promise_string(),
@@ -135,8 +139,7 @@ class FSReqPromise : public FSReqBase {
}
void ResolveStat(const uv_stat_t* stat) override {
- node::FillStatsArray(&stats_field_array_, stat);
- Resolve(stats_field_array_.GetJSArray());
+ Resolve(node::FillStatsArray(&stats_field_array_, stat));
}
void SetReturnValue(const FunctionCallbackInfo<Value>& args) override {
diff --git a/src/node_internals.h b/src/node_internals.h
index f76aacd7e8..49f1d4f230 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -337,14 +337,14 @@ v8::Local<v8::Value> FillStatsArray(AliasedBuffer<NativeT, V8T>* fields_ptr,
#if defined(__POSIX__)
fields[offset + 6] = s->st_blksize;
#else
- fields[offset + 6] = -1;
+ fields[offset + 6] = 0;
#endif
fields[offset + 7] = s->st_ino;
fields[offset + 8] = s->st_size;
#if defined(__POSIX__)
fields[offset + 9] = s->st_blocks;
#else
- fields[offset + 9] = -1;
+ fields[offset + 9] = 0;
#endif
// Dates.
// NO-LINT because the fields are 'long' and we just want to cast to `unsigned`
@@ -365,8 +365,14 @@ v8::Local<v8::Value> FillStatsArray(AliasedBuffer<NativeT, V8T>* fields_ptr,
inline v8::Local<v8::Value> FillGlobalStatsArray(Environment* env,
const uv_stat_t* s,
+ bool use_bigint = false,
int offset = 0) {
- return node::FillStatsArray(env->fs_stats_field_array(), s, offset);
+ if (use_bigint) {
+ return node::FillStatsArray(
+ env->fs_stats_field_bigint_array(), s, offset);
+ } else {
+ return node::FillStatsArray(env->fs_stats_field_array(), s, offset);
+ }
}
void SetupBootstrapObject(Environment* env,
diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc
index 3749c2e21f..3f7da197b2 100644
--- a/src/node_stat_watcher.cc
+++ b/src/node_stat_watcher.cc
@@ -75,9 +75,10 @@ void StatWatcher::Initialize(Environment* env, Local<Object> target) {
}
-StatWatcher::StatWatcher(Environment* env, Local<Object> wrap)
+StatWatcher::StatWatcher(Environment* env, Local<Object> wrap, bool use_bigint)
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_STATWATCHER),
- watcher_(new uv_fs_poll_t) {
+ watcher_(new uv_fs_poll_t),
+ use_bigint_(use_bigint) {
MakeWeak();
uv_fs_poll_init(env->event_loop(), watcher_);
watcher_->data = static_cast<void*>(this);
@@ -102,8 +103,10 @@ void StatWatcher::Callback(uv_fs_poll_t* handle,
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
- Local<Value> arr = node::FillGlobalStatsArray(env, curr);
- node::FillGlobalStatsArray(env, prev, env->kFsStatsFieldsLength);
+ Local<Value> arr = node::FillGlobalStatsArray(env, curr,
+ wrap->use_bigint_);
+ node::FillGlobalStatsArray(env, prev, wrap->use_bigint_,
+ env->kFsStatsFieldsLength);
Local<Value> argv[2] {
Integer::New(env->isolate(), status),
@@ -116,7 +119,7 @@ void StatWatcher::Callback(uv_fs_poll_t* handle,
void StatWatcher::New(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall());
Environment* env = Environment::GetCurrent(args);
- new StatWatcher(env, args.This());
+ new StatWatcher(env, args.This(), args[0]->IsTrue());
}
bool StatWatcher::IsActive() {
diff --git a/src/node_stat_watcher.h b/src/node_stat_watcher.h
index 587203ff1e..0d0d263d5c 100644
--- a/src/node_stat_watcher.h
+++ b/src/node_stat_watcher.h
@@ -39,7 +39,7 @@ class StatWatcher : public AsyncWrap {
static void Initialize(Environment* env, v8::Local<v8::Object> target);
protected:
- StatWatcher(Environment* env, v8::Local<v8::Object> wrap);
+ StatWatcher(Environment* env, v8::Local<v8::Object> wrap, bool use_bigint);
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -57,6 +57,7 @@ class StatWatcher : public AsyncWrap {
bool IsActive();
uv_fs_poll_t* watcher_;
+ const bool use_bigint_;
};
} // namespace node
diff --git a/test/parallel/test-fs-stat-bigint.js b/test/parallel/test-fs-stat-bigint.js
new file mode 100644
index 0000000000..4030a06fae
--- /dev/null
+++ b/test/parallel/test-fs-stat-bigint.js
@@ -0,0 +1,145 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+const fs = require('fs');
+const promiseFs = require('fs').promises;
+const path = require('path');
+const tmpdir = require('../common/tmpdir');
+const { isDate } = require('util').types;
+
+common.crashOnUnhandledRejection();
+tmpdir.refresh();
+
+const fn = path.join(tmpdir.path, 'test-file');
+fs.writeFileSync(fn, 'test');
+
+let link;
+if (!common.isWindows) {
+ link = path.join(tmpdir.path, 'symbolic-link');
+ fs.symlinkSync(fn, link);
+}
+
+function verifyStats(bigintStats, numStats) {
+ for (const key of Object.keys(numStats)) {
+ const val = numStats[key];
+ if (isDate(val)) {
+ const time = val.getTime();
+ const time2 = bigintStats[key].getTime();
+ assert(
+ Math.abs(time - time2) < 2,
+ `difference of ${key}.getTime() should < 2.\n` +
+ `Number version ${time}, BigInt version ${time2}n`);
+ } else if (key === 'mode') {
+ // eslint-disable-next-line no-undef
+ assert.strictEqual(bigintStats[key], BigInt(val));
+ assert.strictEqual(
+ bigintStats.isBlockDevice(),
+ numStats.isBlockDevice()
+ );
+ assert.strictEqual(
+ bigintStats.isCharacterDevice(),
+ numStats.isCharacterDevice()
+ );
+ assert.strictEqual(
+ bigintStats.isDirectory(),
+ numStats.isDirectory()
+ );
+ assert.strictEqual(
+ bigintStats.isFIFO(),
+ numStats.isFIFO()
+ );
+ assert.strictEqual(
+ bigintStats.isFile(),
+ numStats.isFile()
+ );
+ assert.strictEqual(
+ bigintStats.isSocket(),
+ numStats.isSocket()
+ );
+ assert.strictEqual(
+ bigintStats.isSymbolicLink(),
+ numStats.isSymbolicLink()
+ );
+ } else if (common.isWindows && (key === 'blksize' || key === 'blocks')) {
+ assert.strictEqual(bigintStats[key], undefined);
+ assert.strictEqual(numStats[key], undefined);
+ } else if (Number.isSafeInteger(val)) {
+ // eslint-disable-next-line no-undef
+ assert.strictEqual(bigintStats[key], BigInt(val));
+ } else {
+ assert(
+ Math.abs(Number(bigintStats[key]) - val) < 1,
+ `${key} is not a safe integer, difference should < 1.\n` +
+ `Number version ${val}, BigInt version ${bigintStats[key]}n`);
+ }
+ }
+}
+
+{
+ const bigintStats = fs.statSync(fn, { bigint: true });
+ const numStats = fs.statSync(fn);
+ verifyStats(bigintStats, numStats);
+}
+
+if (!common.isWindows) {
+ const bigintStats = fs.lstatSync(link, { bigint: true });
+ const numStats = fs.lstatSync(link);
+ verifyStats(bigintStats, numStats);
+}
+
+{
+ const fd = fs.openSync(fn, 'r');
+ const bigintStats = fs.fstatSync(fd, { bigint: true });
+ const numStats = fs.fstatSync(fd);
+ verifyStats(bigintStats, numStats);
+ fs.closeSync(fd);
+}
+
+{
+ fs.stat(fn, { bigint: true }, (err, bigintStats) => {
+ fs.stat(fn, (err, numStats) => {
+ verifyStats(bigintStats, numStats);
+ });
+ });
+}
+
+if (!common.isWindows) {
+ fs.lstat(link, { bigint: true }, (err, bigintStats) => {
+ fs.lstat(link, (err, numStats) => {
+ verifyStats(bigintStats, numStats);
+ });
+ });
+}
+
+{
+ const fd = fs.openSync(fn, 'r');
+ fs.fstat(fd, { bigint: true }, (err, bigintStats) => {
+ fs.fstat(fd, (err, numStats) => {
+ verifyStats(bigintStats, numStats);
+ fs.closeSync(fd);
+ });
+ });
+}
+
+(async function() {
+ const bigintStats = await promiseFs.stat(fn, { bigint: true });
+ const numStats = await promiseFs.stat(fn);
+ verifyStats(bigintStats, numStats);
+})();
+
+if (!common.isWindows) {
+ (async function() {
+ const bigintStats = await promiseFs.lstat(link, { bigint: true });
+ const numStats = await promiseFs.lstat(link);
+ verifyStats(bigintStats, numStats);
+ })();
+}
+
+(async function() {
+ const handle = await promiseFs.open(fn, 'r');
+ const bigintStats = await handle.stat({ bigint: true });
+ const numStats = await handle.stat();
+ verifyStats(bigintStats, numStats);
+ await handle.close();
+})();
diff --git a/test/parallel/test-fs-sync-fd-leak.js b/test/parallel/test-fs-sync-fd-leak.js
index e7107546e5..52e40eb390 100644
--- a/test/parallel/test-fs-sync-fd-leak.js
+++ b/test/parallel/test-fs-sync-fd-leak.js
@@ -40,7 +40,7 @@ fs.writeSync = function() {
throw new Error('BAM');
};
-process.binding('fs').fstat = function(fd, _, ctx) {
+process.binding('fs').fstat = function(fd, bigint, _, ctx) {
ctx.errno = uv.UV_EBADF;
ctx.syscall = 'fstat';
};
diff --git a/test/parallel/test-fs-watchfile-bigint.js b/test/parallel/test-fs-watchfile-bigint.js
new file mode 100644
index 0000000000..89cefd12e0
--- /dev/null
+++ b/test/parallel/test-fs-watchfile-bigint.js
@@ -0,0 +1,63 @@
+'use strict';
+const common = require('../common');
+
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+
+const tmpdir = require('../common/tmpdir');
+
+const enoentFile = path.join(tmpdir.path, 'non-existent-file');
+const expectedStatObject = new fs.Stats(
+ 0n, // dev
+ 0n, // mode
+ 0n, // nlink
+ 0n, // uid
+ 0n, // gid
+ 0n, // rdev
+ common.isWindows ? undefined : 0n, // blksize
+ 0n, // ino
+ 0n, // size
+ common.isWindows ? undefined : 0n, // blocks
+ 0n, // atim_msec
+ 0n, // mtim_msec
+ 0n, // ctim_msec
+ 0n // birthtim_msec
+);
+
+tmpdir.refresh();
+
+// If the file initially didn't exist, and gets created at a later point of
+// time, the callback should be invoked again with proper values in stat object
+let fileExists = false;
+const options = { interval: 0, bigint: true };
+
+const watcher =
+ fs.watchFile(enoentFile, options, common.mustCall((curr, prev) => {
+ if (!fileExists) {
+ // If the file does not exist, all the fields should be zero and the date
+ // fields should be UNIX EPOCH time
+ assert.deepStrictEqual(curr, expectedStatObject);
+ assert.deepStrictEqual(prev, expectedStatObject);
+ // Create the file now, so that the callback will be called back once the
+ // event loop notices it.
+ fs.closeSync(fs.openSync(enoentFile, 'w'));
+ fileExists = true;
+ } else {
+ // If the ino (inode) value is greater than zero, it means that the file
+ // is present in the filesystem and it has a valid inode number.
+ assert(curr.ino > 0n);
+ // As the file just got created, previous ino value should be lesser than
+ // or equal to zero (non-existent file).
+ assert(prev.ino <= 0n);
+ // Stop watching the file
+ fs.unwatchFile(enoentFile);
+ watcher.stop(); // stopping a stopped watcher should be a noop
+ }
+ }, 2));
+
+// 'stop' should only be emitted once - stopping a stopped watcher should
+// not trigger a 'stop' event.
+watcher.on('stop', common.mustCall(function onStop() {}));
+
+watcher.start(); // starting a started watcher should be a noop