AST 技巧:还原逗号表达式

大家好,今天分享使用 AST 去除逗号表达式的方法。

逗号表达式

1
2
3
4
5
var a = 1;
var b = 2;
var c = 3;

a += 1, b += 1, c += 1,d+=1,s(),c(),f=function(){};

逗号表达式大家应该都不陌生了,像上面这种将多个表达式写在一行的代码,在混淆的 JS 中随处可见,下面我们就依此为例来还原它。

将其复制到在线 AST 网站,将代码解析为 AST 语法树,并且观察语法树的结构。

image-20250609214854764

image-20250609215444692

观察规律,发现逗号表达式其实是个 ExpressionStatement 节点,并且它里面有一个 expression 属性,是一个 SequenceExpression 节点,SequenceExpression 节点有一个 expressions 属性,expressions 属性里面就是所有的表达式。

到这里逗号表达式的 AST 语法树结构就搞清楚了。那应该如何将其还原成正常的表达式呢?来看看正常的表达式的 AST 语法树的结构是什么样子的,先手动还原一下哈哈哈。

image-20250609215739225

对比上图和之前的逗号表达式会发现,正常的表达式都是一个 ExpressionStatement 节点,并且它的 expression 属性就是一个 AssignmentExpression 节点或者其他类型的节点。

也就是说,要想将逗号表达式还原,那就要将刚才的每个节点都构造成一个 ExpressionStatement 节点即可,ExpressionStatement 节点的 expression 就是每个表达式本身。

还原逗号表达式

有了前面的思路,代码就很简单了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const processSequenceExpression = {
ExpressionStatement: {
exit(path) {
let {node} = path;
//保存所有的节点
let nodes = [];
//别忘了判断节点类型,可能会死循环
if (!types.isSequenceExpression(node.expression)) {
return
}
//为每个节点生成一个ExpressionStatement节点
for (const expression of node.expression.expressions) {
nodes.push(types.expressionStatement(expression));
}
//替换原来的节点
path.replaceWithMultiple(nodes);
}
}
}
traverse(ast, processSequenceExpression);

首先获取到逗号表达式节点,然后遍历它的 expressions 属性,将里面的所有内容构造成一个个 ExpressionStatement 节点,最后使用刚才构造的节点数组替换原有的节点即可。

运行结果:

image-20250609221123453

逗号表达式已经被拆分开,并且不影响代码的执行逻辑。

对了,别忘了判断节点类型,因为正常的 ExpressionStatement 节点的 expression 属性不会是 SequenceExpression 类型,这里要判断一下防止死循环,只需要处理逗号表达式节点即可。

代码量不多,大家可以自己试一下,如果看不懂可以单步调试一下,有利于理解 AST 的运行逻辑。

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

本文作者:LLLibra146

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

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

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