Skip to content

Commit 9c334ff

Browse files
committed
fs: throw errors from sync branches instead of separate implementations
Previously to throw errors from C++ land, sync versions of the fs were created by copying C++ code from the original implementation and moving JS code to a separate file. This can lead to several problems: 1. By moving code to a new file for the sake of moving, it would be harder to use git blame to trace changes and harder to backport changes to older branches. 2. Scattering the async and sync versions of fs methods in different files makes it harder to keep them in sync and share code in the prologues and epilogues. 3. Having two copies of code doing almost the same thing results in duplication and can be prone to out-of-sync problems when the prologue and epilogue get updated. 4. There is a minor cost to startup in adding an additional file. This can add up even with the help of snapshots. This patch moves the JS code back to lib/fs.js to stop 1, 2 & 4 and introduces C++ helpers SyncCallAndThrowIf() and SyncCallAndThrowOnError() so that the original implementations can be easily tweaked to allow throwing from C++ and stop 3.
1 parent 3838b57 commit 9c334ff

File tree

6 files changed

+169
-381
lines changed

6 files changed

+169
-381
lines changed

lib/fs.js

+52-14
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ const {
141141
validateObject,
142142
validateString,
143143
} = require('internal/validators');
144-
const syncFs = require('internal/fs/sync');
145144

