Nodejs中JS是如何引用C的
JavaScript使用internalBunding方法调用C++内容,看了一下大致的流程。
直接看这个文章肯定看不懂,阅读Node.js代码时看下这个会很有帮助。
全是代码,建议在较大屏幕上阅读。
C++模块暴露给JSNode.js中C++模块的最后都会调用NODE_MODULE_CONTEXT_AWARE_INTERNAL来将该模块暴露给JavaScript,例如src/node_contextify.cc文件中,将node::contextify::Initialize作为参数调用了NODE_MODULE_CONTEXT_AWARE_INTERNAL:
NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify,node::contextify::Initialize)NODE_MODULE_CONTEXT_AWARE_INTERNAL
src/node_binding.h
#defineNODE_MODULE_CONTEXT_AWARE_CPP(modname,regfunc,priv,flags)\staticnode::node_module_module={\NODE_MODULE_VERSION,\flags,\nullptr,\__FILE__,\nullptr,\(node::addon_context_register_func)(regfunc),\NODE_STRINGIFY(modname),\priv,\nullptr};\void_register_##modname(){node_module_register(_module);}
这个宏首先创建一个node::node_module实例。
之后,生成一个*_register_##modname*函数,调用这个函数,会调用node_module_register方法。
node_module_registersrc/node_binding.cc
extern"C"voidnode_module_register(void*m){structnode_module*mp=reinterpret_caststructnode_module*(m);if(mp-nm_flagsNM_F_INTERNAL){mp-nm_link=modlist_internal;modlist_internal=mp;}elseif(!node_is_initialized){//"Linked"modulesareincludedaspartofthenodeproject.//Likebuiltinstheyareregistered*before*node::Initruns.mp-nm_flags=NM_F_LINKED;mp-nm_link=modlist_linked;modlist_linked=mp;}else{thread_local_modpending=mp;}}
将模块放在模块链中。
有两个链:modlist_internal、modlist_linked。分别表示内部模块和外部模块。
internalBindinglib/internal/bootstrap/loaders.js
JavaScript中使用internalBinding来引用C++模块。
//SetupinternalBinding()intheclosure.letinternalBinding;{constbindingObj=Object.create(null);//eslint-disable-next-lineno-global-assigninternalBinding=functioninternalBinding(module){letmod=bindingObj[module];if(typeofmod!==object){mod=bindingObj[module]=getInternalBinding(module);moduleLoadList.push(`InternalBinding{module}`);}returnmod;};}
getInternalBinding方法是C++定义的,同process等作为参数传给internal/bootstrap/loaders执行。
//BootstrapinternalloadersLocalValueloader_exports;if(!ExecuteBootstrapper(this,"internal/bootstrap/loaders",loaders_params,loaders_args).ToLocal(loader_exports)){returnMaybeLocalValue();}
internal/bootstrap/loaders返回的是这样一个对象:
constloaderExports={internalBinding,NativeModule,require:nativeModuleRequire};
其中就包含定义好的internalBinding。之后取出internal_binding_loader,调用set_internal_binding_loader。
LocalObjectloader_exports_obj=loader_exports.AsObject();LocalValueinternal_binding_loader=loader_exports_obj-Get(context(),internal_binding_string()).ToLocalChecked();CHECK(internal_binding_loader-IsFunction());set_internal_binding_loader(internal_binding_loader.AsFunction());
set_internal_binding_loader的作用是将internal_binding_loader设置在Environment上。
set_internal_binding_loader是由一个宏来定义的:
#defineV(PropertyName,TypeName)\inlinev8::LocalTypeNameEnvironment::PropertyName()const{\returnPersistentToLocal::Strong(PropertyName##_);\}\inlinevoidEnvironment::set_##PropertyName(v8::LocalTypeNamevalue){\PropertyName##_.Reset(isolate(),value);\}ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V)ENVIRONMENT_STRONG_PERSISTENT_VALUES(V)#undefV
其中ENVIRONMENT_STRONG_PERSISTENT_VALUES里调用了V(internal_binding_loader,v8::Function):
#defineENVIRONMENT_STRONG_PERSISTENT_VALUES(V)\...\V(internal_binding_loader,v8::Function)GetInternalBinding
src/node_binding.cc
GetInternalBinding中,通过get_internal_module方法来获取到内部模块。module_v是utf8形式的字符串。
node_module*mod=get_internal_module(*module_v);
get_internal_module就是通过遍历链表来寻找模块。
之后调用InitModule方法执行模块:
exports=InitModule(env,mod,module);
InitModule方法会调用模块的nm_context_register_func方法,这个nm_context_register_func方法就是C++模块注册时传入的初始化方法。
mod-nm_context_register_func(exports,unused,env-context(),mod-nm_priv);Initialize函数的参数
所以再回到最初的C++注册方法,可以看到最终node::contextify::Initialize被调用时,传入的参数就是exports,unused,env-context(),mod-nm_priv。
NODE_MODULE_CONTEXT_AWARE_INTERNAL(contextify,node::contextify::Initialize)Mayo
大吉大利,天天开心