fs: include more fs.stat*() optimizations

Including:

* Move async *stat() functions to FillStatsArray() now used by the
sync *stat() functions

* Avoid creating fs.Stats instances for implicit async/sync *stat()
calls used in various fs functions

* Store reference to Float64Array data on C++ side for easier/faster
access, instead of passing from JS to C++ on every async/sync *stat()
call

PR-URL: https://github.com/nodejs/node/pull/11665
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Brian White authored and James M Snell committed 2017-03-14 21:54:40 -07:00
1 parent 3d7245c098
commit 6a5ab5d550
10 files changed
+205 -244

No files matched your search

+16 -6
View File
@@ -4,20 +4,30 @@ const common = require('../common');
const fs = require('fs');
const bench = common.createBenchmark(main, {
n: [1e4],
kind: ['lstat', 'stat']
n: [20e4],
kind: ['fstat', 'lstat', 'stat']
});
function main(conf) {
const n = conf.n >>> 0;
const kind = conf.kind;
var arg;
if (kind === 'fstat')
arg = fs.openSync(__filename, 'r');
else
arg = __filename;
bench.start();
(function r(cntr, fn) {
if (cntr-- <= 0)
return bench.end(n);
fn(__filename, function() {
if (cntr-- <= 0) {
bench.end(n);
if (kind === 'fstat')
fs.closeSync(arg);
return;
}
fn(arg, function() {
r(cntr, fn);
});
}(n, fs[conf.kind]));
}(n, fs[kind]));
}
+12 -25
View File
@@ -4,36 +4,23 @@ const common = require('../common');
const fs = require('fs');
const bench = common.createBenchmark(main, {
n: [1e4],
n: [1e6],
kind: ['fstatSync', 'lstatSync', 'statSync']
});
function main(conf) {
const n = conf.n >>> 0;
var fn;
var i;
switch (conf.kind) {
case 'statSync':
case 'lstatSync':
fn = fs[conf.kind];
bench.start();
for (i = 0; i < n; i++) {
fn(__filename);
}
bench.end(n);
break;
case 'fstatSync':
fn = fs.fstatSync;
const fd = fs.openSync(__filename, 'r');
bench.start();
for (i = 0; i < n; i++) {
fn(fd);
}
bench.end(n);
fs.closeSync(fd);
break;
default:
throw new Error('Invalid kind argument');
const kind = conf.kind;
const arg = (kind === 'fstatSync' ? fs.openSync(__filename, 'r') : __dirname);
const fn = fs[conf.kind];
bench.start();
for (var i = 0; i < n; i++) {
fn(arg);
}
bench.end(n);
if (kind === 'fstat')
fs.closeSync(arg);
}
+17
View File
@@ -0,0 +1,17 @@
'use strict';
var common = require('../common.js');
var fs = require('fs');
var bench = common.createBenchmark(main, {
n: [60e4]
});
function main(conf) {
var n = +conf.n;
bench.start();
for (var i = 0; i < n; ++i)
fs.readFileSync(__filename);
bench.end(n);
}
+100 -53
View File
@@ -25,6 +25,7 @@
'use strict';
const constants = process.binding('constants').fs;
const { S_IFMT, S_IFREG, S_IFLNK } = constants;
const util = require('util');
const pathModule = require('path');
const { isUint8Array } = process.binding('util');
@@ -135,6 +136,24 @@ function makeCallback(cb) {
};
}
// Special case of `makeCallback()` that is specific to async `*stat()` calls as
// an optimization, since the data passed back to the callback needs to be
// transformed anyway.
function makeStatsCallback(cb) {
if (cb === undefined) {
return rethrow();
}
if (typeof cb !== 'function') {
throw new TypeError('"callback" argument must be a function');
}
return function(err) {
if (err) return cb(err);
cb(err, statsFromValues());
};
}
function nullCheck(path, callback) {
if (('' + path).indexOf('\u0000') !== -1) {
var er = new Error('Path must be a string without null bytes');
@@ -151,7 +170,7 @@ function isFd(path) {
return (path >>> 0) === path;
}
// Static method to set the stats properties on a Stats object.
// Constructor for file stats.
function Stats(
dev,
mode,
@@ -184,41 +203,49 @@ function Stats(
}
fs.Stats = Stats;
// Create a C++ binding to the function which creates a Stats object.
binding.FSInitialize(fs.Stats);
fs.Stats.prototype._checkModeProperty = function(property) {
return ((this.mode & constants.S_IFMT) === property);
Stats.prototype._checkModeProperty = function(property) {
return ((this.mode & S_IFMT) === property);
};
fs.Stats.prototype.isDirectory = function() {
Stats.prototype.isDirectory = function() {
return this._checkModeProperty(constants.S_IFDIR);
};
fs.Stats.prototype.isFile = function() {
return this._checkModeProperty(constants.S_IFREG);
Stats.prototype.isFile = function() {
return this._checkModeProperty(S_IFREG);
};
fs.Stats.prototype.isBlockDevice = function() {
Stats.prototype.isBlockDevice = function() {
return this._checkModeProperty(constants.S_IFBLK);
};
fs.Stats.prototype.isCharacterDevice = function() {
Stats.prototype.isCharacterDevice = function() {
return this._checkModeProperty(constants.S_IFCHR);
};
fs.Stats.prototype.isSymbolicLink = function() {
return this._checkModeProperty(constants.S_IFLNK);
Stats.prototype.isSymbolicLink = function() {
return this._checkModeProperty(S_IFLNK);
};
fs.Stats.prototype.isFIFO = function() {
Stats.prototype.isFIFO = function() {
return this._checkModeProperty(constants.S_IFIFO);
};
fs.Stats.prototype.isSocket = function() {
Stats.prototype.isSocket = function() {
return this._checkModeProperty(constants.S_IFSOCK);
};
const statValues = binding.getStatValues();
function statsFromValues() {
return new Stats(statValues[0], statValues[1], statValues[2], statValues[3],
statValues[4], statValues[5],
statValues[6] < 0 ? undefined : statValues[6], statValues[7],
statValues[8], statValues[9] < 0 ? undefined : statValues[9],
statValues[10], statValues[11], statValues[12],
statValues[13]);
}
// Don't allow mode to accidentally be overwritten.
['F_OK', 'R_OK', 'W_OK', 'X_OK'].forEach(function(key) {
Object.defineProperty(fs, key, {
@@ -275,7 +302,7 @@ fs.exists = function(path, callback) {
var req = new FSReqWrap();
req.oncomplete = cb;
binding.stat(pathModule._makeLong(path), req);
function cb(err, stats) {
function cb(err) {
if (callback) callback(err ? false : true);
}
};
@@ -284,7 +311,7 @@ fs.existsSync = function(path) {
try {
handleError((path = getPathFromURL(path)));
nullCheck(path);
binding.stat(pathModule._makeLong(path), statValues);
binding.stat(pathModule._makeLong(path));
return true;
} catch (e) {
return false;
@@ -387,13 +414,19 @@ function readFileAfterOpen(err, fd) {
binding.fstat(fd, req);
}
function readFileAfterStat(err, st) {
function readFileAfterStat(err) {
var context = this.context;
if (err)
return context.close(err);
var size = context.size = st.isFile() ? st.size : 0;
// Use stats array directly to avoid creating an fs.Stats instance just for
// our internal use.
var size;
if ((statValues[1/*mode*/] & S_IFMT) === S_IFREG)
size = context.size = statValues[8/*size*/];
else
size = context.size = 0;
if (size === 0) {
context.buffers = [];
@@ -467,14 +500,13 @@ function tryToString(buf, encoding, callback) {
function tryStatSync(fd, isUserFd) {
var threw = true;
var st;
try {
st = fs.fstatSync(fd);
binding.fstat(fd);
threw = false;
} finally {
if (threw && !isUserFd) fs.closeSync(fd);
}
return st;
return !threw;
}
function tryCreateBuffer(size, fd, isUserFd) {
@@ -506,8 +538,13 @@ fs.readFileSync = function(path, options) {
var isUserFd = isFd(path); // file descriptor ownership
var fd = isUserFd ? path : fs.openSync(path, options.flag || 'r', 0o666);
var st = tryStatSync(fd, isUserFd);
var size = st.isFile() ? st.size : 0;
// Use stats array directly to avoid creating an fs.Stats instance just for
// our internal use.
var size;
if (tryStatSync(fd, isUserFd) && (statValues[1/*mode*/] & S_IFMT) === S_IFREG)
size = statValues[8/*size*/];
else
size = 0;
var pos = 0;
var buffer; // single buffer with file data
var buffers; // list for when size is unknown
@@ -854,12 +891,12 @@ fs.readdirSync = function(path, options) {
fs.fstat = function(fd, callback) {
var req = new FSReqWrap();
req.oncomplete = makeCallback(callback);
req.oncomplete = makeStatsCallback(callback);
binding.fstat(fd, req);
};
fs.lstat = function(path, callback) {
callback = makeCallback(callback);
callback = makeStatsCallback(callback);
if (handleError((path = getPathFromURL(path)), callback))
return;
if (!nullCheck(path, callback)) return;
@@ -869,7 +906,7 @@ fs.lstat = function(path, callback) {
};
fs.stat = function(path, callback) {
callback = makeCallback(callback);
callback = makeStatsCallback(callback);
if (handleError((path = getPathFromURL(path)), callback))
return;
if (!nullCheck(path, callback)) return;
@@ -878,32 +915,22 @@ fs.stat = function(path, callback) {
binding.stat(pathModule._makeLong(path), req);
};
const statValues = new Float64Array(14);
function statsFromValues() {
return new Stats(statValues[0], statValues[1], statValues[2], statValues[3],
statValues[4], statValues[5],
statValues[6] < 0 ? undefined : statValues[6], statValues[7],
statValues[8], statValues[9] < 0 ? undefined : statValues[9],
statValues[10], statValues[11], statValues[12],
statValues[13]);
}
fs.fstatSync = function(fd) {
binding.fstat(fd, statValues);
binding.fstat(fd);
return statsFromValues();
};
fs.lstatSync = function(path) {
handleError((path = getPathFromURL(path)));
nullCheck(path);
binding.lstat(pathModule._makeLong(path), statValues);
binding.lstat(pathModule._makeLong(path));
return statsFromValues();
};
fs.statSync = function(path) {
handleError((path = getPathFromURL(path)));
nullCheck(path);
binding.stat(pathModule._makeLong(path), statValues);
binding.stat(pathModule._makeLong(path));
return statsFromValues();
};
@@ -1380,6 +1407,15 @@ function emitStop(self) {
self.emit('stop');
}
function statsFromPrevValues() {
return new Stats(statValues[14], statValues[15], statValues[16],
statValues[17], statValues[18], statValues[19],
statValues[20] < 0 ? undefined : statValues[20],
statValues[21], statValues[22],
statValues[23] < 0 ? undefined : statValues[23],
statValues[24], statValues[25], statValues[26],
statValues[27]);
}
function StatWatcher() {
EventEmitter.call(this);
@@ -1390,13 +1426,13 @@ function StatWatcher() {
// the sake of backwards compatibility
var oldStatus = -1;
this._handle.onchange = function(current, previous, newStatus) {
this._handle.onchange = function(newStatus) {
if (oldStatus === -1 &&
newStatus === -1 &&
current.nlink === previous.nlink) return;
statValues[2/*new nlink*/] === statValues[16/*old nlink*/]) return;
oldStatus = newStatus;
self.emit('change', current, previous);
self.emit('change', statsFromValues(), statsFromPrevValues());
};
this._handle.onstop = function() {
@@ -1544,7 +1580,7 @@ fs.realpathSync = function realpathSync(p, options) {
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
fs.lstatSync(base);
binding.lstat(pathModule._makeLong(base));
knownHard[base] = true;
}
@@ -1576,8 +1612,12 @@ fs.realpathSync = function realpathSync(p, options) {
if (maybeCachedResolved) {
resolvedLink = maybeCachedResolved;
} else {
var stat = fs.lstatSync(base);
if (!stat.isSymbolicLink()) {
// Use stats array directly to avoid creating an fs.Stats instance just
// for our internal use.
binding.lstat(pathModule._makeLong(base));
if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) {
knownHard[base] = true;
if (cache) cache.set(base, base);
continue;
@@ -1588,14 +1628,16 @@ fs.realpathSync = function realpathSync(p, options) {
var linkTarget = null;
var id;
if (!isWindows) {
id = `${stat.dev.toString(32)}:${stat.ino.toString(32)}`;
var dev = statValues[0/*dev*/].toString(32);
var ino = statValues[7/*ino*/].toString(32);
id = `${dev}:${ino}`;
if (seenLinks.hasOwnProperty(id)) {
linkTarget = seenLinks[id];
}
}
if (linkTarget === null) {
fs.statSync(base);
linkTarget = fs.readlinkSync(base);
binding.stat(pathModule._makeLong(base));
linkTarget = binding.readlink(pathModule._makeLong(base));
}
resolvedLink = pathModule.resolve(previous, linkTarget);
@@ -1614,7 +1656,7 @@ fs.realpathSync = function realpathSync(p, options) {
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
fs.lstatSync(base);
binding.lstat(pathModule._makeLong(base));
knownHard[base] = true;
}
}
@@ -1694,11 +1736,14 @@ fs.realpath = function realpath(p, options, callback) {
return fs.lstat(base, gotStat);
}
function gotStat(err, stat) {
function gotStat(err) {
if (err) return callback(err);
// Use stats array directly to avoid creating an fs.Stats instance just for
// our internal use.
// if not a symlink, skip to the next path part
if (!stat.isSymbolicLink()) {
if ((statValues[1/*mode*/] & S_IFMT) !== S_IFLNK) {
knownHard[base] = true;
return process.nextTick(LOOP);
}
@@ -1708,7 +1753,9 @@ fs.realpath = function realpath(p, options, callback) {
// dev/ino always return 0 on windows, so skip the check.
let id;
if (!isWindows) {
id = `${stat.dev.toString(32)}:${stat.ino.toString(32)}`;
var dev = statValues[0/*ino*/].toString(32);
var ino = statValues[7/*ino*/].toString(32);
id = `${dev}:${ino}`;
if (seenLinks.hasOwnProperty(id)) {
return gotTarget(null, seenLinks[id], base);
}
+10
View File
@@ -199,6 +199,7 @@ inline Environment::Environment(IsolateData* isolate_data,
#endif
handle_cleanup_waiting_(0),
http_parser_buffer_(nullptr),
fs_stats_field_array_(nullptr),
context_(context->GetIsolate(), context) {
// We'll be creating new objects so make sure we've entered the context.
v8::HandleScope handle_scope(isolate());
@@ -380,6 +381,15 @@ inline void Environment::set_http_parser_buffer(char* buffer) {
http_parser_buffer_ = buffer;
}
inline double* Environment::fs_stats_field_array() const {
return fs_stats_field_array_;
}
inline void Environment::set_fs_stats_field_array(double* fields) {
CHECK_EQ(fs_stats_field_array_, nullptr); // Should be set only once.
fs_stats_field_array_ = fields;
}
inline Environment* Environment::from_cares_timer_handle(uv_timer_t* handle) {
return ContainerOf(&Environment::cares_timer_handle_, handle);
}
+5 -1
View File
@@ -253,7 +253,6 @@ namespace node {
V(context, v8::Context) \
V(domain_array, v8::Array) \
V(domains_stack_array, v8::Array) \
V(fs_stats_constructor_function, v8::Function) \
V(generic_internal_field_template, v8::ObjectTemplate) \
V(jsstream_constructor_template, v8::FunctionTemplate) \
V(module_load_list_array, v8::Array) \
@@ -492,6 +491,9 @@ class Environment {
inline char* http_parser_buffer() const;
inline void set_http_parser_buffer(char* buffer);
inline double* fs_stats_field_array() const;
inline void set_fs_stats_field_array(double* fields);
inline void ThrowError(const char* errmsg);
inline void ThrowTypeError(const char* errmsg);
inline void ThrowRangeError(const char* errmsg);
@@ -600,6 +602,8 @@ class Environment {
char* http_parser_buffer_;
double* fs_stats_field_array_;
#define V(PropertyName, TypeName) \
v8::Persistent<TypeName> PropertyName ## _;
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
+38 -151
View File
@@ -50,7 +50,6 @@ namespace node {
using v8::Array;
using v8::ArrayBuffer;
using v8::Context;
using v8::EscapableHandleScope;
using v8::Float64Array;
using v8::Function;
using v8::FunctionCallbackInfo;
@@ -211,6 +210,14 @@ static void After(uv_fs_t *req) {
argc = 1;
break;
case UV_FS_STAT:
case UV_FS_LSTAT:
case UV_FS_FSTAT:
argc = 1;
FillStatsArray(env->fs_stats_field_array(),
static_cast<const uv_stat_t*>(req->ptr));
break;
case UV_FS_UTIME:
case UV_FS_FUTIME:
argc = 0;
@@ -224,13 +231,6 @@ static void After(uv_fs_t *req) {
argv[1] = Integer::New(env->isolate(), req->result);
break;
case UV_FS_STAT:
case UV_FS_LSTAT:
case UV_FS_FSTAT:
argv[1] = BuildStatsObject(env,
static_cast<const uv_stat_t*>(req->ptr));
break;
case UV_FS_MKDTEMP:
link = StringBytes::Encode(env->isolate(),
static_cast<const char*>(req->path),
@@ -441,116 +441,6 @@ static void Close(const FunctionCallbackInfo<Value>& args) {
}
Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
EscapableHandleScope handle_scope(env->isolate());
// If you hit this assertion, you forgot to enter the v8::Context first.
CHECK_EQ(env->context(), env->isolate()->GetCurrentContext());
// The code below is very nasty-looking but it prevents a segmentation fault
// when people run JS code like the snippet below. It's apparently more
// common than you would expect, several people have reported this crash...
//
// function crash() {
// fs.statSync('.');
// crash();
// }
//
// We need to check the return value of Number::New() and Date::New()
// and make sure that we bail out when V8 returns an empty handle.
// Unsigned integers. It does not actually seem to be specified whether
// uid and gid are unsigned or not, but in practice they are unsigned,
// and Node’s (F)Chown functions do check their arguments for unsignedness.
#define X(name) \
Local<Value> name = Integer::NewFromUnsigned(env->isolate(), s->st_##name); \
if (name.IsEmpty()) \
return Local<Object>(); \
X(uid)
X(gid)
# if defined(__POSIX__)
X(blksize)
# else
Local<Value> blksize = Undefined(env->isolate());
# endif
#undef X
// Integers.
#define X(name) \
Local<Value> name = Integer::New(env->isolate(), s->st_##name); \
if (name.IsEmpty()) \
return Local<Object>(); \
X(dev)
X(mode)
X(nlink)
X(rdev)
#undef X
// Numbers.
#define X(name) \
Local<Value> name = Number::New(env->isolate(), \
static_cast<double>(s->st_##name)); \
if (name.IsEmpty()) \
return Local<Object>(); \
X(ino)
X(size)
# if defined(__POSIX__)
X(blocks)
# else
Local<Value> blocks = Undefined(env->isolate());
# endif
#undef X
// Dates.
#define X(name) \
Local<Value> name##_msec = \
Number::New(env->isolate(), \
(static_cast<double>(s->st_##name.tv_sec) * 1000) + \
(static_cast<double>(s->st_##name.tv_nsec / 1000000))); \
\
if (name##_msec.IsEmpty()) \
return Local<Object>(); \
X(atim)
X(mtim)
X(ctim)
X(birthtim)
#undef X
// Pass stats as the first argument, this is the object we are modifying.
Local<Value> argv[] = {
dev,
mode,
nlink,
uid,
gid,
rdev,
blksize,
ino,
size,
blocks,
atim_msec,
mtim_msec,
ctim_msec,
birthtim_msec
};
// Call out to JavaScript to create the stats object.
Local<Value> stats =
env->fs_stats_constructor_function()->NewInstance(
env->context(),
arraysize(argv),
argv).FromMaybe(Local<Value>());
if (stats.IsEmpty())
return handle_scope.Escape(Local<Object>());
return handle_scope.Escape(stats);
}
void FillStatsArray(double* fields, const uv_stat_t* s) {
fields[0] = s->st_dev;
fields[1] = s->st_mode;
@@ -666,15 +556,12 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
BufferValue path(env->isolate(), args[0]);
ASSERT_PATH(path)
if (args[1]->IsFloat64Array()) {
Local<Float64Array> array = args[1].As<Float64Array>();
CHECK_EQ(array->Length(), 14);
Local<ArrayBuffer> ab = array->Buffer();
double* fields = static_cast<double*>(ab->GetContents().Data());
SYNC_CALL(stat, *path, *path)
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
} else if (args[1]->IsObject()) {
if (args[1]->IsObject()) {
ASYNC_CALL(stat, args[1], UTF8, *path)
} else {
SYNC_CALL(stat, *path, *path)
FillStatsArray(env->fs_stats_field_array(),
static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
}
}
@@ -687,15 +574,12 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
BufferValue path(env->isolate(), args[0]);
ASSERT_PATH(path)
if (args[1]->IsFloat64Array()) {
Local<Float64Array> array = args[1].As<Float64Array>();
CHECK_EQ(array->Length(), 14);
Local<ArrayBuffer> ab = array->Buffer();
double* fields = static_cast<double*>(ab->GetContents().Data());
SYNC_CALL(lstat, *path, *path)
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
} else if (args[1]->IsObject()) {
if (args[1]->IsObject()) {
ASYNC_CALL(lstat, args[1], UTF8, *path)
} else {
SYNC_CALL(lstat, *path, *path)
FillStatsArray(env->fs_stats_field_array(),
static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
}
}
@@ -709,15 +593,12 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
int fd = args[0]->Int32Value();
if (args[1]->IsFloat64Array()) {
Local<Float64Array> array = args[1].As<Float64Array>();
CHECK_EQ(array->Length(), 14);
Local<ArrayBuffer> ab = array->Buffer();
double* fields = static_cast<double*>(ab->GetContents().Data());
SYNC_CALL(fstat, 0, fd)
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
} else if (args[1]->IsObject()) {
if (args[1]->IsObject()) {
ASYNC_CALL(fstat, args[1], UTF8, fd)
} else {
SYNC_CALL(fstat, nullptr, fd)
FillStatsArray(env->fs_stats_field_array(),
static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
}
}
@@ -1496,12 +1377,20 @@ static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
}
}
void FSInitialize(const FunctionCallbackInfo<Value>& args) {
Local<Function> stats_constructor = args[0].As<Function>();
CHECK(stats_constructor->IsFunction());
void GetStatValues(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
env->set_fs_stats_constructor_function(stats_constructor);
double* fields = env->fs_stats_field_array();
if (fields == nullptr) {
// stat fields contains twice the number of entries because `fs.StatWatcher`
// needs room to store data for *two* `fs.Stats` instances.
fields = new double[2 * 14];
env->set_fs_stats_field_array(fields);
}
Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(),
fields,
sizeof(double) * 2 * 14);
Local<Float64Array> fields_array = Float64Array::New(ab, 0, 2 * 14);
args.GetReturnValue().Set(fields_array);
}
void InitFs(Local<Object> target,
@@ -1510,10 +1399,6 @@ void InitFs(Local<Object> target,
void* priv) {
Environment* env = Environment::GetCurrent(context);
// Function which creates a new Stats object.
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "FSInitialize"),
env->NewFunctionTemplate(FSInitialize)->GetFunction());
env->SetMethod(target, "access", Access);
env->SetMethod(target, "close", Close);
env->SetMethod(target, "open", Open);
@@ -1552,6 +1437,8 @@ void InitFs(Local<Object> target,
env->SetMethod(target, "mkdtemp", Mkdtemp);
env->SetMethod(target, "getStatValues", GetStatValues);
StatWatcher::Initialize(env, target);
// Create FunctionTemplate for FSReqWrap
+1 -1
View File
@@ -162,7 +162,7 @@ NO_RETURN void FatalError(const char* location, const char* message);
void ProcessEmitWarning(Environment* env, const char* fmt, ...);
v8::Local<v8::Value> BuildStatsObject(Environment* env, const uv_stat_t* s);
void FillStatsArray(double* fields, const uv_stat_t* s);
void SetupProcessObject(Environment* env,
int argc,
+5 -6
View File
@@ -86,12 +86,11 @@ void StatWatcher::Callback(uv_fs_poll_t* handle,
Environment* env = wrap->env();
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
Local<Value> argv[] = {
BuildStatsObject(env, curr),
BuildStatsObject(env, prev),
Integer::New(env->isolate(), status)
};
wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv);
FillStatsArray(env->fs_stats_field_array(), curr);
FillStatsArray(env->fs_stats_field_array() + 14, prev);
Local<Value> arg = Integer::New(env->isolate(), status);
wrap->MakeCallback(env->onchange_string(), 1, &arg);
}
+1 -1
View File
@@ -39,7 +39,7 @@ fs.writeSync = function() {
throw new Error('BAM');
};
fs.fstatSync = function() {
process.binding('fs').fstat = function() {
throw new Error('BAM');
};