AST反混淆练习题1

AST反混淆练习题(一)

第1题:变量名替换(入门)

混淆代码

javascript 复制代码
//混淆代码
var _0x1a2b = "Hello";
var _0x3c4d = "World";
var _0x5e6f = _0x1a2b + " " + _0x3c4d;

function _0x7g8h(_0x9i0j) {
    return _0x9i0j.toUpperCase();
}

console.log(_0x7g8h(_0x5e6f));

期望还原

javascript 复制代码
//期望还原
var greeting = "Hello";
var subject = "World";
var message = greeting + " " + subject;

function toUpperCase(text) {
    return text.toUpperCase();
}

console.log(toUpperCase(message));

提示:

核心思路:建立一个映射表,把所有 _0x 开头的变量名替换为有意义的名称。

步骤:

定义映射表 nameMap ,键是旧名,值是新名

遍历 Identifier 节点

如果 path.node.name 在映射表中,替换为新名

答案:

javascript 复制代码
// 1. 导入必要的模块
const fs = require('fs');                           // 文件系统模块(本题未使用)
const parser = require('@babel/parser');            // 将JS代码解析为AST
const traverse = require('@babel/traverse').default; // 遍历AST节点
const t = require('@babel/types');                  // AST节点类型工具
const generator = require('@babel/generator').default; // 将AST还原为代码


let jsCode = `
var _0x1a2b = "Hello";
var _0x3c4d = "World";
var _0x5e6f = _0x1a2b + " " + _0x3c4d;

function _0x7g8h(_0x9i0j) {
    return _0x9i0j.toUpperCase();
}

console.log(_0x7g8h(_0x5e6f));
`

const ast = parser.parse(jsCode,{
    sourceType: 'module'
});
let nameMap = {
    '_0x1a2b':'greeting ',
    '_0x3c4d':'subject',
    '_0x7g8h':'toUpperCase',
    '_0x9i0j':'text',
    '_0x5e6f':'message'
};// 保存变量名和变量值的映射关系

const visitor = {
    Identifier (path){
        let name = path.node.name;
        console.log(`混淆代码:${name}===>还原后:${nameMap[name]}`)
        if(nameMap[name]){ // 如果存在映射关系,则替换变量名
            path.node.name = nameMap[name]
        }
    }
}// 遍历AST节点
traverse(ast, visitor);// 执行遍历

let code = generator(ast).code;
console.log(code);

第2题:十六进制字符串解码

混淆代码

javascript 复制代码
var msg = "\x48\x65\x6c\x6c\x6f \x57\x6f\x72\x6c\x64";
console.log(msg);

期望还原

javascript 复制代码
var msg = "Hello World";
console.log(msg);

提示:

核心思路:十六进制字符串在AST中存储时, node.value 已经是解码后的值,但 node.extra.raw 还保留着原始的十六进制形式。我们需要让generator输出普通字符串。

关键知识点:

StringLiteral 节点有两个重要属性:

node.value :解码后的字符串值(如 "Hello World" )

node.extra.raw :原始字符串形式(如 "\x48\x65\x6c\x6c\x6f \x57\x6f\x72\x6c\x64" )

解决方法:

删除 node.extra 对象,这样generator就会使用 node.value 输出普通字符串

答案:

javascript 复制代码
let jsCode = `
var msg = "\x48\x65\x6c\x6c\x6f \x57\x6f\x72\x6c\x64";
console.log(msg);
`

const ast = parser.parse(jsCode,{
    sourceType: 'module'
});
const visitor = {
    StringLiteral(path) {
        // 删除 extra 对象,让generator输出普通字符串
        delete path.node.extra;
    }
};
traverse(ast, visitor);// 执行遍历

let code = generator(ast).code;
console.log(code);

第3题:字符串数组混淆(基础)

混淆代码

javascript 复制代码
var _0x1234 = ['\x48\x65\x6c\x6c\x6f', '\x57\x6f\x72\x6c\x64', '\x4c\x6f\x67\x69\x6e'];

