fs: improve error performance of sync methods

PR-URL: https://github.com/nodejs/node/pull/49593
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
This commit is contained in:
Yagiz Nizipli 2023-09-17 16:42:46 -04:00 committed by GitHub
parent c177ac0a7d
commit 7e12d0e16d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 529 additions and 198 deletions

View File

@ -0,0 +1,42 @@
'use strict';
const common = require('../common');
const fs = require('fs');
const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();
const tmpfile = tmpdir.resolve(`.existing-file-${process.pid}`);
fs.writeFileSync(tmpfile, 'this-is-for-a-benchmark', 'utf8');
const bench = common.createBenchmark(main, {
type: ['existing', 'non-existing', 'non-flat-existing'],
n: [1e5],
});
function main({ n, type }) {
let path;
switch (type) {
case 'existing':
path = __filename;
break;
case 'non-flat-existing':
path = tmpfile;
break;
case 'non-existing':
path = tmpdir.resolve(`.non-existing-file-${process.pid}`);
break;
default:
new Error('Invalid type');
}
bench.start();
for (let i = 0; i < n; i++) {
try {
fs.accessSync(path);
} catch {
// do nothing
}
}
bench.end(n);
}

View File

@ -0,0 +1,37 @@
'use strict';
const common = require('../common');
const fs = require('fs');
const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();
const bench = common.createBenchmark(main, {
type: ['invalid', 'valid'],
n: [1e4],
});
function main({ n, type }) {
tmpdir.refresh();
const dest = tmpdir.resolve(`copy-file-bench-${process.pid}`);
let path;
switch (type) {
case 'invalid':
path = tmpdir.resolve(`.existing-file-${process.pid}`);
break;
case 'valid':
path = __filename;
break;
default:
throw new Error('Invalid type');
}
bench.start();
for (let i = 0; i < n; i++) {
try {
fs.copyFileSync(path, dest);
} catch {
// do nothing
}
}
bench.end(n);
}

View File

@ -0,0 +1,38 @@
'use strict';
const common = require('../common');
const fs = require('fs');
const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();
const tmpfile = tmpdir.resolve(`.existing-file-${process.pid}`);
fs.writeFileSync(tmpfile, 'this-is-for-a-benchmark', 'utf8');
const bench = common.createBenchmark(main, {
type: ['existing', 'non-existing', 'non-flat-existing'],
n: [1e6],
});
function main({ n, type }) {
let path;
switch (type) {
case 'existing':
path = __filename;
break;
case 'non-flat-existing':
path = tmpfile;
break;
case 'non-existing':
path = tmpdir.resolve(`.non-existing-file-${process.pid}`);
break;
default:
new Error('Invalid type');
}
bench.start();
for (let i = 0; i < n; i++) {
fs.existsSync(path);
}
bench.end(n);
}

View File

@ -0,0 +1,37 @@
'use strict';
const common = require('../common');
const fs = require('fs');
const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();
const bench = common.createBenchmark(main, {
type: ['existing', 'non-existing'],
n: [1e5],
});
function main({ n, type }) {
let path;
switch (type) {
case 'existing':
path = __filename;
break;
case 'non-existing':
path = tmpdir.resolve(`.non-existing-file-${process.pid}`);
break;
default:
new Error('Invalid type');
}
bench.start();
for (let i = 0; i < n; i++) {
try {
const fd = fs.openSync(path, 'r', 0o666);
fs.closeSync(fd);
} catch {
// do nothing
}
}
bench.end(n);
}

View File

