I. Source Code Composition
1. It has 8000 lines of C ++ code and 2000 lines of javascript code.
2. the built-in javascript in node. js includes the main program src/node. js and the Module Program lib/*. js.
3. main libraries on which node. js depends: v8, uv, and zlib
Ii. js2c. py Tool
Node. js uses the js2c. py tool attached with V8 to convert all built-in js Code into arrays in C ++,
Generate node_natives.h and directly include it into the program. It becomes part of the C ++ source code,
This improves the compilation efficiency of the built-in js module.
Use js2c. py to generate a source code array for each js file, which is stored in build/src/node_natives.h after conversion,
Node_natives.h is generated only after node. js is compiled.
The code structure is as follows:
Struct _ native {
Const char * name;
Const char * source;
Size_t source_len;
};
Static const struct _ native natives [] = {
{"Node", node_native, sizeof (node_native)-1 },
{"_ Debugger", _ debugger_native, sizeof (_ debugger_native)-1 },
{"_ Linklist", _ linklist_native, sizeof (_ linklist_native)-1 },
{"Assert", assert_native, sizeof (assert_native)-1 },
{"Buffer", buffer_native, sizeof (buffer_native)-1}
}
Iii. Start and Load
1. Entry: node_main.cc. After parsing the command line parameters, call node: Start (argc, argv) to Start
2. Initialize the v8 interface, V8: Initialize ()
3. Create and set the process Object in v8: Handle <Object> SetupProcessObject (int argc, char * argv []) {}
4. Load node. js and void Load (Handle <Object> process_l)
Node. js description:
This file is the core of node startup. It is called by node: Load in src/node. cc. Considering the performance of the startup process,
All dependencies adopt delayed Loading
Use MainSource () to obtain the converted source code of src/node. js and execute it: the source code has been included in node_natives.h through conversion,
Node_native is a string containing the source code.
Local <Value> f_value = ExecuteString (MainSource (),
IMMUTABLE_STRING ("node. js "));
Node. js code:
(Function (process ){
.........
});
It can be seen that after node. js is executed, a function is obtained.
Function processing in node. cc:
Assert (f_value-> IsFunction ());
Local <Function> f = Local <Function>: Cast (f_value );
Create a function execution environment, call the function, and pass the process
Local <Object> global = v8: Context: GetCurrent ()-> Global ();
Local <Value> args [1] = {Local <Value >:: New (process_l )};
F-> Call (global, 1, args );
Run:
Uv_run (uv_default_loop (), UV_RUN_DEFAULT );
For the code of the startup entry function, refer:
Int Start (int argc, char * argv []) {
// Hack aroung with the argv pointer. Used for process. title = "blah ".
Argv = uv_setup_args (argc, argv );
// Logic to duplicate argv as Init () modifies arguments
// That are passed into it.
Char ** argv_copy = copy_argv (argc, argv );
// This needs to run * before * V8: Initialize ()
// Use copy here as to not modify the original argv:
Init (argc, argv_copy );
V8: Initialize ();
{
Locker locker;
HandleScope handle_scope;
// Create the one and only Context.
Persistent <Context> context = Context: New ();
Context: Scope context_scope (context );
// Use original argv, as we're just copying values out of it.
Handle <Object> process_l = SetupProcessObject (argc, argv );
V8_typed_array: AttachBindings (context-> Global ());
// Create all the objects, load modules, do everything.
// So your next reading stop shoshould be node: Load ()!
Load (process_l );
// All our arguments are loaded. We 've evaluated all of the scripts. We
// Might even have created TCP servers. Now we enter the main eventloop. If
// There are no watchers on the loop (Response t for the ones that were
// Uv_unref 'd) then this function exits. As long as there are active
// Watchers, it blocks.
Uv_run (uv_default_loop (), UV_RUN_DEFAULT );
EmitExit (process_l );
RunAtExit ();
# Ifndef NDEBUG
Context. Dispose ();
# Endif
}
# Ifndef NDEBUG
// Clean up. Not strictly necessary.
V8: Dispose ();
# Endif // NDEBUG
// Clean up the copy:
Free (argv_copy );
Return 0;
}
Iv. Loading of built-in c ++ modules
The node. js module is written in the js language in lib/*. js, and some system modules are written in C ++,
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 search for these modules from a hash table.
Static Handle <Value> Binding (const Arguments & args ){
HandleScope scope;
Local <String> module = args [0]-> ToString ();
String: Utf8Value module_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 such module ")));
}
Return scope. Close (exports );
}
From the source code, we can see that when loading the c ++ module, we first find it from the cache. If we cannot find it, we can find the C ++ built-in module in get_builtin_module,
If it is found, bind it to exports and return exports at the end.
V. Loading c ++ extension modules
For modules with the. node extension, use Dlopen to load
Handle <Value> DLOpen (const v8: Arguments & args ){
HandleScope scope;
Char symbol [1024], * base, * pos;
Uv_lib_t lib;
Int r;
If (args. Length () <2 ){
Local <Value> exception = Exception: Error (
String: New ("process. dlopen takes exactly 2 arguments ."));
Return ThrowException (exception );
}
Local <Object> module = args [0]-> ToObject (); // Cast
String: Utf8Value filename (args [1]); // Cast
If (exports_symbol.IsEmpty ()){
Exports_symbol = NODE_PSYMBOL ("exports ");
}
Local <Object> exports = module-> Get (exports_symbol)-> ToObject ();
If (uv_dlopen (* filename, & lib )){
Local <String> errmsg = String: New (uv_dlerror (& lib ));
# Ifdef _ WIN32
// Windows needs to add the filename into the error message
Errmsg = String: Concat (errmsg, args [1]-> ToString ());
# Endif
Return ThrowException (Exception: Error (errmsg ));
}
String: Utf8Value path (args [1]);
Base = * path;
/* Find the shared library filename within the full path .*/
# Ifdef _ POSIX __
Pos = strrchr (base ,'/');
If (pos! = NULL ){
Base = pos + 1;
}
# Else // Windows
For (;;){
Pos = strpbrk (base ,"\\/:");
If (pos = NULL ){
Break;
}
Base = pos + 1;
}
# Endif
/* Strip the. node extension .*/
Pos = strrchr (base ,'.');
If (pos! = NULL ){
* Pos = '\ 0 ';
}
/* Add the '_ module' suffix to the extension name .*/
R = snprintf (symbol, sizeof symbol, "% s_module", base );
If (r <= 0 | static_cast <size_t> (r)> = sizeof symbol ){
Local <Value> exception =
Exception: Error (String: New ("Out of memory ."));
Return ThrowException (exception );
}
/* Replace dashes with underscores. When loading foo-bar.node,
* Look for foo_bar_module, not foo-bar_module.
*/
For (pos = symbol; * pos! = '\ 0'; ++ pos ){
If (* pos = '-') * pos = '_';
}
Node_module_struct * mod;
If (uv_dlsym (& lib, symbol, reinterpret_cast <void **> (& mod ))){
Char errmsg [1024];
Snprintf (errmsg, sizeof (errmsg), "Symbol % s not found.", symbol );
Return ThrowError (errmsg );
}
If (mod-> version! = NODE_MODULE_VERSION ){
Char errmsg [1024];
Snprintf (errmsg,
Sizeof (errmsg ),
"Module version mismatch. Expected % d, got % d .",
NODE_MODULE_VERSION, mod-> version );
Return ThrowError (errmsg );
}
//
Mod-> register_func (exports, module );
// Tell coverity that 'handle' shoshould not be freed when we return.
// Coverity [leaked_storage]
Return Undefined ();
}
After opening the dynamic link library, Dlopen calls the uv_dlsym of libuv and finds the address of the method defined through NODE_MODULE in the dynamic link library. Mount to the exports object.
Vi. js module Loading
Src/node. js implements an NativeModule object for managing js modules. It calls process. binding ("natives") to place all built-in js modules on 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 Script = process. binding ('evals '). NodeScript;
Var runInThisContext = Script. runInThisContext;
Var fn = runInThisContext (source, this. filename, true );
Fn (this. exports, NativeModule. require, this, this. filename );