Node.js中JS是如何引用C++的
JavaScript 使用 internalBunding 方法调用 C++ 内容,看了一下大致的流程。
internalBinding
C++ 模块暴露给 JS
Node.js 中 C++ 模块的最后都会调用NODE_MODULE_CONTEXT_AWARE_INTERNAL
来将该模块暴露给 JavaScript,例如 src/node_contextify.cc 文件中,将node::contextify::Initialize
作为参数调用了NODE_MODULE_CONTEXT_AWARE_INTERNAL
:
1 | NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize) |
NODE_MODULE_CONTEXT_AWARE_INTERNAL
src/node_binding.h
1 |
这个宏首先创建一个 node::node_module
实例。
之后,生成一个__register_##modname
_函数,调用这个函数,会调用 node_module_register
方法。
node_module_register
src/node_binding.cc
1 | extern "C" void node_module_register(void* m) { |
将模块放在模块链中。
有两个链:modlist_internal
、modlist_linked
。分别表示内部模块和外部模块。
internalBinding
lib/internal/bootstrap/loaders.js
JavaScript 中使用 internalBinding
来引用 C++ 模块。
1 | // Set up internalBinding() in the closure. |
getInternalBinding
方法是 C++ 定义的,同 process
等作为参数传给 internal/bootstrap/loaders
执行。
1 | // Bootstrap internal loaders |
internal/bootstrap/loaders
返回的是这样一个对象:
1 | const loaderExports = { |
其中就包含定义好的 internalBinding
。之后取出 internal_binding_loader
,调用set_internal_binding_loader
。
1 | Local<Object> loader_exports_obj = loader_exports.As<Object>(); |
set_internal_binding_loader
的作用是将 internal_binding_loader
设置在 Environment
上。
set_internal_binding_loader
是由一个宏来定义的:
1 |
|
其中 ENVIRONMENT_STRONG_PERSISTENT_VALUES
里调用了 V(internal_binding_loader, v8::Function)
:
1 |
GetInternalBinding
src/node_binding.cc
GetInternalBinding
中,通过 get_internal_module
方法来获取到内部模块。module_v
是 utf8 形式的字符串。
1 | node_module* mod = get_internal_module(*module_v); |
get_internal_module
就是通过遍历链表来寻找模块。
之后调用 InitModule
方法执行模块:
1 | exports = InitModule(env, mod, module); |
InitModule
方法会调用模块的 nm_context_register_func
方法,这个 nm_context_register_func
方法就是 C++ 模块注册时传入的初始化方法。
1 | mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv); |
Initialize 函数的参数
所以再回到最初的 C++ 注册方法,可以看到最终 node::contextify::Initialize
被调用时,传入的参数就是 exports
, unused
, env->context()
, mod->nm_priv
。
1 | NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify, node::contextify::Initialize) |