@ -141,7 +141,7 @@ const {
validateObject,
validateString,
} = require('internal/validators');
const { readFileSyncUtf8 } = require('internal/fs/read/utf8');
const syncFs = require('internal/fs/sync');
let truncateWarn = true;
let fs;
@ -243,12 +243,7 @@ function access(path, mode, callback) {
* @returns {void}
*/
function accessSync(path, mode) {
path = getValidatedPath(path);
mode = getValidMode(mode, 'access');
const ctx = { path };
binding.access(pathModule.toNamespacedPath(path), mode, undefined, ctx);
handleErrorFromBinding(ctx);
syncFs.access(path, mode);
}
/**
@ -290,23 +285,7 @@ ObjectDefineProperty(exists, kCustomPromisifiedSymbol, {
* @returns {boolean}
*/
function existsSync(path) {
try {
path = getValidatedPath(path);
} catch {
return false;
}
const ctx = { path };
const nPath = pathModule.toNamespacedPath(path);
binding.access(nPath, F_OK, undefined, ctx);
// In case of an invalid symlink, `binding.access()` on win32
// will **not** return an error and is therefore not enough.
// Double check with `binding.stat()`.
if (isWindows && ctx.errno === undefined) {
binding.stat(nPath, false, undefined, ctx);
}
return ctx.errno === undefined;
return syncFs.exists(path);
}
function readFileAfterOpen(err, fd) {
@ -462,8 +441,7 @@ function readFileSync(path, options) {
// TODO(@anonrig): Do not handle file descriptor ownership for now.
if (!isUserFd && (options.encoding === 'utf8' || options.encoding === 'utf-8')) {
path = getValidatedPath(path);
return readFileSyncUtf8(pathModule.toNamespacedPath(path), stringToFlags(options.flag));
return syncFs.readFileUtf8(path, options.flag);
}
const fd = isUserFd ? path : fs.openSync(path, options.flag, 0o666);
@ -540,11 +518,7 @@ function close(fd, callback = defaultCloseCallback) {
* @returns {void}
*/
function closeSync(fd) {
fd = getValidatedFd(fd);
const ctx = {};
binding.close(fd, undefined, ctx);
handleErrorFromBinding(ctx);
return syncFs.close(fd);
}
/**
@ -590,16 +564,7 @@ function open(path, flags, mode, callback) {
* @returns {number}
*/
function openSync(path, flags, mode) {
path = getValidatedPath(path);
const flagsNumber = stringToFlags(flags);
mode = parseFileMode(mode, 'mode', 0o666);
const ctx = { path };
const result = binding.open(pathModule.toNamespacedPath(path),
flagsNumber, mode,
undefined, ctx);
handleErrorFromBinding(ctx);
return result;
return syncFs.open(path, flags, mode);
}
/**
@ -1702,25 +1667,12 @@ function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) {
* }} [options]
* @returns {Stats}
*/
function statSync(path, options = { bigint: false, throwIfNoEntry: true }) {
path = getValidatedPath(path);
const ctx = { path };
const stats = binding.stat(pathModule.toNamespacedPath(path),
options.bigint, undefined, ctx);
if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) {
return undefined;
}
handleErrorFromBinding(ctx);
return getStatsFromBinding(stats);
function statSync(path, options) {
return syncFs.stat(path, options);
}
function statfsSync(path, options = { bigint: false }) {
path = getValidatedPath(path);
const ctx = { path };
const stats = binding.statfs(pathModule.toNamespacedPath(path),
options.bigint, undefined, ctx);
handleErrorFromBinding(ctx);
return getStatFsFromBinding(stats);
function statfsSync(path, options) {
return syncFs.statfs(path, options);
}
/**
@ -2999,16 +2951,7 @@ function copyFile(src, dest, mode, callback) {
* @returns {void}
*/
function copyFileSync(src, dest, mode) {
src = getValidatedPath(src, 'src');
dest = getValidatedPath(dest, 'dest');
const ctx = { path: src, dest }; // non-prefixed
src = pathModule._makeLong(src);
dest = pathModule._makeLong(dest);
mode = getValidMode(mode, 'copyFile');
binding.copyFile(src, dest, mode, undefined, ctx);
handleErrorFromBinding(ctx);
syncFs.copyFile(src, dest, mode);
}
/**

View File

@ -1,25 +0,0 @@
'use strict';
const { handleErrorFromBinding } = require('internal/fs/utils');
const binding = internalBinding('fs');
/**
* @param {string} path
* @param {number} flag
* @return {string}
*/
function readFileSyncUtf8(path, flag) {
const response = binding.readFileSync(path, flag);
if (typeof response === 'string') {
return response;
}
const { 0: errno, 1: syscall } = response;
handleErrorFromBinding({ errno, syscall, path });
}
module.exports = {
readFileSyncUtf8,
};

98
lib/internal/fs/sync.js Normal file
View File

@ -0,0 +1,98 @@
'use strict';
const pathModule = require('path');
const {
getValidatedPath,
stringToFlags,
getValidMode,
getStatsFromBinding,
getStatFsFromBinding,
getValidatedFd,
} = require('internal/fs/utils');
const { parseFileMode } = require('internal/validators');
const binding = internalBinding('fs');
/**
* @param {string} path
* @param {number} flag
* @return {string}
*/
function readFileUtf8(path, flag) {
path = pathModule.toNamespacedPath(getValidatedPath(path));
return binding.readFileUtf8(path, stringToFlags(flag));
}
function exists(path) {
try {
path = getValidatedPath(path);
} catch {
return false;
}
return binding.existsSync(pathModule.toNamespacedPath(path));
}
function access(path, mode) {
path = getValidatedPath(path);
mode = getValidMode(mode, 'access');
binding.accessSync(pathModule.toNamespacedPath(path), mode);
}
function copyFile(src, dest, mode) {
src = getValidatedPath(src, 'src');
dest = getValidatedPath(dest, 'dest');
binding.copyFileSync(
pathModule.toNamespacedPath(src),
pathModule.toNamespacedPath(dest),
getValidMode(mode, 'copyFile'),
);
}
function stat(path, options = { bigint: false, throwIfNoEntry: true }) {
path = getValidatedPath(path);
const stats = binding.statSync(
pathModule.toNamespacedPath(path),
options.bigint,
options.throwIfNoEntry,
);
if (stats === undefined) {
return undefined;
}
return getStatsFromBinding(stats);
}
function statfs(path, options = { bigint: false }) {
path = getValidatedPath(path);
const stats = binding.statfsSync(pathModule.toNamespacedPath(path), options.bigint);
return getStatFsFromBinding(stats);
}
function open(path, flags, mode) {
path = getValidatedPath(path);
return binding.openSync(
pathModule.toNamespacedPath(path),
stringToFlags(flags),
parseFileMode(mode, 'mode', 0o666),
);
}
function close(fd) {
fd = getValidatedFd(fd);
return binding.closeSync(fd);
}
module.exports = {
readFileUtf8,
exists,
access,
copyFile,
stat,
statfs,
open,
close,
};

View File

@ -996,6 +996,31 @@ void Access(const FunctionCallbackInfo<Value>& args) {
}
}
static void AccessSync(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
const int argc = args.Length();
CHECK_GE(argc, 2);
CHECK(args[1]->IsInt32());
int mode = args[1].As<Int32>()->Value();
BufferValue path(isolate, args[0]);
CHECK_NOT_NULL(*path);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
uv_fs_t req;
FS_SYNC_TRACE_BEGIN(access);
int err = uv_fs_access(nullptr, &req, *path, mode, nullptr);
uv_fs_req_cleanup(&req);
FS_SYNC_TRACE_END(access);
if (err) {
return env->ThrowUVException(err, "access", nullptr, path.out());
}
}
void Close(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
@ -1021,6 +1046,54 @@ void Close(const FunctionCallbackInfo<Value>& args) {
}
}
static void CloseSync(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK_GE(args.Length(), 1);
CHECK(args[0]->IsInt32());
int fd = args[0].As<Int32>()->Value();
env->RemoveUnmanagedFd(fd);
uv_fs_t req;
FS_SYNC_TRACE_BEGIN(close);
int err = uv_fs_close(nullptr, &req, fd, nullptr);
FS_SYNC_TRACE_END(close);
uv_fs_req_cleanup(&req);
if (err < 0) {
return env->ThrowUVException(err, "close");
}
}
static void ExistsSync(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
CHECK_GE(args.Length(), 1);
BufferValue path(isolate, args[0]);
CHECK_NOT_NULL(*path);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
uv_fs_t req;
auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
FS_SYNC_TRACE_BEGIN(access);
int err = uv_fs_access(nullptr, &req, path.out(), 0, nullptr);
FS_SYNC_TRACE_END(access);
#ifdef _WIN32
// In case of an invalid symlink, `uv_fs_access` on win32
// will **not** return an error and is therefore not enough.
// Double check with `uv_fs_stat()`.
if (err == 0) {
FS_SYNC_TRACE_BEGIN(stat);
err = uv_fs_stat(nullptr, &req, path.out(), nullptr);
FS_SYNC_TRACE_END(stat);
}
#endif // _WIN32
args.GetReturnValue().Set(err == 0);
}
// Used to speed up module loading. Returns an array [string, boolean]
static void InternalModuleReadJSON(const FunctionCallbackInfo<Value>& args) {
@ -1142,6 +1215,41 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
}
}
static void StatSync(const FunctionCallbackInfo<Value>& args) {
Realm* realm = Realm::GetCurrent(args);
BindingData* binding_data = realm->GetBindingData<BindingData>();
Environment* env = realm->env();
CHECK_GE(args.Length(), 3);
BufferValue path(realm->isolate(), args[0]);
bool use_bigint = args[1]->IsTrue();
bool do_not_throw_if_no_entry = args[2]->IsFalse();
CHECK_NOT_NULL(*path);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
env->PrintSyncTrace();
uv_fs_t req;
auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
FS_SYNC_TRACE_BEGIN(stat);
int err = uv_fs_stat(nullptr, &req, *path, nullptr);
FS_SYNC_TRACE_END(stat);
if (err < 0) {
if (err == UV_ENOENT && do_not_throw_if_no_entry) {
return;
}
return env->ThrowUVException(err, "stat", nullptr, path.out());
}
Local<Value> arr = FillGlobalStatsArray(
binding_data, use_bigint, static_cast<const uv_stat_t*>(req.ptr));
args.GetReturnValue().Set(arr);
}
static void LStat(const FunctionCallbackInfo<Value>& args) {
Realm* realm = Realm::GetCurrent(args);
BindingData* binding_data = realm->GetBindingData<BindingData>();
@ -1255,6 +1363,34 @@ static void StatFs(const FunctionCallbackInfo<Value>& args) {
}
}
static void StatFsSync(const FunctionCallbackInfo<Value>& args) {
Realm* realm = Realm::GetCurrent(args);
BindingData* binding_data = realm->GetBindingData<BindingData>();
Environment* env = realm->env();
CHECK_GE(args.Length(), 2);
BufferValue path(realm->isolate(), args[0]);
bool use_bigint = args[1]->IsTrue();
CHECK_NOT_NULL(*path);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
uv_fs_t req;
auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
FS_SYNC_TRACE_BEGIN(statfs);
int err = uv_fs_statfs(nullptr, &req, *path, nullptr);
FS_SYNC_TRACE_END(statfs);
if (err < 0) {
return env->ThrowUVException(err, "statfs", *path, nullptr);
}
Local<Value> arr = FillGlobalStatFsArray(
binding_data, use_bigint, static_cast<const uv_statfs_t*>(req.ptr));
args.GetReturnValue().Set(arr);
}
static void Symlink(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
@ -1964,74 +2100,6 @@ static inline Maybe<void> CheckOpenPermissions(Environment* env,
return JustVoid();
}
static void ReadFileSync(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
auto isolate = env->isolate();
CHECK_GE(args.Length(), 2);
BufferValue path(env->isolate(), args[0]);
CHECK_NOT_NULL(*path);
CHECK(args[1]->IsInt32());
const int flags = args[1].As<Int32>()->Value();
if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
uv_fs_t req;
auto defer_req_cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
FS_SYNC_TRACE_BEGIN(open);
uv_file file = uv_fs_open(nullptr, &req, *path, flags, 438, nullptr);
FS_SYNC_TRACE_END(open);
if (req.result < 0) {
// req will be cleaned up by scope leave.
Local<Value> out[] = {
Integer::New(isolate, req.result), // errno
FIXED_ONE_BYTE_STRING(isolate, "open"), // syscall
};
return args.GetReturnValue().Set(Array::New(isolate, out, arraysize(out)));
}
uv_fs_req_cleanup(&req);
auto defer_close = OnScopeLeave([file]() {
uv_fs_t close_req;
CHECK_EQ(0, uv_fs_close(nullptr, &close_req, file, nullptr));
uv_fs_req_cleanup(&close_req);
});
std::string result{};
char buffer[8192];
uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
FS_SYNC_TRACE_BEGIN(read);
while (true) {
auto r = uv_fs_read(nullptr, &req, file, &buf, 1, -1, nullptr);
if (req.result < 0) {
FS_SYNC_TRACE_END(read);
// req will be cleaned up by scope leave.
Local<Value> out[] = {
Integer::New(isolate, req.result), // errno
FIXED_ONE_BYTE_STRING(isolate, "read"), // syscall
};
return args.GetReturnValue().Set(
Array::New(isolate, out, arraysize(out)));
}
uv_fs_req_cleanup(&req);
if (r <= 0) {
break;
}
result.append(buf.base, r);
}
FS_SYNC_TRACE_END(read);
args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(),
result.data(),
v8::NewStringType::kNormal,
result.size())
.ToLocalChecked());
}
static void Open(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
@ -2068,6 +2136,35 @@ static void Open(const FunctionCallbackInfo<Value>& args) {
}
}
static void OpenSync(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
const int argc = args.Length();
CHECK_GE(argc, 3);
BufferValue path(env->isolate(), args[0]);
CHECK_NOT_NULL(*path);
CHECK(args[1]->IsInt32());
const int flags = args[1].As<Int32>()->Value();
CHECK(args[2]->IsInt32());
const int mode = args[2].As<Int32>()->Value();
if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
uv_fs_t req;
auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
FS_SYNC_TRACE_BEGIN(open);
int err = uv_fs_open(nullptr, &req, *path, flags, mode, nullptr);
FS_SYNC_TRACE_END(open);
if (err < 0) {
return env->ThrowUVException(err, "open", nullptr, path.out());
}
env->AddUnmanagedFd(err);
args.GetReturnValue().Set(err);
}
static void OpenFileHandle(const FunctionCallbackInfo<Value>& args) {
Realm* realm = Realm::GetCurrent(args);
BindingData* binding_data = realm->GetBindingData<BindingData>();
@ -2150,6 +2247,38 @@ static void CopyFile(const FunctionCallbackInfo<Value>& args) {
}
}
static void CopyFileSync(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
const int argc = args.Length();
CHECK_GE(argc, 3);
BufferValue src(isolate, args[0]);
CHECK_NOT_NULL(*src);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemRead, src.ToStringView());
BufferValue dest(isolate, args[1]);
CHECK_NOT_NULL(*dest);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView());
CHECK(args[2]->IsInt32());
const int flags = args[2].As<Int32>()->Value();
uv_fs_t req;
FS_SYNC_TRACE_BEGIN(copyfile);
int err =
uv_fs_copyfile(nullptr, &req, src.out(), dest.out(), flags, nullptr);
uv_fs_req_cleanup(&req);
FS_SYNC_TRACE_END(copyfile);
if (err) {
return env->ThrowUVException(
err, "copyfile", nullptr, src.out(), dest.out());
}
}
// Wrapper for write(2).
//
@ -2412,6 +2541,58 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
}
}
static void ReadFileUtf8(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
auto isolate = env->isolate();
CHECK_GE(args.Length(), 2);
BufferValue path(env->isolate(), args[0]);
CHECK_NOT_NULL(*path);
CHECK(args[1]->IsInt32());
const int flags = args[1].As<Int32>()->Value();
if (CheckOpenPermissions(env, path, flags).IsNothing()) return;
uv_fs_t req;
auto defer_req_cleanup = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); });
FS_SYNC_TRACE_BEGIN(open);
uv_file file = uv_fs_open(nullptr, &req, *path, flags, 438, nullptr);
FS_SYNC_TRACE_END(open);
if (req.result < 0) {
// req will be cleaned up by scope leave.
return env->ThrowUVException(req.result, "open", nullptr, path.out());
}
auto defer_close = OnScopeLeave([file]() {
uv_fs_t close_req;
CHECK_EQ(0, uv_fs_close(nullptr, &close_req, file, nullptr));
uv_fs_req_cleanup(&close_req);
});
std::string result{};
char buffer[8192];
uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));
FS_SYNC_TRACE_BEGIN(read);
while (true) {
auto r = uv_fs_read(nullptr, &req, file, &buf, 1, -1, nullptr);
if (req.result < 0) {
FS_SYNC_TRACE_END(read);
// req will be cleaned up by scope leave.
return env->ThrowUVException(req.result, "read", nullptr, path.out());
}
if (r <= 0) {
break;
}
result.append(buf.base, r);
}
FS_SYNC_TRACE_END(read);
args.GetReturnValue().Set(
ToV8Value(env->context(), result, isolate).ToLocalChecked());
}
// Wrapper for readv(2).
//
@ -2524,7 +2705,6 @@ static void FChmod(const FunctionCallbackInfo<Value>& args) {
}
}
/* fs.chown(path, uid, gid);
* Wrapper for chown(1) / EIO_CHOWN
*/
@ -3171,10 +3351,15 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
Isolate* isolate = isolate_data->isolate();
SetMethod(isolate, target, "access", Access);
SetMethodNoSideEffect(isolate, target, "accessSync", AccessSync);
SetMethod(isolate, target, "close", Close);
SetMethod(isolate, target, "closeSync", CloseSync);
SetMethodNoSideEffect(isolate, target, "existsSync", ExistsSync);
SetMethod(isolate, target, "open", Open);
SetMethod(isolate, target, "openSync", OpenSync);
SetMethod(isolate, target, "openFileHandle", OpenFileHandle);
SetMethod(isolate, target, "read", Read);
SetMethodNoSideEffect(isolate, target, "readFileUtf8", ReadFileUtf8);
SetMethod(isolate, target, "readBuffers", ReadBuffers);
SetMethod(isolate, target, "fdatasync", Fdatasync);
SetMethod(isolate, target, "fsync", Fsync);
@ -3186,10 +3371,11 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
SetMethod(isolate, target, "internalModuleReadJSON", InternalModuleReadJSON);
SetMethod(isolate, target, "internalModuleStat", InternalModuleStat);
SetMethod(isolate, target, "stat", Stat);
SetMethod(isolate, target, "statSync", StatSync);
SetMethod(isolate, target, "lstat", LStat);
SetMethod(isolate, target, "fstat", FStat);
SetMethodNoSideEffect(isolate, target, "readFileSync", ReadFileSync);
SetMethod(isolate, target, "statfs", StatFs);
SetMethod(isolate, target, "statfsSync", StatFsSync);
SetMethod(isolate, target, "link", Link);
SetMethod(isolate, target, "symlink", Symlink);
SetMethod(isolate, target, "readlink", ReadLink);
@ -3199,6 +3385,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
SetMethod(isolate, target, "writeString", WriteString);
SetMethod(isolate, target, "realpath", RealPath);
SetMethod(isolate, target, "copyFile", CopyFile);
SetMethodNoSideEffect(isolate, target, "copyFileSync", CopyFileSync);
SetMethod(isolate, target, "chmod", Chmod);
SetMethod(isolate, target, "fchmod", FChmod);
@ -3286,13 +3473,18 @@ BindingData* FSReqBase::binding_data() {
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(Access);
registry->Register(AccessSync);
StatWatcher::RegisterExternalReferences(registry);
BindingData::RegisterExternalReferences(registry);
registry->Register(Close);
registry->Register(CloseSync);
registry->Register(ExistsSync);
registry->Register(Open);
registry->Register(OpenSync);
registry->Register(OpenFileHandle);
registry->Register(Read);
registry->Register(ReadFileUtf8);
registry->Register(ReadBuffers);
registry->Register(Fdatasync);
registry->Register(Fsync);
@ -3304,10 +3496,11 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(InternalModuleReadJSON);
registry->Register(InternalModuleStat);
registry->Register(Stat);
registry->Register(StatSync);
registry->Register(LStat);
registry->Register(FStat);
registry->Register(ReadFileSync);
registry->Register(StatFs);
registry->Register(StatFsSync);
registry->Register(Link);
registry->Register(Symlink);
registry->Register(ReadLink);
@ -3317,6 +3510,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(WriteString);
registry->Register(RealPath);
registry->Register(CopyFile);
registry->Register(CopyFileSync);
registry->Register(Chmod);
registry->Register(FChmod);

View File

@ -1,4 +1,3 @@
// Flags: --expose-internals
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@ -26,7 +25,6 @@ const common = require('../common');
const assert = require('assert');
const { inspect } = require('util');
const vm = require('vm');
const { internalBinding } = require('internal/test/binding');
const a = assert;
// Disable colored output to prevent color codes from breaking assertion
@ -802,37 +800,6 @@ assert.throws(
}
);
{
// Test caching.
const fs = internalBinding('fs');
const tmp = fs.close;
fs.close = common.mustCall(tmp, 1);
function throwErr() {
assert(
(Buffer.from('test') instanceof Error)
);
}
assert.throws(
() => throwErr(),
{
code: 'ERR_ASSERTION',
constructor: assert.AssertionError,
message: 'The expression evaluated to a falsy value:\n\n ' +
"assert(\n (Buffer.from('test') instanceof Error)\n )\n"
}
);
assert.throws(
() => throwErr(),
{
code: 'ERR_ASSERTION',
constructor: assert.AssertionError,
message: 'The expression evaluated to a falsy value:\n\n ' +
"assert(\n (Buffer.from('test') instanceof Error)\n )\n"
}
);
fs.close = tmp;
}
assert.throws(
() => {
a(

View File

@ -74,7 +74,7 @@ const expectedModules = new Set([
'NativeModule internal/webstreams/queuingstrategies',
'NativeModule internal/blob',
'NativeModule internal/fs/utils',
'NativeModule internal/fs/read/utf8',
'NativeModule internal/fs/sync',
'NativeModule fs',
'Internal Binding options',
'NativeModule internal/options',