之前一直找不到完美的办法来替换eval里面的字符串,想了很多招,但效果不尽人意。
初次使用的是类似这样的代码:
path.replaceWith(t.Identifier(value));
我们知道,eval的实参为字符串,而上面的代码中是生成的确是 Identifier,因此没办法继续遍历eval出来后的代码,除非从新生成代码后再解析遍历,代码不够优雅,并且字符串类型本来就不应该转换成 Identifier 类型。
后来,看到了源码中的 replaceWithSourceString 方法,试用后,效果也不尽人意,部分代码如下:
const types = require("@babel/types");
const replaceEval = {
CallExpression: {
exit(path) {
let {callee, arguments} = path.node;
if (arguments.length != 1 || !types.isLiteral(arguments[0]))
return;
if (!types.isIdentifier(callee, {name: "eval"}))
{
return;
}
let funcPath = path.getFunctionParent();
let sourceCode = funcPath.toString();
let value = arguments[0].value;
sourceCode = sourceCode.replace(path.toString(), value);
path.replaceWithSourceString(sourceCode);
}
}
},
}
使用上面的插件,在某些情况下是个错的,效果还是不行。
就当我手足无措的时候,偶尔使用搜索引擎,竟然达到了完美的答案。
因为是 replaceWithSourceString 这个方面,因此搜索这个试试:
这个著名的 stackoverflow 网站,应该有大神遇到过类似的问题,点进去看看:
虽然只有一个答案,但足够让我眼前一亮,依葫芦画瓢,很快就写出了代码,但是,报错了:
throw new Error("Found multiple statements but wanted one");
看到了 multiple statements,瞬间就明白了。答案给的是单个语句的处理方法,而我处理的eval函数,包含多个语句,因此,稍微改改就可以了。
if (types.isIdentifier(callee, {name: "eval"}))
{
const evalNode = template.statements.ast(arguments[0].value);
path.replaceWithMultiple(evalNode);
}
这里 把 template.statement 改成 template.statements 即可,注意后面的replaceWith也要跟着改变为 replaceWithMultiple。
这就完美的解决了实参为字面量的eval函数还原。完整源代码,星友们可以看这里:
https://t.zsxq.com/6u3ByFq
感谢阅读,欢迎关注本人微信公众号,学习更多AST相关知识。