function _0x5678(_0x9abc) {
    _0x9abc = _0x9abc - 0x0;
    return _0x1234[_0x9abc];
}

var username = _0x5678('0x0');
var action = _0x5678('0x2');
console.log(username + ' ' + action);

期望还原

javascript 复制代码
var username = "Hello";
var action = "Login";
console.log(username + ' ' + action);

提示

  1. 提取数组内容
    遍历 VariableDeclarator ,找到 _0x1234
    提取数组中的字符串到 stringArray
  2. 替换函数调用
    遍历 CallExpression ,找到 _0x5678('0x0') 这样的调用
    解析参数 '0x0' → 0 ,取 stringArray[0]
    用 t.stringLiteral() 创建新节点替换
  3. .删除无用代码
    删除数组定义 _0x1234
    删除函数定义 _0x5678

答案:

javascript 复制代码
let jsCode = `
var _0x1234 = ['\x48\x65\x6c\x6c\x6f', '\x57\x6f\x72\x6c\x64', '\x4c\x6f\x67\x69\x6e'];

function _0x5678(_0x9abc) {
    _0x9abc = _0x9abc - 0x0;
    return _0x1234[_0x9abc];
}

var username = _0x5678('0x0');
var action = _0x5678('0x2');
console.log(username + ' ' + action);

`

const ast = parser.parse(jsCode,{
    sourceType: 'module'
});

let stringArray = [];
const visitor = {
    VariableDeclarator(path) {
        if (path.node.id.name === '_0x1234') { // 找到_0x1234变量
            stringArray = path.node.init.elements.map(item => item.value);// 提取字符串数组
            path.remove();//提取完之后删除_0x1234变量
        }
    },
    CallExpression(path) {
        if(path.node.callee.name && path.node.callee.name === '_0x5678'){
            const index = parseInt(path.node.arguments[0].value)
            const stringvalue = stringArray[index]
            // console.log(`数组中第${index}个元素是${stringvalue}`)
            path.replaceWith(t.stringLiteral(stringvalue))
        }

    },
    FunctionDeclaration(path) {
        if(path.node.id.name === '_0x5678'){
            path.remove();//删除_0x5678函数声明
        }
    }
};
traverse(ast, visitor);// 执行遍历
let code = generator(ast).code;
console.log(code);

第4题:简单控制流平坦化

混淆代码

javascript 复制代码
function calculate(a, b) {
    var _0x1 = 0;
    var _0x2 = 0;
    while (true) {
        switch (_0x1) {
            case 0:
                _0x2 = a + b;
                _0x1 = 1;
                break;
            case 1:
                _0x2 = _0x2 * 2;
                _0x1 = 2;
                break;
            case 2:
                return _0x2;
        }
    }
}

期望还原

javascript 复制代码
function calculate(a, b) {
    var _0x2 = 0;
    _0x2 = a + b;
    _0x2 = _0x2 * 2;
    return _0x2;
}

提示

步骤1:找到 while(true) 结构

遍历 WhileStatement

检查 path.node.test.value === true

如果不是 while(true) ,直接 return 跳过

步骤2:获取 switch 语句

while 的循环体是 BlockStatement (代码块)

BlockStatement 的 body 是语句数组

switch 是数组的第0个元素: path.node.body.body[0]

检查它是否是 SwitchStatement 类型

步骤3:判断是否状态赋值

判断条件:

stmt.type === 'ExpressionStatement' (表达式语句)

stmt.expression.type === 'AssignmentExpression' (赋值表达式)

stmt.expression.left.name === '_0x1' (赋值给状态变量)

步骤4:提取有效代码

创建一个空数组 statements = []

遍历 switchStmt.cases (所有case)

对每个 caseNode ,遍历 caseNode.consequent (case里的语句)

对每个语句:

如果是 BreakStatement ,跳过( return )

如果是状态赋值,跳过(调用你的辅助函数判断)

其他语句,推入 statements 数组

步骤5:替换整个 while

使用 path.replaceWithMultiple(statements)

这样就把 while-switch 替换成了顺序执行的代码

预期提取过程:

