A Rough Study of the node. js source code,It has 8000 lines of C ++ code and 2000 lines of javascript code.To see how js and C ++ are organized and connected, and how each module calls each other.
The node. js version used in this article is 0.4.8. You can see the source code in https://github.com/joyent/node/tree/v0.4.8.
Js2c. py
Node. js uses the js2c that comes with V8. the py tool converts all the built-in js Code into an array in C ++, generates node_natives.h, and directly includes it into the program, becoming part of the C ++ source code. This improves the compilation efficiency of the built-in js module.
Node. the built-in javascript in js includes the main program src/node. js and Module Program lib /*. js, through js2c. py generates a source code array for each js file, which is stored in build/src/node_natives.h and node_natives.h in node. js2c is called in the compiled wscript. py), you can see the approximate structure is as follows:
- namespace node {
- const char node_native[] = {47, 47, 32, 67, 112 ......}
- const char console_native[] = {47, 47, 32, 67, 112 ......}
- const char buffer_native[] = {47, 47, 32, 67, 112 ......}
- .....
- }
- struct _native { const char* name; const char* source; size_t source_len;};
- static const struct _native natives[] = {
- { "node", node_native, sizeof(node_native)-1 },
- { "dgram", dgram_native, sizeof(dgram_native)-1 },
- { "console", console_native, sizeof(console_native)-1 },
- { "buffer", buffer_native, sizeof(buffer_native)-1 },
- ....
- }
This file is included in node_javascript.cc. node_javascript.cc provides two interfaces:
MainSource () processes node_native source code and returns v8: Handle type data for compilation.
DefineJavaScript (target) converts the source code of all other modules into the v8: Handle type and loads it to the input target object.
All js modules are converted into C arrays. Next, let's see how they are executed and called each other.
Execute the js main program/pass the process
First, let's take a look at the underlying C ++ variable process passed to javascript by node. js. When you start running node. js, the program configures process first.
- Handleprocess = SetupProcessObject(argc, argv);
Then, process is used as a parameter to call the function returned by the js main program src/node. js, so that process is passed to javascript.
- // Node. cc
- // Obtain the converted src/node. js source code through MainSource () and execute it
- Local f_value = ExecuteString (MainSource (), IMMUTABLE_STRING ("node. js "));
-
- // After src/node. js is executed, a function is obtained, which can be seen from the node. js source code:
- // Node. js
- // (Function (process ){
- // Global = this;
- //....
- //})
- Local f = Local: Cast (f_value );
-
-
- // Create a function execution environment, call the function, and pass the process
- Localglobal = v8: Context: GetCurrent ()-> Global ();
- Local args [1] = {Local: New (process )};
- F-> Call (global, 1, args );
C ++ Module
Node. js modules are not only written in lib/*. js but also in C ++, such as OS/stdio/crypto/buffer. These modules are stored in the Variable _ module through the NODE_MODULE method provided by node. h. Node_extensions.cc provides the get_builtin_module (name) interface to obtain these modules.
Process. binding/C ++ module Loading
The interface provided by process to obtain a module is binding. Its implementation of the Binding () function can be found in node. cc.
- Persistent binding_cache;
- static Handle Binding(const Arguments& args) {
- HandleScope scope;
- Local module = args[0]->ToString();
- String::Utf8Value module_v(module);
- node_module_struct* modp;
-
- if (binding_cache.IsEmpty()) {
- binding_cache = Persistent::New(Object::New());
- }
- Local exports;
- if (binding_cache->Has(module)) {
- exports = binding_cache->Get(module)->ToObject();
-
- } else if ((modp = get_builtin_module(*module_v)) != NULL) {
- exports = Object::New();
- modp->register_func(exports);
- binding_cache->Set(module, exports);
-
- } else if (!strcmp(*module_v, "constants")) {
- exports = Object::New();
- DefineConstants(exports);
- binding_cache->Set(module, exports);
-
- #ifdef __POSIX__
- } else if (!strcmp(*module_v, "io_watcher")) {
- exports = Object::New();
- IOWatcher::Initialize(exports);
- binding_cache->Set(module, exports);
- #endif
-
- } else if (!strcmp(*module_v, "natives")) {
- exports = Object::New();
- DefineJavaScript(exports);
- binding_cache->Set(module, exports);
-
-
- } else {
- return ThrowException(Exception::Error(String::New("No such module")));
- }
- return scope.Close(exports);
- }
From the source code, we can see that process is called. when binding, check whether this module already exists in the cache. If it does not exist, call get_builtin_module to find the C ++ built-in module. If it is found, bind it to exports and return exports.
The natives module calls the DefineJavaScript (exports) interface mentioned above to obtain all the built-in js modules bound to exports.
Now, you only need to call process. binding to call the modules provided by C ++ in js. For example
- var stdio = process.binding("stdio")
Js module Loading
Src/node. js implements an NativeModule object for managing the js module. It calls process. binding ("natives") places all built-in js modules in the NativeModule. _ source, and provides the require interface for calling. In require, code is encapsulated and some variables are passed to this module.
- NativeModule.wrapper = [
- '(function (exports, require, module, __filename, __dirname) { ',
- '\n});'
- ];
Use one of the js compilation interfaces provided by process to compile process. runInThisContext to execute the code.
- var fn = runInThisContext(source, this.filename, true);
- fn(this.exports, NativeModule.require, this, this.filename);
So in the main program src/node. nativeModule can be called on js. require ("net") to load the net module, in lib /*. each js module of js can load other built-in js modules by calling the passed require.
Summary Process
A rough summary of the process of loading modules:
Load the C ++ module (take stdio as an example ):
Process. binding ("stdio")-> get_builtin_module ("stdio")-> _ module-> NODE_MODULE (node_stdio, node: Stdio: Initialize) (defined)
Loading js modules (take net as an example)
Require ("net")-> NativeModule. require ("net")-> process. binding ("natives") ["net"]-> DefineJavaScript ()-> natives []-> node_natives.h
Http://cnodejs.org/blog? P = 1280