Browse Source

fs: fix 'object is not a function' callback errors

Use a default callback if the user omitted one. Avoids errors like the one
below:

  fs.js:777
      if (err) return callback(err);
                      ^
  TypeError: object is not a function
          at fs.appendFile (fs.js:777:21)
          at Object.oncomplete (fs.js:297:15)

This commit fixes the behavior of fs.lchmod(), fs.lchown() and fs.readFile()
when the callback is omitted. Before, they silently swallowed errors.

Fixes #4352.
v0.9.4-release
Ben Noordhuis 12 years ago
parent
commit
a80434736b
  1. 27
      doc/api/fs.markdown
  2. 59
      lib/fs.js

27
doc/api/fs.markdown

@ -59,8 +59,31 @@ In busy processes, the programmer is _strongly encouraged_ to use the
asynchronous versions of these calls. The synchronous versions will block
the entire process until they complete--halting all connections.
Relative path to filename can be used, remember however that this path will be relative
to `process.cwd()`.
Relative path to filename can be used, remember however that this path will be
relative to `process.cwd()`.
Most fs functions let you omit the callback argument. If you do, a default
callback is used that rethrows errors. To get a trace to the original call
site, set the NODE_DEBUG environment variable:
$ cat script.js
function bad() {
require('fs').readFile('/');
}
bad();
$ env NODE_DEBUG=fs node script.js
fs.js:66
throw err;
^
Error: EISDIR, read
at rethrow (fs.js:61:21)
at maybeCallback (fs.js:79:42)
at Object.fs.readFile (fs.js:153:18)
at bad (/path/to/script.js:2:17)
at Object.<anonymous> (/path/to/script.js:5:1)
<etc.>
## fs.rename(oldPath, newPath, [callback])

59
lib/fs.js

@ -52,6 +52,27 @@ var O_WRONLY = constants.O_WRONLY || 0;
var isWindows = process.platform === 'win32';
function rethrow(err) {
if (err) throw err;
}
function maybeCallback(cb) {
return typeof cb === 'function' ? cb : rethrow;
}
// Ensure that callbacks run in the global context. Only use this function
// for callbacks that are passed to the binding layer, callbacks that are
// invoked from JS already run in the proper scope.
function makeCallback(cb) {
if (typeof cb !== 'function') {
return rethrow;
}
return function() {
return cb.apply(null, arguments);
};
}
function assertEncoding(encoding) {
if (encoding && !Buffer.isEncoding(encoding)) {
throw new Error('Unknown encoding: ' + encoding);
@ -110,8 +131,7 @@ fs.existsSync = function(path) {
fs.readFile = function(path, encoding_) {
var encoding = typeof(encoding_) === 'string' ? encoding_ : null;
var callback = arguments[arguments.length - 1];
if (typeof(callback) !== 'function') callback = function() {};
var callback = maybeCallback(arguments[arguments.length - 1]);
assertEncoding(encoding);
@ -295,21 +315,6 @@ Object.defineProperty(exports, '_stringToFlags', {
});
// Ensure that callbacks run in the global context. Only use this function
// for callbacks that are passed to the binding layer, callbacks that are
// invoked from JS already run in the proper scope.
function makeCallback(cb) {
if (typeof cb !== 'function') {
// faster than returning a ref to a global no-op function
return function() {};
}
return function() {
return cb.apply(null, arguments);
};
}
// Yes, the follow could be easily DRYed up but I provide the explicit
// list to make the arguments clear.
@ -425,9 +430,11 @@ fs.write = function(fd, buffer, offset, length, position, callback) {
return;
}
callback = maybeCallback(callback);
function wrapper(err, written) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback && callback(err, written || 0, buffer);
callback(err, written || 0, buffer);
}
binding.write(fd, buffer, offset, length, position, wrapper);
@ -470,6 +477,7 @@ fs.truncate = function(path, len, callback) {
} else if (typeof len === 'undefined') {
len = 0;
}
callback = maybeCallback(callback);
fs.open(path, 'w', function(er, fd) {
if (er) return callback(er);
binding.ftruncate(fd, len, function(er) {
@ -659,7 +667,7 @@ fs.fchmodSync = function(fd, mode) {
if (constants.hasOwnProperty('O_SYMLINK')) {
fs.lchmod = function(path, mode, callback) {
callback = callback || (function() {});
callback = maybeCallback(callback);
fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
if (err) {
callback(err);
@ -709,7 +717,7 @@ fs.chmodSync = function(path, mode) {
if (constants.hasOwnProperty('O_SYMLINK')) {
fs.lchown = function(path, uid, gid, callback) {
callback = callback || (function() {});
callback = maybeCallback(callback);
fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
if (err) {
callback(err);
@ -782,8 +790,7 @@ fs.futimesSync = function(fd, atime, mtime) {
};
function writeAll(fd, buffer, offset, length, position, callback) {
var callback_ = arguments[arguments.length - 1];
callback = (typeof(callback_) == 'function' ? callback_ : null);
callback = maybeCallback(arguments[arguments.length - 1]);
// write(fd, buffer, offset, length, position, callback)
fs.write(fd, buffer, offset, length, position, function(writeErr, written) {
@ -808,8 +815,7 @@ fs.writeFile = function(path, data, encoding_, callback) {
var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
assertEncoding(encoding);
var callback_ = arguments[arguments.length - 1];
callback = (typeof(callback_) == 'function' ? callback_ : null);
callback = maybeCallback(arguments[arguments.length - 1]);
fs.open(path, 'w', 438 /*=0666*/, function(openErr, fd) {
if (openErr) {
if (callback) callback(openErr);
@ -843,8 +849,7 @@ fs.appendFile = function(path, data, encoding_, callback) {
var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
assertEncoding(encoding);
var callback_ = arguments[arguments.length - 1];
callback = (typeof(callback_) == 'function' ? callback_ : null);
callback = maybeCallback(arguments[arguments.length - 1]);
fs.open(path, 'a', 438 /*=0666*/, function(err, fd) {
if (err) return callback(err);
@ -1158,7 +1163,7 @@ fs.realpathSync = function realpathSync(p, cache) {
fs.realpath = function realpath(p, cache, cb) {
if (typeof cb !== 'function') {
cb = cache;
cb = maybeCallback(cache);
cache = null;
}

Loading…
Cancel
Save