case 0:

_0x2 = a + b; ✅ 保留

_0x1 = 1; ❌ 跳过(状态赋值)

break; ❌ 跳过(break)

case 1:

_0x2 = _0x2 * 2; ✅ 保留

_0x1 = 2; ❌ 跳过(状态赋值)

break; ❌ 跳过(break)

case 2:

return _0x2; ✅ 保留

最终 statements 数组包含3个语句,替换掉整个 while

答案

javascript 复制代码
let jsCode = `
function calculate(a, b) {
    var _0x1 = 0;
    var _0x2 = 0;
    while (true) {
        switch (_0x1) {
            case 0:
                _0x2 = a + b;
                _0x1 = 1;
                break;
            case 1:
                _0x2 = _0x2 * 2;
                _0x1 = 2;
                break;
            case 2:
                return _0x2;
        }
    }
}
`

const ast = parser.parse(jsCode, {
    sourceType: 'module'
});

const visitor = {
        WhileStatement(path) {
            // 检查循环条件是否为true,如果不是则直接返回
            if (path.node.test.value !== true) return;
            // 检查循环体是否为switch语句,如果不是则直接返回
            let switchStatement = path.node.body.body[0];
            if (switchStatement.type !== 'SwitchStatement') return;
            // 获取switch语句的所有case语句
            let statements = [];
            // 遍历case语句,获取所有非break语句和状态赋值语句
            switchStatement.cases.forEach(caseNode => {
                    caseNode.consequent.forEach(statement => {
                        if (statement.type === 'BreakStatement') return;
                        //跳过状态赋值语句
                        if (statement.type === 'ExpressionStatement' && statement.expression.type === 'AssignmentExpression' &&
                            statement.expression.left.name === '_0x1') return;//
                        statements.push(statement);
                        // console.log(statements)
                    })
                }
            )
            // 替换循环体为所有非break语句和状态赋值语句
             path.replaceWithMultiple(statements);
        }
    };
traverse(ast, visitor);

let code = generator(ast).code;
console.log(code);

第5题:do-while 控制流平坦化

混淆代码

javascript 复制代码
function processData(data) {
    var _0x1 = 0;
    var _0x2 = [];
    var _0x3 = data.length;
    var _0x4 = 0;
    
    do {
        switch (_0x1) {
            case 0:
                if (_0x4 < _0x3) {
                    _0x1 = 1;
                } else {
                    _0x1 = 3;
                }
                break;
            case 1:
                _0x2.push(data[_0x4]);
                _0x1 = 2;
                break;
            case 2:
                _0x4++;
                _0x1 = 0;
                break;
            case 3:
                return _0x2;
        }
    } while (true);
}

期望还原

javascript 复制代码
function processData(data) {
    var _0x2 = [];
    var _0x3 = data.length;
    var _0x4 = 0;
    
    while (_0x4 < _0x3) {
        _0x2.push(data[_0x4]);
        _0x4++;
    }
    return _0x2;
}

提示

提示:

这道题和第4题类似,但有3个关键区别:

区别1:循环类型不同

第4题是 while(true) + switch

本题是 do-while(true) + switch

访问者要改成 DoWhileStatement

区别2:有循环逻辑(不是顺序执行)

第4题的 case 是 0→1→2→结束(顺序执行)

本题的 case 是 0→1→2→0→1→2...(循环执行)

case 0 是条件判断,决定继续循环还是结束

需要识别出循环模式,保留 while ,只去掉 switch

区别3:需要提取循环条件

从 case 0 中提取条件: _0x4 < _0x3

用这个条件构建新的 while 循环

答案

javascript 复制代码
let jsCode = `
function processData(data) {
    var _0x1 = 0;
    var _0x2 = [];
    var _0x3 = data.length;
    var _0x4 = 0;
    
    do {
        switch (_0x1) {
            case 0:
                if (_0x4 < _0x3) {
                    _0x1 = 1;
                } else {
                    _0x1 = 3;
                }
                break;
            case 1:
                _0x2.push(data[_0x4]);
                _0x1 = 2;
                break;
            case 2:
                _0x4++;
                _0x1 = 0;
                break;
            case 3:
                return _0x2;
        }
    } while (true);
}
`

