先做这样的约定,node提供的模块,称为核心模块。用户编写的模块,称为文件模块。Node中那些由纯c/c++编写的模块称为内建模块。Node中buffer crypto evals fs os 等都是部分通过c/c++编写的。
内建模块内部的数据结构
struct node_module_struct {
intversion;
void *dso_handle;
const char *filename;
node::addon_register_func register_func;
const char *modname;
};
在内建模块在定义之后,通过NODE_MODULE将模块定义到node命名空间中去:
#define NODE_MODULE(modname, regfunc) \
extern "C" { \
NODE_MODULE_EXPORT node::node_module_struct modname ## _module = \
{ \
NODE_STANDARD_MODULE_STUFF, \
(node::addon_register_func)regfunc, \
NODE_STRINGIFY(modname) \
}; \
}
简单点来讲就是将某个模块方法放到了node_module_struct的实例中的register_func成员中。这里一般是模块初始化方法。
在node_extensions.h中将那些内建模块统一放进了node_module_list的数组中。
NODE_EXT_LIST_START
NODE_EXT_LIST_ITEM(node_buffer)
#if HAVE_OPENSSL
NODE_EXT_LIST_ITEM(node_crypto)
#endif
NODE_EXT_LIST_ITEM(node_evals)
NODE_EXT_LIST_ITEM(node_fs)
NODE_EXT_LIST_ITEM(node_http_parser)
NODE_EXT_LIST_ITEM(node_os)
NODE_EXT_LIST_ITEM(node_zlib)
// libuv rewrite
NODE_EXT_LIST_ITEM(node_timer_wrap)
NODE_EXT_LIST_ITEM(node_tcp_wrap)
NODE_EXT_LIST_ITEM(node_udp_wrap)
NODE_EXT_LIST_ITEM(node_pipe_wrap)
NODE_EXT_LIST_ITEM(node_cares_wrap)
NODE_EXT_LIST_ITEM(node_tty_wrap)
NODE_EXT_LIST_ITEM(node_process_wrap)
NODE_EXT_LIST_ITEM(node_fs_event_wrap)
NODE_EXT_LIST_ITEM(node_signal_wrap)
NODE_EXT_LIST_END
这样之后,就能够从node_module_list中取出我们想要的模块信息了。
在node启动时,会生成全局的process,并调用bind方法来加载内建模块。
static Handle<Value> Binding(constArguments& args) {
HandleScope scope;
Local<String> module = args[0]->ToString();
node::Utf8Valuemodule_v(module);
node_module_struct* modp;
if(binding_cache.IsEmpty()) {
binding_cache = Persistent<Object>::New(Object::New());
}
Local<Object> exports;
if(binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();
return scope.Close(exports);
}
//Append a string to process.moduleLoadList
char buf[1024];
snprintf(buf, 1024, "Binding %s", *module_v);
uint32_t l = module_load_list->Length();
module_load_list->Set(l, String::New(buf));
if((modp = get_builtin_module(*module_v)) != NULL) {
exports = Object::New();
// Internal bindings don't have a "module" object,
// only exports.
modp->register_func(exports, Undefined());
binding_cache->Set(module, exports);
}else if (!strcmp(*module_v, "constants")) {
exports = Object::New();
DefineConstants(exports);
binding_cache->Set(module, exports);
}else if (!strcmp(*module_v, "natives")) {
exports = Object::New();
DefineJavaScript(exports);
binding_cache->Set(module, exports);
}else {
return ThrowException(Exception::Error(String::New("No suchmodule")));
}
return scope.Close(exports);
}
创建了exports对象,然后调用get_builtin_module方法去获取内建对象,再使用register_func(就是NODE_MODULE中的第二个参数)去填充exports对象,然后缓存exports对象,并返回给调用方完成导出。这里register_func函数是比较重要的,它到底是个什么函数呢?
我们看看node_file.cc中的register_func:
NODE_MODULE(node_fs,node::InitFs)
node::InitFs函数
void InitFs(Handle<Object> target) {
HandleScope scope;
//Initialize the stats object
Local<FunctionTemplate> stat_templ = FunctionTemplate::New();
stats_constructor_template =Persistent<FunctionTemplate>::New(stat_templ);
target->Set(String::NewSymbol("Stats"),//由v8引擎完成添加方法
stats_constructor_template->GetFunction());
File::Initialize(target);
oncomplete_sym = NODE_PSYMBOL("oncomplete");
StatWatcher::Initialize(target);// 由 v8引擎完成添加方法
}
可以看到这里做的最主要的工作就是给传入的对象target添加各种各样的方法。比如:
NODE_SET_METHOD(target, "close",Close);
NODE_SET_METHOD(target, "open", Open);
NODE_SET_METHOD(target, "read",Read);
NODE_SET_METHOD(target, "fdatasync", Fdatasync);
NODE_SET_METHOD(target, "fsync", Fsync);
NODE_SET_METHOD(target, "rename", Rename);
NODE_SET_METHOD(target, "ftruncate", FTruncate);
NODE_SET_METHOD(target, "rmdir", RMDir);
NODE_SET_METHOD(target, "mkdir", MKDir);
NODE_SET_METHOD(target, "readdir", ReadDir);
NODE_SET_METHOD(target, "stat", Stat);
NODE_SET_METHOD(target, "lstat", LStat);
NODE_SET_METHOD(target, "fstat", FStat);
NODE_SET_METHOD(target, "link", Link);
NODE_SET_METHOD(target, "symlink", Symlink);
NODE_SET_METHOD(target, "readlink", ReadLink);
NODE_SET_METHOD(target, "unlink", Unlink);
NODE_SET_METHOD(target, "write", Write);
NODE_SET_METHOD(target, "chmod", Chmod);
NODE_SET_METHOD(target, "fchmod", FChmod);
//NODE_SET_METHOD(target, "lchmod", LChmod);
NODE_SET_METHOD(target, "chown", Chown);
NODE_SET_METHOD(target, "fchown", FChown);
//NODE_SET_METHOD(target, "lchown", LChown);
NODE_SET_METHOD(target, "utimes", UTimes);
NODE_SET_METHOD(target, "futimes", FUTimes);
这样完成了c++方法挂载带了exports对象上,调用者就能够轻松调用了。后续的工作应该是要借助v8完成javascript调用c++。