AST 笔记

Sherry 发布于 2025-06-22 86 次阅读 文章 最后更新于 2025-06-22 884 字


babel

npm install @babel/parse
npm install @babel/core
npm install @babel/generator
npm install @babel/traverse
npm install @babel/types

工具基础使用方法

  • generator: 将 AST 转化回 js 代码
  • parse: 将 js 代码转化为 AST
  • traverse: 节点遍历操作的方便功能
  • types: 提供一个与格式相关的对象合集,可以操作从而生成节点
// 导入解析工具  
const {parse} = require("@babel/parser")  
// 导入生成工具  
const generator = require("@babel/generator").default  

// 对js代码进行解析  
var ast_code = parse('var a; a = "This is a test"')  
// 尝试修改节点  
ast_code.program.body[0].declarations[0].id.name ='b';  

// 转换ast_code为js代码  
js_code = generator(ast_code).code;  
console.log(js_code);

parse 用法

const {parse} = require("@babel/parser");  
var js_code = `  
var a =1;  
var b 10;  
`  
let ast_code = parse(js_code,{  
    sourceType : "module", //不加这句话的时候,如果解析的AsT里面包含import等一些写法的话,会报错  
})  
// console.log(ast_code);

generator 用法

const generator = require("@babel/generator").default;  
const js_code_out = generator(ast_code,{  
    retainLines : true, // 是否保留行号  
    // comment:false,   // 是否保留注释  
    // compact:false,   // 代码压缩正常压,保留完整分号  
    // minified:true,   // 代码压缩高压 删掉全部无用,硬压缩。混淆常用参数  
    // concise:false,   // 代码压缩低压一行,基本空格等基本会保留  
    // jsescoption:{}   // 日后再议  
}).code  
console.log(js_code_out)

traverse 用法

遍历都是深度优先

// traverse是遍历操作,babel为我们提供了很多的API去处理类似的问题。使得我们不需要手写遍历即可目的  
const traverse = require("@babel/traverse").default;  
const visitor = {  
    // 遍历节点  
    ExpressionStatement(path){  
        console.log(path.node)  
    }  
}  
traverse(ast_code,visitor)

高级(ES6)遍历写法

const visitor = {  
    // 遍历节点  两种都会遍历
    "ExpressionStatement|AssignmentExpression"(path){  
        console.log(path.node)  
    }  
}  

停止遍历

const visitor = {  
    // 遍历节点  两种都会遍历
    "ExpressionStatement|AssignmentExpression"(path){  
        enter(path){
            console.log(path.toString())
            path.stop()  
        }   
    }  
}  

types 用法

  • is 开头判断
  • assert 断言

生成节点

types.valueToNode("123456")

path 用法

节点替换

  • replacewith 替换的是节点 1 对 1
  • replacewithMultiple 替换的是节点 1 对多
  • replaceInline 自动识别,包含了以上两种方法。~所以直接用这个吧
  • replacewithsourcestring 节点替换成源码的字符串【也蛮常用。相当于强制替换一波】

停止遍历

  • path.stop()

删除节点

  • path.remove()

插入节点

  • path.insertBefore()
  • path.insertAfter()

获取对象

  • path.parent 【父节点的 path 对象】

  • path.parentPath 【父节点的 nodepath 对象】

向上遍历父节点,直到满足回调函数

  • path.findParent(function(result){return result.isVariableDeclaration()})

向上遍历父节点,包含当前节点

  • path.find

向上找函数

  • path.getFunctionParent()

向上遍历语法树,找到父节点为止(包含当前节点)

  • path.getStatementParent()

返回所在容器节点

  • path. container

返回节点所在容器索引

  • path.key 【对象返回键,数组返回索引】
  • path.listkey

判断容器是否为数组

  • path.inList

获取同级节点的第几个 key 的 path

  • path.getsibling(index)

获取下一个同级节点

  • path.getsibling(path. key+1)

获取当前作用域的引用值

  • path. evaluate()

模拟解混淆

// 导入解析工具  
const {parse} = require("@babel/parser")  
// 导入生成工具  
const generator = require("@babel/generator").default  
const traverse = require("@babel/traverse").default  

js_code = `function hi(){console['\x6c\x6f\x67']('\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21');}hi();`  
var ast_code = parse(js_code)  
// console.log(ast_code)  

const visitor = {  
    // 遍历节点  
    StringLiteral(path){  
        path.node.extra.raw = '\'' + path.node.extra.rawValue + '\''  
        // console.log(path.node.extra)  
    }  
}  

traverse(ast_code,visitor)  
jscode = generator(ast_code, {}).code  
console.log(jscode)