const ast = parser.parse(jsCode, {
    sourceType: 'module'
});

const visitor = {
    DoWhileStatement(path) {
        // 1. 检查是否是 do-while(true)
        if (path.node.test.value !== true) return;

        // 2. 获取 switch 语句
        let switchStmt = path.node.body.body[0];
        if (switchStmt.type !== 'SwitchStatement') return;

        let loopCondition = null;
        let loopBody = [];
        let returnStmt = null;

        // 3. 遍历所有 case,自动识别类型
        switchStmt.cases.forEach(caseNode => {
            // 提取有效语句(去掉 break 和状态赋值)
            let validStmts = caseNode.consequent.filter(stmt => {
                if (stmt.type === 'BreakStatement') return false;
                if (isStateAssignment(stmt)) return false;
                return true;
            });

            // 判断 case 类型
            if (isConditionCase(caseNode)) {
                // 条件判断 case:提取 if 条件
                let ifStmt = caseNode.consequent.find(s => s.type === 'IfStatement');
                loopCondition = ifStmt.test;
            }
            else if (isReturnCase(caseNode)) {
                // 返回 case
                returnStmt = validStmts[0];
            }
            else {
                // 循环体代码
                loopBody.push(...validStmts);
            }
        });

        // 4. 替换为 while + return
        if (loopCondition && returnStmt) {
            path.replaceWithMultiple([
                t.whileStatement(loopCondition, t.blockStatement(loopBody)),
                returnStmt
            ]);
        }
    },

    // 删除无用的状态变量
    VariableDeclarator(path) {
        if (path.node.id.name === '_0x1') {
            path.remove();
        }
    }
};

// 辅助函数:判断是否是状态赋值
function isStateAssignment(stmt) {
    return stmt.type === 'ExpressionStatement' &&
           stmt.expression.type === 'AssignmentExpression' &&
           stmt.expression.left.name === '_0x1';
}

// 辅助函数:判断是否是条件判断 case
function isConditionCase(caseNode) {
    return caseNode.consequent.some(s => s.type === 'IfStatement');
}

// 辅助函数:判断是否是返回 case
function isReturnCase(caseNode) {
    return caseNode.consequent.some(s => s.type === 'ReturnStatement');
}

traverse(ast, visitor);

let code = generator(ast).code;
console.log(code);

第6题:乱序 Switch 控制流平坦化

混淆代码

javascript 复制代码
function calculate(a, b, c) {
    var _0x1 = 3;
    var _0x2 = 0;
    
    while (true) {
        switch (_0x1) {
            case 0:
                _0x2 = _0x2 / 2;
                _0x1 = 5;
                break;
            case 1:
                return _0x2;
            case 2:
                _0x2 = a + b;
                _0x1 = 4;
                break;
            case 3:
                _0x2 = c * 10;
                _0x1 = 2;
                break;
            case 4:
                _0x2 = _0x2 - c;
                _0x1 = 0;
                break;
            case 5:
                _0x2 = _0x2 + 100;
                _0x1 = 1;
                break;
        }
    }
}

期望还原

javascript 复制代码
let jsCode = `
function calculate(a, b, c) {
    var _0x1 = 3;
    var _0x2 = 0;
    
    while (true) {
        switch (_0x1) {
            case 0:
                _0x2 = _0x2 / 2;
                _0x1 = 5;
                break;
            case 1:
                return _0x2;
            case 2:
                _0x2 = a + b;
                _0x1 = 4;
                break;
            case 3:
                _0x2 = c * 10;
                _0x1 = 2;
                break;
            case 4:
                _0x2 = _0x2 - c;
                _0x1 = 0;
                break;
            case 5:
                _0x2 = _0x2 + 100;
                _0x1 = 1;
                break;
        }
    }
}
`

const ast = parser.parse(jsCode, {
    sourceType: 'module'
});

