NodeJS执行js文件流程
2020-03-27 本文已影响0人
orgcheng
在终端执行node test.js来运行test.js文件,通过抛出的异常日志,来分析nodejs的执行流程
D:\AliOS\HelloWorldTS>node test.js
internal/modules/cjs/loader.js:800
throw err;
^
Error: Cannot find module 'D:\AliOS\HelloWorldTS\test.js'
[90m at Function.Module._resolveFilename (internal/modules/cjs/loader.js:797:15)[
[90m at Function.Module._load (internal/modules/cjs/loader.js:690:27)[39m
[90m at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)[39m
[90m at internal/main/run_main_module.js:17:11[39m {
code: [32m'MODULE_NOT_FOUND'[39m,
requireStack: []
}
先看 internal/main/run_main_module.js
const {
prepareMainThreadExecution
} = require('internal/bootstrap/pre_execution');
prepareMainThreadExecution(true);
require('internal/modules/cjs/loader').Module.runMain(process.argv[1]);
最后一句,加载internal/modules/cjs/loader并运行Module.runMain方法,传递的参数就是test.js
但是它首先加载了internal/bootstrap/pre_execution的prepareMainThreadExecution方法,并执行。
接下来就看看internal/bootstrap/pre_execution
function prepareMainThreadExecution(expandArgv1 = false) {
...
initializeCJSLoader();
...
}
function initializeCJSLoader() {
const CJSLoader = require('internal/modules/cjs/loader');
CJSLoader.Module._initPaths();
// TODO(joyeecheung): deprecate this in favor of a proper hook?
CJSLoader.Module.runMain =
require('internal/modules/run_main').executeUserEntryPoint;
}
module.exports = {
...
prepareMainThreadExecut
};
到此我们知道,internal/modules/cjs/loader中的Module.runMain方法,是internal/modules/run_main模块的executeUserEntryPoint方法
看下internal/modules/run_main
function executeUserEntryPoint(main = process.argv[1]) {
const resolvedMain = resolveMainPath(main);
const useESMLoader = shouldUseESMLoader(resolvedMain);
if (useESMLoader) {
runMainESM(resolvedMain || main);
} else {
// Module._load is the monkey-patchable CJS module loader.
Module._load(main, null, true); // 只需要知道,最终走这里就可以了
}
}
module.exports = {
executeUserEntryPoint
};
接下来就是执行internal/modules/cjs/loader中的Module._load方法
Module._extensions = ObjectCreate(null);
// Check the cache for the requested file.
// 1. If a module already exists in the cache: return its exports object.
// 2. If the module is native: call
// `NativeModule.prototype.compileForPublicLoader()` and return the exports.
// 3. Otherwise, create a new module for the file and save it to the cache.
// Then have it load the file contents before returning its exports
// object.
Module._load = function(request, parent, isMain) {
let relResolveCacheIdentifier;
if (parent) {
... // 这里parent=null,不执行此处代码
}
// 开头报错就是这里抛出的
const filename = Module._resolveFilename(request, parent, isMain);
// 查找缓存
const cachedModule = Module._cache[filename];
if (cachedModule !== undefined) {
updateChildren(parent, cachedModule, true);
return cachedModule.exports;
}
// 加载Native模块
const mod = loadNativeModule(filename, request, experimentalModules);
if (mod && mod.canBeRequiredByUsers) return mod.exports;
// Don't call updateChildren(), Module constructor already does.
const module = new Module(filename, parent);
if (isMain) {
process.mainModule = module;
module.id = '.';
}
Module._cache[filename] = module;
if (parent !== undefined) {
relativeResolveCache[relResolveCacheIdentifier] = filename;
}
let threw = true;
try {
// Intercept exceptions that occur during the first tick and rekey them
// on error instance rather than module instance (which will immediately be
// garbage collected).
if (enableSourceMaps) {
try {
module.load(filename);
} catch (err) {
rekeySourceMap(Module._cache[filename], err);
throw err; /* node-do-not-add-exception-line */
}
} else {
module.load(filename);
}
threw = false;
} finally {
if (threw) {
delete Module._cache[filename];
if (parent !== undefined) {
delete relativeResolveCache[relResolveCacheIdentifier];
}
}
}
return module.exports;
};
// Given a file name, pass it to the proper extension handler.
Module.prototype.load = function(filename) {
this.filename = filename;
this.paths = Module._nodeModulePaths(path.dirname(filename));
const extension = findLongestRegisteredExtension(filename);
// allow .mjs to be overridden
if (filename.endsWith('.mjs') && !Module._extensions['.mjs']) {
throw new ERR_REQUIRE_ESM(filename);
}
// 这里是调用对应的扩展名方法
Module._extensions[extension](this, filename);
this.loaded = true;
...
}
// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
if (filename.endsWith('.js')) {
const pkg = readPackageScope(filename);
// Function require shouldn't be used in ES modules.
if (pkg && pkg.data && pkg.data.type === 'module') {
const parentPath = module.parent && module.parent.filename;
const packageJsonPath = path.resolve(pkg.path, 'package.json');
throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
}
}
const content = fs.readFileSync(filename, 'utf8');
module._compile(content, filename);
};
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {
...
maybeCacheSourceMap(filename, content, this);
const compiledWrapper = wrapSafe(filename, content, this);
...
const dirname = path.dirname(filename);
const require = makeRequireFunction(this, redirects);
let result;
const exports = this.exports;
const thisValue = exports;
const module = this;
...
result = compiledWrapper.call(thisValue, exports, require, module,
filename, dirname);
...
hasLoadedAnyUserCJSModule = true;
return result;
};
function wrapSafe(filename, content, cjsModuleInstance) {
if (patched) {
...//这里patched为false,表示没有修改过wrapper数组
}
let compiled;
try {
compiled = compileFunction(
content,
filename,
0,
0,
undefined,
false,
undefined,
[],
[
'exports',
'require',
'module',
'__filename',
'__dirname',
]
);
} catch (err) {
if (experimentalModules && process.mainModule === cjsModuleInstance)
enrichCJSError(err);
throw err;
}
return compiled.function;
}
const { compileFunction } = internalBinding('contextify');
internalBinding就是nodejs的C++代码层暴露到js层的方法,用来加载内置的C++模块
接着看下internal/main/run_main_module方法是在哪里调用的,搜索文件名后发现在node.cc代码中使用到了
// node.cc文件
MaybeLocal<Value> StartMainThreadExecution(Environment* env) {
// To allow people to extend Node in different ways, this hook allows
// one to drop a file lib/_third_party_main.js into the build
// directory which will be executed instead of Node's normal loading.
if (NativeModuleEnv::Exists("_third_party_main")) {
return StartExecution(env, "internal/main/run_third_party_main");
}
std::string first_argv;
if (env->argv().size() > 1) {
first_argv = env->argv()[1];
}
...
if (!first_argv.empty() && first_argv != "-") {
return StartExecution(env, "internal/main/run_main_module");
}
...
return StartExecution(env, "internal/main/eval_stdin");
}
void LoadEnvironment(Environment* env) {
CHECK(env->is_main_thread());
// TODO(joyeecheung): Not all of the execution modes in
// StartMainThreadExecution() make sense for embedders. Pick the
// useful ones out, and allow embedders to customize the entry
// point more directly without using _third_party_main.js
USE(StartMainThreadExecution(env));
}
LoadEnvironment方法是在node_main_instance.cc方法中执行的
int NodeMainInstance::Run() {
Locker locker(isolate_);
Isolate::Scope isolate_scope(isolate_);
HandleScope handle_scope(isolate_);
int exit_code = 0;
// TODO
std::unique_ptr<Environment> env = CreateMainEnvironment(&exit_code);
CHECK_NOT_NULL(env);
Context::Scope context_scope(env->context());
if (exit_code == 0) {
{
InternalCallbackScope callback_scope(
env.get(),
Local<Object>(),
{ 1, 0 },
InternalCallbackScope::kAllowEmptyResource |
InternalCallbackScope::kSkipAsyncHooks);
// TODO
LoadEnvironment(env.get());
}
env->set_trace_sync_io(env->options()->trace_sync_io);
{
SealHandleScope seal(isolate_);
bool more;
env->performance_state()->Mark(
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
// TODO chengzhen libuv库的试下Nodejs主循环
do {
uv_run(env->event_loop(), UV_RUN_DEFAULT);
per_process::v8_platform.DrainVMTasks(isolate_);
more = uv_loop_alive(env->event_loop());
if (more && !env->is_stopping()) continue;
if (!uv_loop_alive(env->event_loop())) {
EmitBeforeExit(env.get());
}
// Emit `beforeExit` if the loop became alive either after emitting
// event, or after running some callbacks.
more = uv_loop_alive(env->event_loop());
} while (more == true && !env->is_stopping());
env->performance_state()->Mark(
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
}
env->set_trace_sync_io(false);
exit_code = EmitExit(env.get());
}
env->set_can_call_into_js(false);
env->stop_sub_worker_contexts();
ResetStdio();
env->RunCleanup();
// TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really
// make sense here.
#if HAVE_INSPECTOR && defined(__POSIX__) && !defined(NODE_SHARED_MODE)
struct sigaction act;
memset(&act, 0, sizeof(act));
for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF)
continue;
act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
CHECK_EQ(0, sigaction(nr, &act, nullptr));
}
#endif
RunAtExit(env.get());
per_process::v8_platform.DrainVMTasks(isolate_);
#if defined(LEAK_SANITIZER)
__lsan_do_leak_check();
#endif
return exit_code;
}