之前碰到有混淆的代码,都是依靠别人写好的插件,能不能还原听天由命
如果要冲高级工程师的话,AST是必备的一项技能,刚好现在有空,就花时间来研究一番……
参考资料:
环境准备:
- AST 在线解析网站:https://astexplorer.net/
纯白色实在太过亮眼了,官方没有提供模式切换,不过这里有DIY方案,效果如下:
- 安装babel功能包:
npm install @babel/core @babel/parser @babel/traverse @babel/generator @babel/types
解析代码 -> 语法树(parser)
1 2 3 4 5 6 7 8 9 10
| const fs = require("fs"); const code1 = fs.readFileSync('code.js', 'utf-8');
const parser = require("@babel/parser"); const code2 = "const name = 'cxs';";
const ast = parser.parse(code2, {sourceType: "module"}) console.log(ast)
|
语法树 -> 生成代码(generator)
1 2 3 4 5 6 7 8 9 10 11 12 13
| const parser = require("@babel/parser"); const generator = require("@babel/generator").default;
const code2 = "const name = 'cxs';"; const ast = parser.parse(code2, {sourceType: "module"})
ast.program.body[0].declarations[0].id.name = "age"; ast.program.body[0].declarations[0].init.value = 25; const result = generator(ast) console.log(result.code);
>>> const age = 25;
|
遍历节点(traverse)
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
| const parser = require("@babel/parser"); const generate = require("@babel/generator").default const traverse = require("@babel/traverse").default
const code = ` const name = ""; const age = 0; console.log(xxx); ` const ast = parser.parse(code)
const visitor = { NumericLiteral(path){ path.node.value = 25 }, StringLiteral(path){ path.node.value = "cxs" }, CallExpression(path){ path.remove() } }
traverse(ast, visitor) const result = generate(ast) console.log(result.code)
` 输出: const name = "cxs"; const age = 25; `
|
构造新节点(types)
对照 AST网站 和 函数的使用说明,倒序的方式构造节点
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
| const parser = require("@babel/parser"); const generate = require("@babel/generator").default; const traverse = require("@babel/traverse").default; const types = require("@babel/types");
const code = ` const name = "cxs"; ` const ast = parser.parse(code)
const visitor = { VariableDeclaration(path){ let id = types.identifier("age"); let init = types.numericLiteral(25); let declarator = types.variableDeclarator(id, init); let declaration = types.variableDeclaration("const", [declarator]); path.insertAfter(declaration); path.stop(); } }
traverse(ast, visitor) const result = generate(ast) console.log(result.code)
` const name = "cxs"; const age = 25; `
|
进入和退出节点(enter / exit)