let startMap = {};
let caseMap = {};
const visitor = {
    //找到变量声明,记录到字典中
    VariableDeclarator(path){
        let varname = path.node.id.name;
        let initValue = path.node.init?.value;
        if(typeof initValue === 'number'){
            startMap[varname] = initValue;
            if(varname === '_0x1'){
                path.remove();
            }
            //startMap  -->{ _0x1: 3, _0x2: 0 }
        }
    },
    //找到switch语句,找到开始的case变量
    WhileStatement(path){
        let caseMap = {};//case和变量的映射关系 - 使用对象
        let startCase = null;//开始的case变量,初始值是null  代码解析后 应该是3
        if(path.node.test.value !== true) return;//只处理while(true)的switch语句
        //找到switch语句的case变量
        let startVar = path.node.body.body[0].discriminant.name;
        //判断case变量是否在字典中
        if(startMap[startVar]){
            startCase = startMap[startVar]; // 3

        }

        // console.log('startVar:', startVar, 'startCase:', startCase);

        //下面需要处理case的执行顺序
        let switchStmt = path.node.body.body[0].cases
        //遍历case语句
        switchStmt.forEach(caseStmt =>{
            //找到_0x1的赋值,以及当前的case值
            let caseNum = caseStmt.test.value;
            let statements = [];
            let nextCase = null;
            let hasReturn = false;
            //找到case语句的执行语句
            caseStmt.consequent.forEach(stmt =>{
                if(stmt.type ==='BreakStatement')return ; //跳过break语句
                if(stmt.type ==='ExpressionStatement' &&
                   stmt.expression.type === 'AssignmentExpression' &&
                   stmt.expression.left.name === startVar ){//找到_0x1的赋值语句
                    nextCase = stmt.expression.right.value;//找到当前case的下一个case值
                    return;
                }
                if(stmt.type ==='ReturnStatement'){//找到return语句
                    hasReturn = true;//标记当前case有return语句
                    statements.push(stmt);//将return语句添加到执行语句中
                    return;
                }
                statements.push(stmt);//将其他语句添加到执行语句中

            })
            //将case值和执行语句添加到结果中
            caseMap[caseNum] = {
                statements,
                nextCase,
                hasReturn
            }
        });
        // console.log('caseMap:', caseMap);

        //按顺序集case的执行语句
        // 2. 按顺序收集代码
        let newStatements = [];//按顺序收集的执行语句
        let current = startCase;//当前的case值
        let visited = new Set();//记录已经访问过的case值

        while(current !== null && !visited.has(current)) {//如果当前的case值不为null,并且没有访问过当前的case值
            visited.add(current);//添加当前的case值到访问过的case值中
            let info = caseMap[current]; // 获取当前case的信息

            if(!info) break;//如果当前case没有信息,则跳出循环

            newStatements.push(...info.statements);//将当前case的执行语句添加到新语句中
            if(info.hasReturn) break;//如果当前case有return语句,则跳出循环

            current = info.nextCase;//将当前的case值更新为下一个case值
        }
        // console.log('newStatements:', newStatements);

        path.replaceWithMultiple(newStatements);//用新语句替换switch语句
    }
}

traverse(ast, visitor);

let code = generator(ast).code;
console.log(code);

第7题

混淆代码

javascript 复制代码
function complexCalc(a, b, c, d) {
    var _s = 0;
    var _r = 0;

    while (true) {
        switch (_s) {
            case 0:
                _r = a * b;
                _s = 2;
                break;
            case 1:
                if (_r > 100) {
                    _s = 3;
                } else {
                    _s = 5;
                }
                break;
            case 2:
                _r = _r + c;
                _s = 1;
                break;
            case 3:
                _r = _r * 2;
                _s = 4;
                break;
            case 4:
                _r = _r - d;
                _s = 6;
                break;
            case 5:
                _r = _r + 50;
                _s = 6;
                break;
            case 6:
                return _r;
        }
    }
}

期望还原