146145
let truncateWarn = true;
147146
let fs;
@@ -243,7 +242,10 @@ function access(path, mode, callback) {
243242
* @returns {void}
244243
*/
245244
function accessSync(path, mode) {
246-
syncFs.access(path, mode);
245+
path = getValidatedPath(path);
246+
mode = getValidMode(mode, 'access');
247+
248+
binding.access(pathModule.toNamespacedPath(path), mode);
247249
}
248250

249251
/**
@@ -285,7 +287,13 @@ ObjectDefineProperty(exists, kCustomPromisifiedSymbol, {
285287
* @returns {boolean}
286288
*/
287289
function existsSync(path) {
288-
return syncFs.exists(path);
290+
try {
291+
path = getValidatedPath(path);
292+
} catch {
293+
return false;
294+
}
295+
296+
return binding.existsSync(pathModule.toNamespacedPath(path));
289297
}
290298

291299
function readFileAfterOpen(err, fd) {
@@ -438,7 +446,10 @@ function readFileSync(path, options) {
438446
options = getOptions(options, { flag: 'r' });
439447

440448
if (options.encoding === 'utf8' || options.encoding === 'utf-8') {
441-
return syncFs.readFileUtf8(path, options.flag);
449+
if (!isInt32(path)) {
450+
path = pathModule.toNamespacedPath(getValidatedPath(path));
451+
}
452+
return binding.readFileUtf8(path, stringToFlags(options.flag));
442453
}
443454

444455
const isUserFd = isFd(path); // File descriptor ownership
@@ -516,7 +527,9 @@ function close(fd, callback = defaultCloseCallback) {
516527
* @returns {void}
517528
*/
518529
function closeSync(fd) {
519-
return syncFs.close(fd);
530+
fd = getValidatedFd(fd);
531+
532+
return binding.close(fd);
520533
}
521534

522535
/**
@@ -562,7 +575,13 @@ function open(path, flags, mode, callback) {
562575
* @returns {number}
563576
*/
564577
function openSync(path, flags, mode) {
565-
return syncFs.open(path, flags, mode);
578+
path = getValidatedPath(path);
579+
580+
return binding.open(
581+
pathModule.toNamespacedPath(path),
582+
stringToFlags(flags),
583+
parseFileMode(mode, 'mode', 0o666),
584+
);
566585
}
567586

568587
/**
@@ -1665,12 +1684,24 @@ function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) {
16651684
* }} [options]
16661685
* @returns {Stats}
16671686
*/
1668-
function statSync(path, options) {
1669-
return syncFs.stat(path, options);
1687+
function statSync(path, options = { bigint: false, throwIfNoEntry: true }) {
1688+
path = getValidatedPath(path);
1689+
const stats = binding.stat(
1690+
pathModule.toNamespacedPath(path),
1691+
options.bigint,
1692+
undefined,
1693+
options.throwIfNoEntry,
1694+
);
1695+
if (stats === undefined) {
1696+
return undefined;
1697+
}
1698+
return getStatsFromBinding(stats);
16701699
}
16711700

1672-
function statfsSync(path, options) {
1673-
return syncFs.statfs(path, options);
1701+
function statfsSync(path, options = { bigint: false }) {
1702+
path = getValidatedPath(path);
1703+
const stats = binding.statfs(pathModule.toNamespacedPath(path), options.bigint);
1704+
return getStatFsFromBinding(stats);
16741705
}
16751706

16761707
/**
@@ -1850,7 +1881,8 @@ function unlink(path, callback) {
18501881
* @returns {void}
18511882
*/
18521883
function unlinkSync(path) {
1853-
return syncFs.unlink(path);
1884+
path = pathModule.toNamespacedPath(getValidatedPath(path));
1885+
return binding.unlink(path);
18541886
}
18551887

18561888
/**
@@ -2650,8 +2682,7 @@ function realpathSync(p, options) {
26502682
}
26512683
if (linkTarget === null) {
26522684
const ctx = { path: base };
2653-
binding.stat(baseLong, false, undefined, ctx);
2654-
handleErrorFromBinding(ctx);
2685+
binding.stat(baseLong, false, undefined, false);
26552686
linkTarget = binding.readlink(baseLong, undefined, undefined, ctx);
26562687
handleErrorFromBinding(ctx);
26572688
}
@@ -2946,7 +2977,14 @@ function copyFile(src, dest, mode, callback) {
29462977
* @returns {void}
29472978
*/
29482979
function copyFileSync(src, dest, mode) {
2949-
syncFs.copyFile(src, dest, mode);
2980+
src = getValidatedPath(src, 'src');
2981+
dest = getValidatedPath(dest, 'dest');
2982+
2983+
binding.copyFile(
2984+
pathModule.toNamespacedPath(src),
2985+
pathModule.toNamespacedPath(dest),
2986+
getValidMode(mode, 'copyFile'),
2987+
);
29502988
}
29512989

29522990
/**

lib/internal/fs/sync.js

-106
This file was deleted.

src/node_file-inl.h

+32
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,38 @@ int SyncCall(Environment* env, v8::Local<v8::Value> ctx,
349349
return err;
350350
}
351351

352+
// Similar to SyncCall but throws immediately if there is an error.
353+
template <typename Predicate, typename Func, typename... Args>
354+
int SyncCallAndThrowIf(Predicate should_throw,
355+
Environment* env,
356+
FSReqWrapSync* req_wrap,
357+
Func fn,
358+
Args... args) {
359+
env->PrintSyncTrace();
360+
int result = fn(env->event_loop(), &(req_wrap->req), args..., nullptr);
361+
if (should_throw(result)) {
362+
env->ThrowUVException(result,
363+
req_wrap->syscall_p,
364+
nullptr,
365+
req_wrap->path_p,
366+
req_wrap->dest_p);
367+
}
368+
return result;
369+
}
370+
371+
constexpr bool is_uv_error(int result) {
372+
return result < 0;
373+
}
374+
375+
// Similar to SyncCall but throws immediately if there is an error.
376+
template <typename Func, typename... Args>
377+
int SyncCallAndThrowOnError(Environment* env,
378+
FSReqWrapSync* req_wrap,
379+
Func fn,
380+
Args... args) {
381+
return SyncCallAndThrowIf(is_uv_error, env, req_wrap, fn, args...);
382+
}
383+
352384
} // namespace fs
353385
} // namespace node
354386

0 commit comments

Comments
 (0)