Skip to content

Commit 10f7934

Browse files
Fix fs.stat(), fs.lstat(), and fs.fstat() for Node 7.7+
Node 7.7 changed the behavior of the `binding.{stat,lstat,fstat}` functions (see nodejs/node#11522). This updates the binding functions used in mock-fs to match the new Node binding behavior, while still maintaining compatibility with old Node versions. The new behavior is detected when the second argument to `binding.{stat,lstat,fstat}` is a `Float64Array`, which would be an invalid argument for previous versions of the binding.
1 parent 61926f8 commit 10f7934

File tree

1 file changed

+61
-7
lines changed

1 file changed

+61
-7
lines changed

lib/binding.js

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var getPathParts = require('./filesystem').getPathParts;
2020
* @return {*} Return (if callback is not provided).
2121
*/
2222
function maybeCallback(callback, thisArg, func) {
23-
if (callback) {
23+
if (callback && (typeof callback === 'function' || typeof callback.oncomplete === 'function')) {
2424
var err = null;
2525
var val;
2626
try {
@@ -266,11 +266,36 @@ Binding.prototype.realpath = function(filepath, encoding, callback) {
266266
});
267267
};
268268

269+
/**
270+
* Fill a Float64Array with stat information
271+
* This is based on the internal FillStatsArray function in Node.
272+
* https://github.com/nodejs/node/blob/4e05952a8a75af6df625415db612d3a9a1322682/src/node_file.cc#L533
273+
* @param {object} stats An object with file stats
274+
* @param {Float64Array} statValues A Float64Array where stat values should be inserted
275+
* @returns {void}
276+
*/
277+
function fillStatsArray(stats, statValues) {
278+
statValues[0] = stats.dev;
279+
statValues[1] = stats.mode;
280+
statValues[2] = stats.nlink;
281+
statValues[3] = stats.uid;
282+
statValues[4] = stats.gid;
283+
statValues[5] = stats.rdev;
284+
statValues[6] = stats.blksize;
285+
statValues[7] = stats.ino;
286+
statValues[8] = stats.size;
287+
statValues[9] = stats.blocks;
288+
statValues[10] = +stats.atime;
289+
statValues[11] = +stats.mtime;
290+
statValues[12] = +stats.ctime;
291+
statValues[13] = +stats.birthtime;
292+
}
269293

270294
/**
271295
* Stat an item.
272296
* @param {string} filepath Path.
273-
* @param {function(Error, Stats)} callback Callback (optional).
297+
* @param {function(Error, Stats)|Float64Array} callback Callback (optional). In Node 7.7.0+ this will be a Float64Array
298+
* that should be filled with stat values.
274299
* @return {Stats|undefined} Stats or undefined (if sync).
275300
*/
276301
Binding.prototype.stat = function(filepath, callback) {
@@ -283,22 +308,41 @@ Binding.prototype.stat = function(filepath, callback) {
283308
if (!item) {
284309
throw new FSError('ENOENT', filepath);
285310
}
286-
return new Stats(item.getStats());
311+
var stats = item.getStats();
312+
313+
// In Node 7.7.0+, binding.stat accepts a Float64Array as the second argument,
314+
// which should be filled with stat values.
315+
// In prior versions of Node, binding.stat simply returns a Stats instance.
316+
if (callback instanceof Float64Array) {
317+
fillStatsArray(stats, callback);
318+
} else {
319+
return new Stats(stats);
320+
}
287321
});
288322
};
289323

290324

291325
/**
292326
* Stat an item.
293327
* @param {number} fd File descriptor.
294-
* @param {function(Error, Stats)} callback Callback (optional).
328+
* @param {function(Error, Stats)|Float64Array} callback Callback (optional). In Node 7.7.0+ this will be a Float64Array
329+
* that should be filled with stat values.
295330
* @return {Stats|undefined} Stats or undefined (if sync).
296331
*/
297332
Binding.prototype.fstat = function(fd, callback) {
298333
return maybeCallback(callback, this, function() {
299334
var descriptor = this._getDescriptorById(fd);
300335
var item = descriptor.getItem();
301-
return new Stats(item.getStats());
336+
var stats = item.getStats();
337+
338+
// In Node 7.7.0+, binding.stat accepts a Float64Array as the second argument,
339+
// which should be filled with stat values.
340+
// In prior versions of Node, binding.stat simply returns a Stats instance.
341+
if (callback instanceof Float64Array) {
342+
fillStatsArray(stats, callback);
343+
} else {
344+
return new Stats(stats);
345+
}
302346
});
303347
};
304348

@@ -933,7 +977,8 @@ Binding.prototype.readlink = function(pathname, encoding, callback) {
933977
/**
934978
* Stat an item.
935979
* @param {string} filepath Path.
936-
* @param {function(Error, Stats)} callback Callback (optional).
980+
* @param {function(Error, Stats)|Float64Array} callback Callback (optional). In Node 7.7.0+ this will be a Float64Array
981+
* that should be filled with stat values.
937982
* @return {Stats|undefined} Stats or undefined (if sync).
938983
*/
939984
Binding.prototype.lstat = function(filepath, callback) {
@@ -942,7 +987,16 @@ Binding.prototype.lstat = function(filepath, callback) {
942987
if (!item) {
943988
throw new FSError('ENOENT', filepath);
944989
}
945-
return new Stats(item.getStats());
990+
var stats = item.getStats();
991+
992+
// In Node 7.7.0+, binding.stat accepts a Float64Array as the second argument,
993+
// which should be filled with stat values.
994+
// In prior versions of Node, binding.stat simply returns a Stats instance.
995+
if (callback instanceof Float64Array) {
996+
fillStatsArray(stats, callback);
997+
} else {
998+
return new Stats(item.getStats());
999+
}
9461000
});
9471001
};
9481002

0 commit comments

Comments
 (0)