javascript 复制代码
function complexCalc(a, b, c, d) {
    var _r = 0;
    _r = a * b;
    _r = _r + c;
    if (_r > 100) {
        _r = _r * 2;
        _r = _r - d;
    } else {
        _r = _r + 50;
    }
    return _r;
}

提示

由于包含条件判断,还原后的代码会包含 if-else 结构

答案

javacript 复制代码
let jsCode = `
function complexCalc(a, b, c, d) {
    var _s = 0;
    var _r = 0;

    while (true) {
        switch (_s) {
            case 0:
                _r = a * b;
                _s = 2;
                break;
            case 1:
                if (_r > 100) {
                    _s = 3;
                } else {
                    _s = 5;
                }
                break;
            case 2:
                _r = _r + c;
                _s = 1;
                break;
            case 3:
                _r = _r * 2;
                _s = 4;
                break;
            case 4:
                _r = _r - d;
                _s = 6;
                break;
            case 5:
                _r = _r + 50;
                _s = 6;
                break;
            case 6:
                return _r;
        }
    }
}
`;

const ast = parser.parse(jsCode);

let startMap = {};
let startVar = null;

// 辅助函数:从 if-else 中提取分支的 nextCase
function extractBranches(ifStmt, stateVar) {
    let branches = {};
    
    // 处理 if 分支
    if (ifStmt.consequent.type === 'BlockStatement') {
        ifStmt.consequent.body.forEach(stmt => {
            if (stmt.type === 'ExpressionStatement' &&
                stmt.expression.type === 'AssignmentExpression' &&
                stmt.expression.left.name === stateVar) {
                branches.if = stmt.expression.right.value;
            }
        });
    }
    
    // 处理 else 分支
    if (ifStmt.alternate && ifStmt.alternate.type === 'BlockStatement') {
        ifStmt.alternate.body.forEach(stmt => {
            if (stmt.type === 'ExpressionStatement' &&
                stmt.expression.type === 'AssignmentExpression' &&
                stmt.expression.left.name === stateVar) {
                branches.else = stmt.expression.right.value;
            }
        });
    }
    
    return branches;
}

// 辅助函数:清理 if-else 中的状态赋值
function cleanIfStatement(ifStmt, stateVar) {
    // 创建新的 if 语句,去掉状态赋值
    let newIf = t.ifStatement(
        ifStmt.test,
        t.blockStatement([]),
        ifStmt.alternate ? t.blockStatement([]) : null
    );
    
    // 复制 if 分支的非状态赋值语句
    if (ifStmt.consequent.type === 'BlockStatement') {
        newIf.consequent.body = ifStmt.consequent.body.filter(stmt => {
            if (stmt.type === 'ExpressionStatement' &&
                stmt.expression.type === 'AssignmentExpression' &&
                stmt.expression.left.name === stateVar) {
                return false;
            }
            return true;
        });
    }
    
    // 复制 else 分支的非状态赋值语句
    if (ifStmt.alternate && ifStmt.alternate.type === 'BlockStatement') {
        newIf.alternate.body = ifStmt.alternate.body.filter(stmt => {
            if (stmt.type === 'ExpressionStatement' &&
                stmt.expression.type === 'AssignmentExpression' &&
                stmt.expression.left.name === stateVar) {
                return false;
            }
            return true;
        });
    }
    
    return newIf;
}

