lib: speed up require(), phase 1
Replace calls to fs.statSync() with an internal variant that does not create Error or Stat objects that put strain on the garbage collector. A secondary benefit is that it improves start-up times in the debugger because it no longer emits thousands of exception debug events. PR-URL: https://github.com/nodejs/io.js/pull/1801 Reviewed-By: Trevor Norris <trev.norris@gmail.com>
This commit is contained in:
parent
98649fd31a
commit
b14fd1a720
@ -6,6 +6,7 @@ const runInThisContext = require('vm').runInThisContext;
|
|||||||
const assert = require('assert').ok;
|
const assert = require('assert').ok;
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const internalModuleStat = process.binding('fs').internalModuleStat;
|
||||||
|
|
||||||
|
|
||||||
// If obj.hasOwnProperty has been overridden, then calling
|
// If obj.hasOwnProperty has been overridden, then calling
|
||||||
@ -56,13 +57,6 @@ const debug = Module._debug;
|
|||||||
// -> a.<ext>
|
// -> a.<ext>
|
||||||
// -> a/index.<ext>
|
// -> a/index.<ext>
|
||||||
|
|
||||||
function statPath(path) {
|
|
||||||
try {
|
|
||||||
return fs.statSync(path);
|
|
||||||
} catch (ex) {}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the directory is a package.json dir
|
// check if the directory is a package.json dir
|
||||||
const packageMainCache = {};
|
const packageMainCache = {};
|
||||||
|
|
||||||
@ -94,7 +88,7 @@ function tryPackage(requestPath, exts) {
|
|||||||
if (!pkg) return false;
|
if (!pkg) return false;
|
||||||
|
|
||||||
var filename = path.resolve(requestPath, pkg);
|
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);
|
tryExtensions(path.resolve(filename, 'index'), exts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,18 +98,19 @@ function tryPackage(requestPath, exts) {
|
|||||||
Module._realpathCache = {};
|
Module._realpathCache = {};
|
||||||
|
|
||||||
// check if the file exists and is not a directory
|
// check if the file exists and is not a directory
|
||||||
function tryFile(requestPath, stats) {
|
function tryFile(requestPath) {
|
||||||
stats = stats || statPath(requestPath);
|
const rc = internalModuleStat(requestPath);
|
||||||
if (stats && !stats.isDirectory()) {
|
return rc === 0 && toRealPath(requestPath);
|
||||||
return fs.realpathSync(requestPath, Module._realpathCache);
|
}
|
||||||
}
|
|
||||||
return false;
|
function toRealPath(requestPath) {
|
||||||
|
return fs.realpathSync(requestPath, Module._realpathCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
// given a path check a the file exists with any of the set extensions
|
// given a path check a the file exists with any of the set extensions
|
||||||
function tryExtensions(p, exts) {
|
function tryExtensions(p, exts) {
|
||||||
for (var i = 0, EL = exts.length; i < EL; i++) {
|
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) {
|
if (filename) {
|
||||||
return filename;
|
return filename;
|
||||||
@ -150,11 +145,10 @@ Module._findPath = function(request, paths) {
|
|||||||
var filename;
|
var filename;
|
||||||
|
|
||||||
if (!trailingSlash) {
|
if (!trailingSlash) {
|
||||||
var stats = statPath(basePath);
|
const rc = internalModuleStat(basePath);
|
||||||
// try to join the request to the path
|
if (rc === 0) { // File.
|
||||||
filename = tryFile(basePath, stats);
|
filename = toRealPath(basePath);
|
||||||
|
} else if (rc === 1) { // Directory.
|
||||||
if (!filename && stats && stats.isDirectory()) {
|
|
||||||
filename = tryPackage(basePath, exts);
|
filename = tryPackage(basePath, exts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,6 +433,26 @@ Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
|
|||||||
return handle_scope.Escape(stats);
|
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<Value>& 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<const uv_stat_t*>(req.ptr);
|
||||||
|
rc = !!(s->st_mode & S_IFDIR);
|
||||||
|
}
|
||||||
|
uv_fs_req_cleanup(&req);
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(rc);
|
||||||
|
}
|
||||||
|
|
||||||
static void Stat(const FunctionCallbackInfo<Value>& args) {
|
static void Stat(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
@ -1141,6 +1161,7 @@ void InitFs(Handle<Object> target,
|
|||||||
env->SetMethod(target, "rmdir", RMDir);
|
env->SetMethod(target, "rmdir", RMDir);
|
||||||
env->SetMethod(target, "mkdir", MKDir);
|
env->SetMethod(target, "mkdir", MKDir);
|
||||||
env->SetMethod(target, "readdir", ReadDir);
|
env->SetMethod(target, "readdir", ReadDir);
|
||||||
|
env->SetMethod(target, "internalModuleStat", InternalModuleStat);
|
||||||
env->SetMethod(target, "stat", Stat);
|
env->SetMethod(target, "stat", Stat);
|
||||||
env->SetMethod(target, "lstat", LStat);
|
env->SetMethod(target, "lstat", LStat);
|
||||||
env->SetMethod(target, "fstat", FStat);
|
env->SetMethod(target, "fstat", FStat);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user