mirror of
https://github.com/nodejs/node.git
synced 2025-04-30 07:19:19 +00:00
fs,module: add module-loader-only realpath cache
Reintroduce a realpath cache with the same mechanisms which existed
before b488b19eaf
(`fs: optimize realpath using uv_fs_realpath()`), but only for
the synchronous version and with the cache being passed as a
hidden option to make sure it is only used internally.
The cache is hidden from userland applications because it has been
decided that fully reintroducing as part of the public API might stand
in the way of future optimizations.
PR-URL: https://github.com/nodejs/node/pull/8100
Reviewed-By: Bartosz Sosnowski <bartosz@janeasystems.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
7bc6aeac86
commit
c084287a60
64
lib/fs.js
64
lib/fs.js
@ -1514,6 +1514,10 @@ function encodeRealpathResult(result, options, err) {
|
||||
}
|
||||
}
|
||||
|
||||
// This is removed from the fs exports in lib/module.js in order to make
|
||||
// sure that this stays internal.
|
||||
const realpathCacheKey = fs.realpathCacheKey = Symbol('realpathCacheKey');
|
||||
|
||||
fs.realpathSync = function realpathSync(p, options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
@ -1528,6 +1532,13 @@ fs.realpathSync = function realpathSync(p, options) {
|
||||
|
||||
const seenLinks = {};
|
||||
const knownHard = {};
|
||||
const cache = options[realpathCacheKey];
|
||||
const original = p;
|
||||
|
||||
const maybeCachedResult = cache && cache.get(p);
|
||||
if (maybeCachedResult) {
|
||||
return maybeCachedResult;
|
||||
}
|
||||
|
||||
// current character position in p
|
||||
var pos;
|
||||
@ -1568,39 +1579,47 @@ fs.realpathSync = function realpathSync(p, options) {
|
||||
pos = nextPartRe.lastIndex;
|
||||
|
||||
// continue if not a symlink
|
||||
if (knownHard[base]) {
|
||||
if (knownHard[base] || (cache && cache.get(base) === base)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var resolvedLink;
|
||||
var stat = fs.lstatSync(base);
|
||||
if (!stat.isSymbolicLink()) {
|
||||
knownHard[base] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// read the link if it wasn't read before
|
||||
// dev/ino always return 0 on windows, so skip the check.
|
||||
var linkTarget = null;
|
||||
if (!isWindows) {
|
||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
||||
if (seenLinks.hasOwnProperty(id)) {
|
||||
linkTarget = seenLinks[id];
|
||||
const maybeCachedResolved = cache && cache.get(base);
|
||||
if (maybeCachedResolved) {
|
||||
resolvedLink = maybeCachedResolved;
|
||||
} else {
|
||||
var stat = fs.lstatSync(base);
|
||||
if (!stat.isSymbolicLink()) {
|
||||
knownHard[base] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (linkTarget === null) {
|
||||
fs.statSync(base);
|
||||
linkTarget = fs.readlinkSync(base);
|
||||
}
|
||||
resolvedLink = pathModule.resolve(previous, linkTarget);
|
||||
|
||||
if (!isWindows) seenLinks[id] = linkTarget;
|
||||
// read the link if it wasn't read before
|
||||
// dev/ino always return 0 on windows, so skip the check.
|
||||
let linkTarget = null;
|
||||
let id;
|
||||
if (!isWindows) {
|
||||
id = `${stat.dev.toString(32)}:${stat.ino.toString(32)}`;
|
||||
if (seenLinks.hasOwnProperty(id)) {
|
||||
linkTarget = seenLinks[id];
|
||||
}
|
||||
}
|
||||
if (linkTarget === null) {
|
||||
fs.statSync(base);
|
||||
linkTarget = fs.readlinkSync(base);
|
||||
}
|
||||
resolvedLink = pathModule.resolve(previous, linkTarget);
|
||||
|
||||
if (cache) cache.set(base, resolvedLink);
|
||||
if (!isWindows) seenLinks[id] = linkTarget;
|
||||
}
|
||||
|
||||
// resolve the link, then start over
|
||||
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
||||
start();
|
||||
}
|
||||
|
||||
if (cache) cache.set(original, p);
|
||||
return encodeRealpathResult(p, options);
|
||||
};
|
||||
|
||||
@ -1696,8 +1715,9 @@ fs.realpath = function realpath(p, options, callback) {
|
||||
// stat & read the link if not read before
|
||||
// call gotTarget as soon as the link target is known
|
||||
// dev/ino always return 0 on windows, so skip the check.
|
||||
let id;
|
||||
if (!isWindows) {
|
||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
||||
id = `${stat.dev.toString(32)}:${stat.ino.toString(32)}`;
|
||||
if (seenLinks.hasOwnProperty(id)) {
|
||||
return gotTarget(null, seenLinks[id], base);
|
||||
}
|
||||
|
@ -109,6 +109,14 @@ function tryPackage(requestPath, exts, isMain) {
|
||||
tryExtensions(path.resolve(filename, 'index'), exts, isMain);
|
||||
}
|
||||
|
||||
// In order to minimize unnecessary lstat() calls,
|
||||
// this cache is a list of known-real paths.
|
||||
// Set to an empty Map to reset.
|
||||
const realpathCache = new Map();
|
||||
|
||||
const realpathCacheKey = fs.realpathCacheKey;
|
||||
delete fs.realpathCacheKey;
|
||||
|
||||
// check if the file exists and is not a directory
|
||||
// if using --preserve-symlinks and isMain is false,
|
||||
// keep symlinks intact, otherwise resolve to the
|
||||
@ -118,7 +126,13 @@ function tryFile(requestPath, isMain) {
|
||||
if (preserveSymlinks && !isMain) {
|
||||
return rc === 0 && path.resolve(requestPath);
|
||||
}
|
||||
return rc === 0 && fs.realpathSync(requestPath);
|
||||
return rc === 0 && toRealPath(requestPath);
|
||||
}
|
||||
|
||||
function toRealPath(requestPath) {
|
||||
return fs.realpathSync(requestPath, {
|
||||
[realpathCacheKey]: realpathCache
|
||||
});
|
||||
}
|
||||
|
||||
// given a path check a the file exists with any of the set extensions
|
||||
@ -164,7 +178,7 @@ Module._findPath = function(request, paths, isMain) {
|
||||
if (preserveSymlinks && !isMain) {
|
||||
filename = path.resolve(basePath);
|
||||
} else {
|
||||
filename = fs.realpathSync(basePath);
|
||||
filename = toRealPath(basePath);
|
||||
}
|
||||
} else if (rc === 1) { // Directory.
|
||||
if (exts === undefined)
|
||||
|
Loading…
Reference in New Issue
Block a user