Node.js 源码分析 - 加载 js 文件
发表于 2018-12-01
作者: 灼灼团队
本文字数: 3392
阅读时长 ≈ 11.3 分钟

# 提出问题

了解 js 文件加载前的准备工作

在《{% post_link nodejs/nodejs-src/the-main 从 main 函数开始 %}》这篇中说到了 LoadEnvironment() 函数负责加载 js 代码,但并没有继续说明加载细节。

这篇从 LoadEnvironment() 开始探究 js 代码加载的详细过程。

# LoadEnvironment()

LoadEnvironment() 的逻辑分两部分:

  1. 加载并执行两个 js 文件:loaders.js node.js,执行后得到两个启动函数;
  2. 分别调用这两个启动函数:loaders_bootstrapper() 和 node_bootstrapper();

这段代码比较长,我们把不影响主逻辑的代码省略掉,然后直接在代码中以注释的形式来解释:

void LoadEnvironment(Environment* env) {
  // ...
  
  /************************************************************/
  /**** 第一步.加载并执行两个 js 文件:`loaders.js` `node.js`****/
  /************************************************************/

  // The bootstrapper scripts are lib/internal/bootstrap/loaders.js and
  // lib/internal/bootstrap/node.js, each included as a static C string
  // defined in node_javascript.h, generated in node_javascript.cc by
  // node_js2c.

  // 这两个 js 文件在 node 构建过程中就被转换成了 C++ 代码,即以 C++ 字符串的
  // 形式存在于 C++ 代码中,根据这个文件名就可以直接获取相应的 js 代码字符串;

  // loaders.js 的文件名
  Local<String> loaders_name =
      FIXED_ONE_BYTE_STRING(env->isolate(), "internal/bootstrap/loaders.js");
  
  // 执行 loaders.js 得到函数: `loaders_bootstrapper`
  MaybeLocal<Function> loaders_bootstrapper =
      GetBootstrapper(env, LoadersBootstrapperSource(env), loaders_name);

  // node.js 文件名
  Local<String> node_name =
      FIXED_ONE_BYTE_STRING(env->isolate(), "internal/bootstrap/node.js");

  // 执行 loaders.js 得到函数: `loaders_bootstrapper`
  MaybeLocal<Function> node_bootstrapper =
      GetBootstrapper(env, NodeBootstrapperSource(env), node_name);

  // 上面代码中:LoadersBootstrapperSource() & NodeBootstrapperSource() 是
  // 在 /src/node_javascript.h 头文件中声明的,node 源码中并没有它们的具体实现,
  // 它们的实现代码是在 node 本身构建过程中生成的;
  // 至于 GetBootstrapper(),它的作用是编译&执行 js 代码,返回执行结果。

  if (loaders_bootstrapper.IsEmpty() || node_bootstrapper.IsEmpty()) {
    return;
  }

  Local<Object> global = env->context()->Global();
  // ...
  // Expose the global object as a property on itself
  // (Allows you to set stuff on `global` from anywhere in JavaScript.)
  global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global);

  /*************************************************************************/
  /* 第二步.分别调用这两个启动函数:loaders_bootstrapper、node_bootstrapper ****/
  /*************************************************************************/

  // Create binding loaders
  // 基于 GetBinding() 函数模板 创建 get_binding_fn 函数
  Local<Function> get_binding_fn =
      env->NewFunctionTemplate(GetBinding)->GetFunction(env->context())
          .ToLocalChecked();

  // 基于 GetLinkedBinding() 函数模板 创建 get_linked_binding_fn 函数
  Local<Function> get_linked_binding_fn =
      env->NewFunctionTemplate(GetLinkedBinding)->GetFunction(env->context())
          .ToLocalChecked();

  // 基于 GetInternalBinding() 函数模板 创建 get_internal_binding_fn 函数
  Local<Function> get_internal_binding_fn =
      env->NewFunctionTemplate(GetInternalBinding)->GetFunction(env->context())
          .ToLocalChecked();

  // 上面三个函数会作为 调用 loaders_bootstrapper() 时的参数。
  Local<Value> loaders_bootstrapper_args[] = {
    env->process_object(),
    get_binding_fn,
    get_linked_binding_fn,
    get_internal_binding_fn,
    Boolean::New(env->isolate(),
                 env->options()->debug_options->break_node_first_line)
  };

  // loaders_bootstrapper() 调用结果将保存在这个变量,
  // 接下来,它将被作为参数传给另一个启动函数:node_bootstrapper()
  Local<Value> bootstrapped_loaders;

  // 调用启动函数 loaders_bootstrapper()
  if (!ExecuteBootstrapper(env, loaders_bootstrapper.ToLocalChecked(),
                           arraysize(loaders_bootstrapper_args),
                           loaders_bootstrapper_args,
                           &bootstrapped_loaders)) {
    return;
  }

  // Bootstrap Node.js
  Local<Object> bootstrapper = Object::New(env->isolate());
  SetupBootstrapObject(env, bootstrapper);
  Local<Value> bootstrapped_node;
  Local<Value> node_bootstrapper_args[] = {
    env->process_object(),
    bootstrapper,
    bootstrapped_loaders
  };
  // 调用启动函数 loaders_bootstrapper()
  if (!ExecuteBootstrapper(env, node_bootstrapper.ToLocalChecked(),
                           arraysize(node_bootstrapper_args),
                           node_bootstrapper_args,
                           &bootstrapped_node)) {
    return;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

# 总结

LoadEnvironment() 主要是调用了两个 启动函数(Bootstrapper)

  • loaders_bootstrapper()
  • node_bootstrapper()

其中 loaders_bootstrapper() 主要实现了一个简单的模块加载机制名为 NativeModule,主要用于加载内部模块的,会在 node_bootstrapper() 中用到;

而在 node_bootstrapper() 则加载并执行了用户的 js 文件(也就是通常的 app.js 或 index.js)。

这两个启动函数分别定义在 /lib/internal/bootstrap/loaders.js/lib/internal/bootstrap/node.js 文件中;

接下来的两篇文,会分别对这两个文件进行详细的探究,弄清楚 js 文件加载执行的细节;

联系我们
联系电话:17681177133
联系邮箱:admin@zhuo-zhuo.com
公司地址:合肥市高新区习友路2666号 (习友路和石莲南路交叉口西北角)二期304室
官网
博客
皖ICP备20009670号-2
合肥灼灼信息技术有限公司 | Copyright © 2020-present zhuo-zhuo.com