const visitor = {
    VariableDeclarator(path) {
        let varname = path.node.id.name;
        let initValue = path.node.init?.value;
        if (typeof initValue === 'number') {
            startMap[varname] = initValue;
        }
    },
    
    WhileStatement(path) {
        if (path.node.test.value !== true) return;
        
        let switchStmt = path.node.body.body[0];
        if (switchStmt.type !== 'SwitchStatement') return;
        
        startVar = switchStmt.discriminant.name;
        let entryCase = startMap[startVar];
        
        // 构建 caseMap
        let caseMap = new Map();
        
        switchStmt.cases.forEach(caseNode => {
            let caseNum = caseNode.test.value;
            let statements = [];
            let nextCase = null;
            let hasReturn = false;
            let hasBranch = false;  // 是否有条件分支
            let branches = {};      // 分支信息
            
            caseNode.consequent.forEach(stmt => {
                if (stmt.type === 'BreakStatement') return;
                
                // 简单状态赋值
                if (stmt.type === 'ExpressionStatement' &&
                    stmt.expression.type === 'AssignmentExpression' &&
                    stmt.expression.left.name === startVar) {
                    nextCase = stmt.expression.right.value;
                    return;
                }
                
                // 条件分支(if-else)
                if (stmt.type === 'IfStatement') {
                    hasBranch = true;
                    branches = extractBranches(stmt, startVar);
                    
                    // 清理 if-else 中的状态赋值,保留条件判断
                    let cleanIf = cleanIfStatement(stmt, startVar);
                    statements.push(cleanIf);
                    return;
                }
                
                if (stmt.type === 'ReturnStatement') {
                    hasReturn = true;
                }
                
                statements.push(stmt);
            });
            
            caseMap.set(caseNum, {
                statements,
                nextCase,
                hasReturn,
                hasBranch,
                branches
            });
        });
        
        // 递归展开所有路径
        function expandCase(caseNum, visited, result) {
            if (visited.has(caseNum)) return;
            visited.add(caseNum);
            
            let info = caseMap.get(caseNum);
            if (!info) return;
            
            // 添加当前 case 的语句
            result.push(...info.statements);
            
            if (info.hasReturn) return;
            
            // 如果有条件分支,递归展开两个分支
            if (info.hasBranch) {
                // 创建 if-else 结构,包含展开的分支
                let lastStmt = result[result.length - 1];
                if (lastStmt.type === 'IfStatement') {
                    // 展开 if 分支
                    let ifBody = [];
                    if (info.branches.if !== undefined) {
                        let ifVisited = new Set(visited);
                        expandCase(info.branches.if, ifVisited, ifBody);
                    }
                    lastStmt.consequent = t.blockStatement(ifBody);
                    
                    // 展开 else 分支
                    let elseBody = [];
                    if (info.branches.else !== undefined) {
                        let elseVisited = new Set(visited);
                        expandCase(info.branches.else, elseVisited, elseBody);
                    }
                    if (lastStmt.alternate) {
                        lastStmt.alternate = t.blockStatement(elseBody);
                    }
                }
            } else if (info.nextCase !== null) {
                expandCase(info.nextCase, visited, result);
            }
        }
        
        // 从入口开始展开
        let newStatements = [];
        expandCase(entryCase, new Set(), newStatements);
        
        // 替换 while
        path.replaceWithMultiple(newStatements);
    }
};

traverse(ast, visitor);

// 清理未使用的变量声明
const cleanVisitor = {
    VariableDeclarator(path) {
        let varname = path.node.id.name;
        if (varname === startVar) {
            path.remove();  // 删除状态变量 _s
        }
    }
};
traverse(ast, cleanVisitor);

console.log(generator(ast, { compact: false }).code);
相关推荐
萧曵 丶9 小时前
Vue 中父子组件之间最常用的业务交互场景
javascript·vue.js·交互
Amumu1213810 小时前
Vue3扩展(二)
前端·javascript·vue.js
NEXT0610 小时前
JavaScript进阶:深度剖析函数柯里化及其在面试中的底层逻辑
前端·javascript·面试
牛奶12 小时前
你不知道的 JS(上):原型与行为委托
前端·javascript·编译原理
牛奶12 小时前
你不知道的JS(上):this指向与对象基础
前端·javascript·编译原理
牛奶12 小时前
你不知道的JS(上):作用域与闭包
前端·javascript·电子书
pas13613 小时前
45-mini-vue 实现代码生成三种联合类型
前端·javascript·vue.js
颜酱14 小时前
数组双指针部分指南 (快慢·左右·倒序)
javascript·后端·算法
兆子龙14 小时前
我成了🤡, 因为不想看广告,花了40美元自己写了个鸡肋挂机脚本
android·javascript
SuperEugene14 小时前
枚举不理解?一文让你醍醐灌顶
前端·javascript