Skip to content

Commit

Permalink
fs: support BigInt in fs.*stat and fs.watchFile
Browse files Browse the repository at this point in the history
Add the `bigint: true` option to all the `fs.*stat` methods and
`fs.watchFile`.

PR-URL: #20220
Fixes: #12115
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information
joyeecheung authored and targos committed Jun 13, 2018
1 parent ea4be72 commit 04e8f07
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 79 deletions.
56 changes: 34 additions & 22 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down
19 changes: 9 additions & 10 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ class FileHandle {
return readFile(this, options);
}

stat() {
return fstat(this);
stat(options) {
return fstat(this, options);
}

truncate(len = 0) {
Expand All @@ -106,7 +106,6 @@ class FileHandle {
}
}


function validateFileHandle(handle) {
if (!(handle instanceof FileHandle))
throw new ERR_INVALID_ARG_TYPE('filehandle', 'FileHandle', handle);
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}

Expand Down
16 changes: 12 additions & 4 deletions lib/internal/fs/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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]);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/internal/fs/watchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
1 change: 1 addition & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
3 changes: 3 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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_;
Expand Down
Loading

0 comments on commit 04e8f07

Please sign in to comment.