Skip to content

fix: prepatch fs-binding to improve support nodejs v10+ readFile #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions lib/binding.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,34 +277,34 @@ Binding.prototype.setSystem = function(system) {
* @param {number} fd File descriptor identifier.
* @return {FileDescriptor} File descriptor.
*/
Binding.prototype._getDescriptorById = function(fd) {
function _getDescriptorById(fd) {
if (!_openFiles.hasOwnProperty(fd)) {
throw new FSError('EBADF');
}
return _openFiles[fd];
};
}

/**
* Keep track of a file descriptor as open.
* @param {FileDescriptor} descriptor The file descriptor.
* @return {number} Identifier for file descriptor.
*/
Binding.prototype._trackDescriptor = function(descriptor) {
function _trackDescriptor(descriptor) {
var fd = ++_counter;
_openFiles[fd] = descriptor;
return fd;
};
}

/**
* Stop tracking a file descriptor as open.
* @param {number} fd Identifier for file descriptor.
*/
Binding.prototype._untrackDescriptorById = function(fd) {
function _untrackDescriptorById(fd) {
if (!_openFiles.hasOwnProperty(fd)) {
throw new FSError('EBADF');
}
delete _openFiles[fd];
};
}

/**
* Resolve the canonicalized absolute pathname.
Expand Down Expand Up @@ -445,7 +445,7 @@ Binding.prototype.fstat = function(fd, options, callback, ctx) {
markSyscall(ctx, 'fstat');

return maybeCallback(wrapStatsCallback(callback), ctx, this, function() {
var descriptor = this._getDescriptorById(fd);
var descriptor = _getDescriptorById(fd);
var item = descriptor.getItem();
var stats = item.getStats();

Expand Down Expand Up @@ -474,7 +474,7 @@ Binding.prototype.close = function(fd, callback, ctx) {
markSyscall(ctx, 'close');

maybeCallback(normalizeCallback(callback), ctx, this, function() {
this._untrackDescriptorById(fd);
_untrackDescriptorById(fd);
});
};

Expand Down Expand Up @@ -542,7 +542,7 @@ Binding.prototype.open = function(pathname, flags, mode, callback, ctx) {
descriptor.setPosition(item.getContent().length);
}
descriptor.setItem(item);
return this._trackDescriptor(descriptor);
return _trackDescriptor(descriptor);
});
};

Expand Down Expand Up @@ -592,7 +592,7 @@ Binding.prototype.read = function(
markSyscall(ctx, 'read');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
var descriptor = this._getDescriptorById(fd);
var descriptor = _getDescriptorById(fd);
if (!descriptor.isRead()) {
throw new FSError('EBADF');
}
Expand Down Expand Up @@ -629,7 +629,7 @@ Binding.prototype.copyFile = function(src, dest, flags, callback, ctx) {
var srcFd = this.open(src, constants.O_RDONLY);

try {
var srcDescriptor = this._getDescriptorById(srcFd);
var srcDescriptor = _getDescriptorById(srcFd);
if (!srcDescriptor.isRead()) {
throw new FSError('EBADF');
}
Expand Down Expand Up @@ -680,7 +680,7 @@ Binding.prototype.writeBuffers = function(
markSyscall(ctx, 'write');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
var descriptor = this._getDescriptorById(fd);
var descriptor = _getDescriptorById(fd);
if (!descriptor.isWrite()) {
throw new FSError('EBADF');
}
Expand Down Expand Up @@ -732,7 +732,7 @@ Binding.prototype.writeBuffer = function(
markSyscall(ctx, 'write');

return maybeCallback(normalizeCallback(callback), ctx, this, function() {
var descriptor = this._getDescriptorById(fd);
var descriptor = _getDescriptorById(fd);
if (!descriptor.isWrite()) {
throw new FSError('EBADF');
}
Expand Down Expand Up @@ -1052,7 +1052,7 @@ Binding.prototype.ftruncate = function(fd, len, callback, ctx) {
markSyscall(ctx, 'ftruncate');

maybeCallback(normalizeCallback(callback), ctx, this, function() {
var descriptor = this._getDescriptorById(fd);
var descriptor = _getDescriptorById(fd);
if (!descriptor.isWrite()) {
throw new FSError('EINVAL');
}
Expand Down Expand Up @@ -1109,7 +1109,7 @@ Binding.prototype.fchown = function(fd, uid, gid, callback, ctx) {
markSyscall(ctx, 'fchown');

maybeCallback(normalizeCallback(callback), ctx, this, function() {
var descriptor = this._getDescriptorById(fd);
var descriptor = _getDescriptorById(fd);
var item = descriptor.getItem();
item.setUid(uid);
item.setGid(gid);
Expand Down Expand Up @@ -1146,7 +1146,7 @@ Binding.prototype.fchmod = function(fd, mode, callback, ctx) {
markSyscall(ctx, 'fchmod');

maybeCallback(normalizeCallback(callback), ctx, this, function() {
var descriptor = this._getDescriptorById(fd);
var descriptor = _getDescriptorById(fd);
var item = descriptor.getItem();
item.setMode(mode);
});
Expand Down Expand Up @@ -1207,7 +1207,7 @@ Binding.prototype.futimes = function(fd, atime, mtime, callback, ctx) {
markSyscall(ctx, 'futimes');

maybeCallback(normalizeCallback(callback), ctx, this, function() {
var descriptor = this._getDescriptorById(fd);
var descriptor = _getDescriptorById(fd);
var item = descriptor.getItem();
item.setATime(new Date(atime * 1000));
item.setMTime(new Date(mtime * 1000));
Expand All @@ -1224,7 +1224,7 @@ Binding.prototype.fsync = function(fd, callback, ctx) {
markSyscall(ctx, 'fsync');

maybeCallback(normalizeCallback(callback), ctx, this, function() {
this._getDescriptorById(fd);
_getDescriptorById(fd);
});
};

Expand All @@ -1238,7 +1238,7 @@ Binding.prototype.fdatasync = function(fd, callback, ctx) {
markSyscall(ctx, 'fdatasync');

maybeCallback(normalizeCallback(callback), ctx, this, function() {
this._getDescriptorById(fd);
_getDescriptorById(fd);
});
};

Expand Down
61 changes: 44 additions & 17 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,52 @@ var realBinding = process.binding('fs');
var path = require('path');
var fs = require('fs');

var realBindingProps = Object.assign({}, realBinding);
var realProcessProps = {
cwd: process.cwd,
chdir: process.chdir
};
var realCreateWriteStream = fs.createWriteStream;
var realStats = realBinding.Stats;
var realStatWatcher = realBinding.StatWatcher;

// Pre-patch fs binding
//
// This allows mock-fs to work properly under nodejs v10+ readFile
// As ReadFileContext nodejs v10+ implementation traps original binding methods:
// const { FSReqWrap, close, read } = process.binding('fs');
// Note this patch only solves issue for readFile, as the require of
// ReadFileContext is delayed by readFile implementation.
// if (!ReadFileContext) ReadFileContext = require('internal/fs/read_file_context')
function _patch(key) {
var existingMethod = realBinding[key];
realBinding[key] = function() {
if (this._mockedBinding) {
return this._mockedBinding[key].apply(this, arguments);
} else {
return existingMethod.apply(this, arguments);
}
}.bind(realBinding);
}

for (var key in Binding.prototype) {
if (typeof realBinding[key] === 'function') {
// Stats and StatWatcher are constructors
if (key !== 'Stats' && key !== 'StatWatcher') {
_patch(key);
}
}
}

function overrideBinding(binding) {
realBinding._mockedBinding = binding;

for (var key in binding) {
if (typeof binding[key] === 'function') {
realBinding[key] = binding[key].bind(binding);
} else {
if (typeof realBinding[key] === 'function') {
// Stats and StatWatcher are constructors
if (key === 'Stats' || key === 'StatWatcher') {
realBinding[key] = binding[key];
}
} else if (typeof realBinding[key] === 'undefined') {
realBinding[key] = binding[key];
}
}
Expand Down Expand Up @@ -62,16 +96,9 @@ function overrideCreateWriteStream() {
}

function restoreBinding() {
var key;
for (key in realBindingProps) {
realBinding[key] = realBindingProps[key];
}
// Delete excess keys that came in when the binding was originally applied.
for (key in realBinding) {
if (typeof realBindingProps[key] === 'undefined') {
delete realBinding[key];
}
}
delete realBinding._mockedBinding;
realBinding.Stats = realStats;
realBinding.StatWatcher = realStatWatcher;
}

function restoreProcess() {
Expand Down Expand Up @@ -120,10 +147,10 @@ var exports = (module.exports = function mock(config, options) {
* If fs hasn't currently been replaced, this will return an empty object
*/
exports.getMockRoot = function() {
if (typeof realBinding.getSystem === 'undefined') {
return {};
} else {
if (realBinding._mockedBinding) {
return realBinding.getSystem().getRoot();
} else {
return {};
}
};

Expand Down