You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// Set up process.binding() and process._linkedBinding()
{
const bindingObj = Object.create(null);
process.binding = function binding(module) {
module = String(module);
let mod = bindingObj[module];
if (typeof mod !== 'object') {
mod = bindingObj[module] = getBinding(module);
moduleLoadList.push(`Binding ${module}`);
}
return mod;
};
process._linkedBinding = function _linkedBinding(module) {
module = String(module);
let mod = bindingObj[module];
if (typeof mod !== 'object')
mod = bindingObj[module] = getLinkedBinding(module);
return mod;
};
}
// Set up internalBinding() in the closure
let internalBinding;
{
const bindingObj = Object.create(null);
internalBinding = function internalBinding(module) {
let mod = bindingObj[module];
if (typeof mod !== 'object') {
mod = bindingObj[module] = getInternalBinding(module);
moduleLoadList.push(`Internal Binding ${module}`);
}
return mod;
};
}
NativeModule.require = function(id) {
if (id === loaderId) {
return loaderExports;
}
const cached = NativeModule.getCached(id);
if (cached && (cached.loaded || cached.loading)) {
return cached.exports;
}
if (!NativeModule.exists(id)) {
// Model the error off the internal/errors.js model, but
// do not use that module given that it could actually be
// the one causing the error if there's a bug in Node.js
// eslint-disable-next-line no-restricted-syntax
const err = new Error(`No such built-in module: ${id}`);
err.code = 'ERR_UNKNOWN_BUILTIN_MODULE';
err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]';
throw err;
}
moduleLoadList.push(`NativeModule ${id}`);
const nativeModule = new NativeModule(id);
nativeModule.cache();
nativeModule.compile();
return nativeModule.exports;
};
function setupV8() {
// Warm up the map and set iterator preview functions. V8 compiles
// functions lazily (unless --nolazy is set) so we need to do this
// before we turn off --allow_natives_syntax again.
const v8 = NativeModule.require('internal/v8');
// 初始化map遍历器等
v8.previewMapIterator(new Map().entries());
v8.previewSetIterator(new Set().entries());
v8.previewWeakMap(new WeakMap(), 1);
v8.previewWeakSet(new WeakSet(), 1);
// Disable --allow_natives_syntax again unless it was explicitly
// specified on the command line.
// 自此Disable掉--allow_natives_syntax
const re = /^--allow[-_]natives[-_]syntax$/;
if (!process.execArgv.some((s) => re.test(s)))
process.binding('v8').setFlagsFromString('--noallow_natives_syntax');
}
其中v8.previewMapIterator就用到了v8 builtin模块:
// Clone the provided Map Iterator.
function previewMapIterator(it) {
// v8 build-in函数,js中调用时以%开头
// 函数一般在v8内部代码中调用,用户的js代码中调用需使用--allow-natives-syntax标记执行
return %MapIteratorClone(it);
}
2.执行
执行阶段的代码如下:
// There is user code to be run
// 有用户代码要执行
// If this is a worker in cluster mode, start up the communication
// channel. This needs to be done before any user code gets executed
// (including preload modules).
// 如果在集群模式下有worder,需要先初始化
if (process.argv[1] && process.env.NODE_UNIQUE_ID) {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_START);
const cluster = NativeModule.require('cluster');
// 实例化worker
// 监听disconnect,newconn等
cluster._setupWorker();
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_END);
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_UNIQUE_ID;
}
if (process._eval != null && !process._forceRepl) {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START);
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END);
// User passed '-e' or '--eval' arguments to Node without '-i' or
// '--interactive'
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START);
preloadModules();
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END);
const {
addBuiltinLibsToObject
} = NativeModule.require('internal/modules/cjs/helpers');
// 为global加上'assert', 'async_hooks', 'buffer'等属性
addBuiltinLibsToObject(global);
evalScript('[eval]');
} else if (process.argv[1] && process.argv[1] !== '-') {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START);
// make process.argv[1] into a full path
const path = NativeModule.require('path');
process.argv[1] = path.resolve(process.argv[1]);
const CJSModule = NativeModule.require('internal/modules/cjs/loader');
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END);
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START);
preloadModules();
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END);
// check if user passed `-c` or `--check` arguments to Node.
if (process._syntax_check_only != null) {
const fs = NativeModule.require('fs');
// read the source
// 查找文件
const filename = CJSModule._resolveFilename(process.argv[1]);
const source = fs.readFileSync(filename, 'utf-8');
// 检测语法,去掉shebang、BOM等
checkScriptSyntax(source, filename);
process.exit(0);
}
CJSModule.runMain();
} else {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START);
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END);
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START);
preloadModules();
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END);
// If -i or --interactive were passed, or stdin is a TTY.
if (process._forceRepl || NativeModule.require('tty').isatty(0)) {
// REPL
const cliRepl = NativeModule.require('internal/repl');
cliRepl.createInternalRepl(process.env, function(err, repl) {
if (err) {
throw err;
}
repl.on('exit', function() {
if (repl._flushing) {
repl.pause();
return repl.once('flushHistory', function() {
process.exit();
});
}
process.exit();
});
});
if (process._eval != null) {
// User passed '-e' or '--eval'
evalScript('[eval]');
}
} else {
// Read all of stdin - execute it.
process.stdin.setEncoding('utf8');
let code = '';
process.stdin.on('data', function(d) {
code += d;
});
process.stdin.on('end', function() {
if (process._syntax_check_only != null) {
checkScriptSyntax(code, '[stdin]');
} else {
process._eval = code;
evalScript('[stdin]');
}
});
}
}
fakeParent.paths = Module._nodeModulePaths(path);
Module._nodeModulePaths = function(from) {
// guarantee that 'from' is absolute.
from = path.resolve(from);
// Return early not only to avoid unnecessary work, but to *avoid* returning
// an array of two items for a root: [ '//node_modules', '/node_modules' ]
if (from === '/')
return ['/node_modules'];
// note: this approach *only* works when the path is guaranteed
// to be absolute. Doing a fully-edge-case-correct path.split
// that works on both Windows and Posix is non-trivial.
const paths = [];
var p = 0;
var last = from.length;
for (var i = from.length - 1; i >= 0; --i) {
const code = from.charCodeAt(i);
if (code === CHAR_FORWARD_SLASH) {
if (p !== nmLen)
paths.push(from.slice(0, last) + '/node_modules');
last = i;
p = 0;
} else if (p !== -1) {
if (nmChars[p] === code) {
++p;
} else {
p = -1;
}
}
}
// Append /node_modules to handle root paths.
paths.push('/node_modules');
return paths;
};
上面文章提到过在src/node.cc中的LoadEnvironment方法会执行
internal/bootstrap/loaders.js
和internal/bootstrap/node.js
,本文就来看看这两个模块做了什么,小伙伴们注意一下,这里会包含威名远扬的vm、模块加载等方面的讲解。GetBootstrapper
首先我们来看下如何获取到
internal/bootstrap/loaders.js
和internal/bootstrap/node.js
文件内容。我们注意到上述两个文件内容的形式如下面所示:
那么是如何获取到其中的函数的呢?
原来在GetBootstrapper中先去执行一下文件,得到了其中的函数。下面去执行的时候,就可以直接执行函数了。
internal/bootstrap/loaders.js
internal/bootstrap/loaders.js
主要用于native模块的loader。函数输入是process、getBinding、 getLinkedBinding、getInternalBinding;输出是NativeModule构造函数和internalBinding方法。下面我们来一步步看下代码:
1.初始化process.binding、internalBinding
process.binding和process._linkedBinding其实调用src/node.cc中的GetBinding和GetLinkedBinding方法。internalBinding调用的是GetInternalBinding方法。
2.引入node_contextify模块
contextify是node中相当重要的一个模块,主要的作用是用来执行js的代码。
ContextifyScript这个js类中,主要挂载RunInContext和RunInThisContext两个方法,后面会做详细介绍。挂载的方法用到了V8中的env->SetProtoMethod来将C++方法挂载到js类的原型上,挂载代码如下所示:
3.定义NativeModule构造函数
NativeModule中主要有id、filename、exports对象等,这也是native模块的数据结构。
NativeModule._source存储的是所有native模块的map,key是模块名称,value是模块的ascII表示。
4.NativeModule.require
顾名思义,NativeModule.require就是用来引用Native模块的。输入是模块id,输出是模块的exports属性。代码如下:
这里做了下面几件事:
编译执行(nativeModule.compile)
上述步骤最为关键的是nativeModule.compile(),下面我们来介绍一下。
这里做了如下几件事:
这里重点创建一下ContextifyScript,因为我们熟知的大名鼎鼎的vm最终也是基于此来实现的。
创建ContextifyScript实例script也就是
new ContextifyScript(source, this.filename)
,实际会调用ContextifyScript类的静态方法new,其实就是实例化了class ContextifyScript
。script.runInThisContext调用的是
class ContextifyScript
的静态方法RunInThisContext,代码如下:上述代码主要检查了参数,调用了静态方法EvalMachine,执行的js代码的关键也在于EvalMachine,我们下面来看一下。
这里主要做了两件事:
watchdog用来监控执行超时,SigintWatchdog用来监听信号。我们下面来看一下watchdog如何实现?
node中的watchdog是利用创建一个新的线程来实现的。这里有一个需要铺垫的点是uv_run的第二个参数代表事件循环模式,UV_RUN_DEFAULT是默认的循环模式,将会不断重复这个循环,直到"循环引用计数器(ref)"减为0。
超时的流程大致如下,在新线程里面,首先执行uv_run把event loop跑起来,当timer到时后,执行
uv_stop
将事件循环终止,接着uv_close执行,关闭了timer handler,这时循环的ref还剩async一个,接着watchdog被析构,给给主线程发送信号,主线程接收到信号后w->isolate()->TerminateExecution(),最后清理了event loop。internal/bootstrap/node.js
internal/bootstrap/node.js
在internal/bootstrap/loader.js
的基础上,做了一系列初始化操作,最终利用CJS模块查找、执行用户的代码。下面将从逻辑流程上展开说明,后面也会重点介绍CJSModule(模块加载也在此)。流程
1.初始化
这里主要做的初始化有如下几点:
这里说明一下setupV8方法,因为它调用了咱们前面提到的v8 builtin模块。代码如下:
其中v8.previewMapIterator就用到了v8 builtin模块:
2.执行
执行阶段的代码如下:
主要做了如下几件事:
CJSModule
1.模块查找
模块查找主要依赖CJSModule._resolveFilename方法,输入为想要引入的模块,输出为模块的真实路径。其代码如下所示:
上述代码首先利用Module._resolveLookupPaths罗列出所有要查找的路径,在利用Module._findPath在其中查找对应模块。
Module._resolveLookupPaths
Module._resolveLookupPaths代码如下所示:
上面注释已经写的比较详细了,其中有几点要注意:
modulePaths根据环境变量HOME和NODE_PATH得到的路径,比如我本地得到的路径是:
parent.paths是在resolveFilename调用该函数时传递下来的参数,表示从现有目录到根目录下的所有node_modules目录,获取的代码如下:
Module._nodeModulePaths获取从起始目录遍历,每一层都加上node_modules。
Module._findPath
Module._findPath方法实在上面列出的查找目录中找到对应的模块,代码如下:
这里将遍历所有目录,在相应目录中再查找对应模块;在每个查找目录中,查找模块也会有一定的优先级:
2.执行
执行的过程时调用CJSModule.runMain(),在其中调用Module._load(),Module._load代码如下:
过程跟NativeModule中的_compile类似:
执行的过程最终调用了CJSModule._compile方法,而CJSModule._compile最终调用的是vm.runInThisContext。
vm.runInThisContext其实就是调用了本文上面描述的ContextifyScript的runInThisContext,简要代码如下:
总结
本文主要从lib/internal中的loader.js和node.js入手,讲述了具体执行js代码的过程,其中还加入了相关的模块查找、vm、contextify等方面的东西。
The text was updated successfully, but these errors were encountered: