AST 技巧:使用 AST 自动打印函数执行路径

大家好,今天分享一个使用 AST 自动打印函数执行路径的方法。

函数执行日志

在扣代码的时候,我遇到过很多次因为环境或者其他问题导致 JS 代码在 nodejs 下面的执行路径和浏览器中不一致,导致最终的计算结果是错误的。

正常来说,想要获取函数的执行堆栈,要么是使用报错法,让函数在某个位置报错并且从错误日志中获取堆栈信息,这种方式不适合调试扣出来的代码,因为我们的目的是让它能正常运行,而不是报错。要么就是在每个函数调用时,添加日志输出当前被调用的函数叫什么名字,这么做当然没问题,问题就是函数调用太多了,一个一个的手动新增肯定不现实,所以使用 AST 的方法来新增就很有必要了。

AST 输出函数名

为了让每个函数能输出函数名,需要遍历所有的函数,并且在函数体的开头添加一个打印当前函数名字的日志,打开 AST 网站,开始分析。

image-20250417212812194

它的函数体只有一条,并且函数体在 FunctionDeclaration 节点的 body 属性的 BlockStatement 节点下面,是一个数组类型。和上篇文章的思路一样,只需要在函数体的 body 数组中添加一条日志语句即可,别忘了先获取一下当前函数的名字,名字在 id 属性中。

image-20250417213440446

函数的名字是一个 Identifier 类型,只需要取到它的 name 属性即可。

开始编码

1
2
3
4
5
6
7
8
9
10
traverse(ast, {
FunctionDeclaration: {
exit(path) {
let {node} = path;
let name=node.id.name;
let t=template('console.log(A);')
node.body.body.unshift(t({'A':name}));
}
}
})

根据思路写出以上代码,运行一下试试:

image-20250417214413735

发现好像不太对,输出的是函数本身的引用,也就是说默认会调用函数的 toString 方法,这不是我们想要的效果。

这是为什么呢?因为在输出的时候,直接将函数名输出了,正常来说应该输出函数名字符串才对,来改一下代码:

1
2
3
4
5
6
7
8
9
10
traverse(ast, {
FunctionDeclaration: {
exit(path) {
let {node} = path;
let name=node.id.name;
let t=template('console.log("A");')
node.body.body.unshift(t({'A':name}));
}
}
})

运行一下看看效果:

image-20250417214622134

现在正常了,输出的都是正常的函数名字符串了。

运行一下代码看看添加日志后的效果:

image-20250417214729218

除了我打印的一些输出外,每次函数被调用的时候都会打印一次函数名。

那如何对比本地执行的函数和浏览器中执行的效果是否一致呢?将同样的代码在浏览器和本地都运行一遍,然后对比打印的日志即可。

总结

以上就是使用 AST 的方式自动给函数调用添加日志输出的方法了,目前的方案只打印了函数名,只能对比函数的调用次数和顺序是不是对的,其实还可以深入一些,打印一下函数的参数和返回值,可以更加精细的对比函数的调用过程,这个就留给大家自己实现了。

本文章首发于个人博客 LLLibra146’s blog

本文作者:LLLibra146

更多文章请关注公众号 (LLLibra146):LLLibra146

版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!

本文链接
https://blog.d77.xyz/archives/e90a8869.html