Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node源码粗读(5):通过调试./lib库的js代码来看javascript在node中运行环境的变化 #14

Open
xtx1130 opened this issue Jan 5, 2018 · 5 comments

Comments

@xtx1130
Copy link
Owner

xtx1130 commented Jan 5, 2018

前言

相信对node源码感兴趣的同学一定知道,./lib库中的js通过node.gyp自动转换成了node_javascript.cc中的ASCII码,如果读过我之前写的一篇文章,应该也是对此有点了解的,而./lib库中的bootstrap_node.js又是js的入口文件,所有./lib下的文件几乎都需要它来挂载。那么这部分代码又该如何调试修改呢?

./lib中的js如何编译

由于./lib库中的js都是存储在node_javascript.cc中,所以我们如果想要调试./lib中的js需要多一个编译的步骤,也就是把你修改的代码编译到node_javascript.cc中。如果大家读过node.gyp以及makefile,那么整个编译流程就会很清晰,在这篇文章中不做过多解读,如果有兴趣了解的话请移步这里。我们在修改完代码后需要执行一下make来进行编译。make会根据lib库中的js重新生成node_javascript.cc文件。这样就可以保证./lib库中的代码在调用的时候都是最新的了。

调试

相信大家都了解,node的入口文件是bootstrap_node.js,那么我们就从这个文件入手,聊一聊该如何调试。二话不说,先写一个console.log然后make一下看看更新了么。

// ./lib/internal/bootstrap_node.js
'use strict';

(function(process) {
  console.log(1111)
  let internalBinding;
  const exceptionHandlerState = { captureFn: null };
//...此处省略n多代码

然后我们make一下(手动滑稽.jpg):
issue14-1
nice,没有报错,release和debug都完美生成了。接下来,我们开始跑了:
issue14-2
What?!代码没更新?console没出来?还没报错?这是什么情况?(黑人问号脸.jpg)
吓得我赶紧去翻一下node_javascript.cc,然后看到了:
issue14-3
上面的红框表示是文件internal/bootstrap_node.js,下面的红框如果大家有兴趣可以查一下ASCII,表示的是console.log(1111)。这里可以说明,编译是绝对没问题的,那么问题出在哪里呢?

js node运行环境的变化

顺着bootstrap_node.js往下看会看到一段代码:

    const browserGlobals = !process._noBrowserGlobals;
    if (browserGlobals) {
      setupGlobalTimeouts();
      setupGlobalConsole();
    }

之后的调用栈就不深扒了,通过命名可以看出来,这是装载全局console的代码。哦,原来是在这里装载的,那我在这之后打个console试试?
issue14-4
居然打出来了?!那么另外一个问题就来了,为什么之前那个console没打出来亦没有报错呢?
然后我们可以做一个小测试,分别在这两个地方设置断点,然后在前面的断点记录下来console.log,在后面的断点对比两者是否相等,代码如下

(function(process) {
  let internalBinding;
  const exceptionHandlerState = { captureFn: null };
  let testConsole = console.log;//添加的testConsole记录下来此时的console.log
  debugger;//添加断点
//....此处省略n多代码
if (browserGlobals) {
      setupGlobalTimeouts();
      setupGlobalConsole();
      console.log(testConsole === console.log);//添加console验证一下
    }
debugger;//添加断点
//...此处省略n多代码

接下来我们跑一遍验证一下,在跑之前记得修改一下调用参数哦:
issue14-5
issue14-6
这个地方应该都没有疑问吧,肯定是true,接下来我们看下面的debugger:
issue14-7
通过这个结果,能清晰的看出来node已然把console重做了。那么为什么要重做呢?
于是乎,在这里引出来一个概念:v8是js的翻译(编译)器,而node在c++中会为js全局添加global而且会把process作为bootstrap_node.js的参数传入;而在bootstrap_node.js中,则会添加和重写一部分方法,使之符合node的相关需求
我们还是用console这个例子说明一下。在node中,console的输出是针对于ttys的,所以node在处理console的时候,其实是引入了process.stdout.write的概念来实现的console,具体代码可以参考这里,对于这部分代码不在本次主要的说明范围内,所以不做过多解释,有兴趣的可以结合上下文看一下。这就是一个非常典型的node runtime interface

node runtime interface

如果大家看过./lib下面的js文件,那么会发现一个非常有意思的文件: ./lib/repl.js,这个文件可以让你的代码获得runtime interface。

总结

  • 通过上面的流程大家应该也看出来了,./lib中的js使用debugger来调试较为方便。
  • 如果大家想知道bootstrap_node.js调用栈的先后顺序,那么在调用到你的代码之后,直接在debugger中打bt,那么一切都清晰可见,😆:
    issue14-8

by 小菜

@xtx1130 xtx1130 changed the title node源码粗读(5):通过调试./lib库的js代码来看js从v8到node运行环境的改变 node源码粗读(5):通过调试./lib库的js代码来看javascript node运行环境的改变 Jan 5, 2018
@xtx1130 xtx1130 changed the title node源码粗读(5):通过调试./lib库的js代码来看javascript node运行环境的改变 node源码粗读(5):通过调试./lib库的js代码来看javascript在node中运行环境的改变 Jan 5, 2018
@xtx1130 xtx1130 changed the title node源码粗读(5):通过调试./lib库的js代码来看javascript在node中运行环境的改变 node源码粗读(5):通过调试./lib库的js代码来看javascript在node中运行环境的变化 Jan 5, 2018
@AsceticBoy
Copy link

再次编译的时候看起来需要把/out/*文件删掉,不然好像会一起带进去,但是删掉重跑,等待时间太长了

@xtx1130
Copy link
Owner Author

xtx1130 commented May 14, 2019

@AsceticBoy 不需要删文件,node的编译是增量的

@AsceticBoy
Copy link

image
再次make的时候报的错,看起来是编译了不该被编译的地方,大佬之前有遇到过吗

@xtx1130
Copy link
Owner Author

xtx1130 commented May 14, 2019

@AsceticBoy 你仔细看下报错,loop多了个o

@AsceticBoy
Copy link

@xtx1130 谢谢🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants