diff --git a/lib/module.js b/lib/module.js index 515ab6789..c9e4e2c78 100644 --- a/lib/module.js +++ b/lib/module.js @@ -6,6 +6,7 @@ const runInThisContext = require('vm').runInThisContext; const assert = require('assert').ok; const fs = require('fs'); const path = require('path'); +const internalModuleStat = process.binding('fs').internalModuleStat; // If obj.hasOwnProperty has been overridden, then calling @@ -56,13 +57,6 @@ const debug = Module._debug; // -> a. // -> a/index. -function statPath(path) { - try { - return fs.statSync(path); - } catch (ex) {} - return false; -} - // check if the directory is a package.json dir const packageMainCache = {}; @@ -94,7 +88,7 @@ function tryPackage(requestPath, exts) { if (!pkg) return false; var filename = path.resolve(requestPath, pkg); - return tryFile(filename, null) || tryExtensions(filename, exts) || + return tryFile(filename) || tryExtensions(filename, exts) || tryExtensions(path.resolve(filename, 'index'), exts); } @@ -104,18 +98,19 @@ function tryPackage(requestPath, exts) { Module._realpathCache = {}; // check if the file exists and is not a directory -function tryFile(requestPath, stats) { - stats = stats || statPath(requestPath); - if (stats && !stats.isDirectory()) { - return fs.realpathSync(requestPath, Module._realpathCache); - } - return false; +function tryFile(requestPath) { + const rc = internalModuleStat(requestPath); + return rc === 0 && toRealPath(requestPath); +} + +function toRealPath(requestPath) { + return fs.realpathSync(requestPath, Module._realpathCache); } // given a path check a the file exists with any of the set extensions function tryExtensions(p, exts) { for (var i = 0, EL = exts.length; i < EL; i++) { - var filename = tryFile(p + exts[i], null); + var filename = tryFile(p + exts[i]); if (filename) { return filename; @@ -150,11 +145,10 @@ Module._findPath = function(request, paths) { var filename; if (!trailingSlash) { - var stats = statPath(basePath); - // try to join the request to the path - filename = tryFile(basePath, stats); - - if (!filename && stats && stats.isDirectory()) { + const rc = internalModuleStat(basePath); + if (rc === 0) { // File. + filename = toRealPath(basePath); + } else if (rc === 1) { // Directory. filename = tryPackage(basePath, exts); } diff --git a/src/node_file.cc b/src/node_file.cc index 71cf31829..4e00f15e2 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -433,6 +433,26 @@ Local BuildStatsObject(Environment* env, const uv_stat_t* s) { return handle_scope.Escape(stats); } +// Used to speed up module loading. Returns 0 if the path refers to +// a file, 1 when it's a directory or < 0 on error (usually -ENOENT.) +// The speedup comes from not creating thousands of Stat and Error objects. +static void InternalModuleStat(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsString()); + node::Utf8Value path(env->isolate(), args[0]); + + uv_fs_t req; + int rc = uv_fs_stat(env->event_loop(), &req, *path, nullptr); + if (rc == 0) { + const uv_stat_t* const s = static_cast(req.ptr); + rc = !!(s->st_mode & S_IFDIR); + } + uv_fs_req_cleanup(&req); + + args.GetReturnValue().Set(rc); +} + static void Stat(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); @@ -1141,6 +1161,7 @@ void InitFs(Handle target, env->SetMethod(target, "rmdir", RMDir); env->SetMethod(target, "mkdir", MKDir); env->SetMethod(target, "readdir", ReadDir); + env->SetMethod(target, "internalModuleStat", InternalModuleStat); env->SetMethod(target, "stat", Stat); env->SetMethod(target, "lstat", LStat); env->SetMethod(target, "fstat", FStat);