AST 样例
本文搬运于GitHub:原文
依赖: 请自行安装node.js 同时安装 babel
控制流相关
while-if转变为while-switch
代码:
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode =
`
function test() {
var index = 0,
arr = [3, 0, 2, 1],
pindex;
while (1) {
pindex = arr[index++];
if (pindex < 1) {
console.log("this is index 0");
} else if (pindex < 2) {
console.log("this is index 1");
return;
} else if (pindex < 3) {
console.log("this is index 2");
} else {
console.log("Hello world!");
}
}
}
`;
let ast = parse(jscode);
const visitor =
{
"WhileStatement"(path) {
let {test, body} = path.node;
//******************************************************特征判断开始
if (!t.isNumericLiteral(test, {value: 1})) return;
let block_body = body.body;
if (block_body.length !== 2 || !t.isExpressionStatement(block_body[0]) || !t.isIfStatement(block_body[1])) {
return;
}
//******************************************************特征判断结束
let {left, right} = block_body[0].expression;
let name = left.name;
let if_arrs = [];
path.traverse({
"IfStatement"(_path) {
let {test, consequent, alternate} = _path.node;
let {left, operator, right} = test;
if (!t.isIdentifier(left, {name: name}) || operator !== '<' || !t.isNumericLiteral(right)) return;
if (consequent.body.length === 1) {
if_arrs[right.value - 1] = consequent.body[0];
} else {
if_arrs[right.value - 1] = consequent;
}
if (!t.isIfStatement(alternate)) {
if (consequent.body.length === 1) {
if_arrs[right.value] = alternate.body[0];
} else {
if_arrs[right.value] = alternate;
}
}
},
})
if (if_arrs.length === 0) return;
for (var i = 0; i < if_arrs.length - 1; i++) {
consequent = [if_arrs[i], t.BreakStatement()];
if_arrs[i] = t.SwitchCase(test = t.NumericLiteral(i), consequent = consequent);
}
consequent = [if_arrs[if_arrs.length - 1], t.BreakStatement()];
if_arrs[i] = t.SwitchCase(test = null, consequent = consequent);
let new_node = t.SwitchStatement(right, if_arrs);
path.get('body.body.1').replaceInline(new_node);
path.get('body.body.0').remove();
},
}
//some function code
traverse(ast, visitor);
let {code} = generator(ast);
console.log(code);
去控制流(while-switch)
代码:
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode =
`
var arr = "3|0|1|2|4".split("|");
var cnt = 0;
while (true) {
switch (arr[cnt++]) {
case "1":
console.log("This is case-block 1");
continue;
case "0":
console.log("This is case-block 0");
continue;
case "2":
console.log("This is case-block 2");
continue;
case "4":
console.log("This is case-block 4");
continue;
case "3":
console.log("This is case-block 3");
continue;
}
break;
}
`;
let ast = parse(jscode);
const visitor =
{
WhileStatement(path) {
const { test, body } = path.node;
//特征语句判断,视情况而定,也可以不判断
if (!t.isBooleanLiteral(test) || test.value !== true) return;
//特征语句判断,body.body[0] 必须是 SwitchStatement 节点,
//注意一定要先判断长度,避免index出错
if (body.body.length === 0 || !t.isSwitchStatement(body.body[0])) return;
let switch_state = body.body[0];
//获取discriminant及cases节点
let { discriminant, cases } = switch_state;
//特征语句判断,经过此判断后,基本可以确定是需要还原的while节点了。
//如果出错了,可以继续增加判断,直到不出错即可
if (!t.isMemberExpression(discriminant) || !t.isUpdateExpression(discriminant.property)) return;
//获取数组名,用于查找该数组。
let arr_name = discriminant.object.name;
let arr = [];
//在这里再加一个特征语句的判断:WhileStatement 节点前面有两个节点
let all_pre_siblings = path.getAllPrevSiblings();
if (all_pre_siblings.length !== 2) return;
all_pre_siblings.forEach((pre_path) => {
//虽然知道是第0个节点,但这里还是做下判断取arr
const { declarations } = pre_path.node;
let { id, init } = declarations[0];
if (arr_name === id.name) {
//如果是定义arr的节点,拿到该arr的值
arr = init.callee.object.value.split("|");
}
//没啥用的语句可以直接删除
pre_path.remove();
});
//新建一个数组变量,用于存放 case 节点
let ret_body = [];
arr.forEach((index) => {
//遍历数组,去case节点
let case_body = [];
for (const tmp of cases){
if(index === tmp.test.value){
case_body = tmp.consequent;
break;
}
}
if (t.isContinueStatement(case_body[case_body.length - 1])) {
//删除 continue语句
case_body.pop();
}
//存放于数组变量中
ret_body = ret_body.concat(case_body);
});
//替换
path.replaceInline(ret_body);
}
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
/*
去控制流(while-switch), 代码不通用,存在优化空间,
比如将 case 做个键值对映射,这样直接取结果就比循环判断好一些
*/
去控制流(for-switch)
代码:
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
// let jscode = "if (true)\n" +
// "{\n" +
// " //do something;\n" +
// " funcA();\n" +
// "}\n" +
// "else\n" +
// "{\n" +
// " //do something;\n" +
// " funcB();\n" +
// "}";
let jscode = "if ('123'==='123')\n" +
" a = 123;\n" +
"else\n" +
" a = 456;";
let ast = parse(jscode);
const visitor1 =
{
BinaryExpression(path) {
let {confident, value} = path.evaluate();
// console.log(path.type, confident, value)
if (confident) {
// console.log(path.node);
path.replaceInline(t.valueToNode(value))
}
}
}
const visitor2 =
{
IfStatement(path) {
let {test, consequent, alternate} = path.node;
if (!t.isBlockStatement(consequent)) {//添加中括号
path.node.consequent = t.BlockStatement([consequent]);
}
if (alternate !== null && !t.isBlockStatement(alternate)) {//添加中括号
path.node.alternate = t.BlockStatement([alternate]);
}
//特征判断,if语句里面的test是否为字面量
if (!t.isLiteral(test)) return;
let value = test.value;
consequent = path.node.consequent;
alternate = path.node.alternate;
if (value) {//替换
path.replaceInline(consequent.body);
} else {//替换
alternate === null ? path.remove() : path.replaceInline(alternate.body);
}
},
}
//some function code
traverse(ast, visitor1);
traverse(ast, visitor2);
let {code} = generator(ast);
console.log(code);
/*
if (true)
//do something;
else
//do something;
与
if (true)
{
//do something;
}
else
{
//do something;
}
解析出来的结构会有小小的差异,不利于处理,因此先将上面的代码转变为下面的代码,再进行处理即可
1.先将if语句块中没有 中括号的处理成 包含中括号的
2.判断if条件里面的值,获取应该执行的语句块
3.用语句块替换整个if表达式即可。
*/
条件表达式拆分为if语句
代码:
/*
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode =
`
r ? r > 1 ? e.apply(t, arguments) : e.call(t, n) : e.call(t);
`;
let ast = parse(jscode);
const visitor =
{
ConditionalExpression(path) {
let { test, consequent, alternate } = path.node;
const ParentPath = path.parentPath;
if (t.isExpressionStatement(ParentPath)) {
if (!t.isExpressionStatement(consequent)) {
consequent = t.BlockStatement([t.ExpressionStatement(consequent)]);
}
if (!t.isExpressionStatement(alternate)) {
alternate = t.BlockStatement([t.ExpressionStatement(alternate)]);
}
ParentPath.replaceInline(t.IfStatement(test, consequent, alternate));
}
}
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
/*
r ? r > 1 ? e.apply(t, arguments) : e.call(t, n) : e.call(t);
if (r) {
if (r > 1) {
e.apply(t, arguments);
} else {
e.call(t, n);
}
} else {
e.call(t);
}
*/
处理条件已知的if语句
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
// let jscode = "if (true)\n" +
// "{\n" +
// " //do something;\n" +
// " funcA();\n" +
// "}\n" +
// "else\n" +
// "{\n" +
// " //do something;\n" +
// " funcB();\n" +
// "}";
let jscode = "if ('123'==='123')\n" +
" a = 123;\n" +
"else\n" +
" a = 456;";
let ast = parse(jscode);
const visitor1 =
{
BinaryExpression(path) {
let {confident, value} = path.evaluate();
// console.log(path.type, confident, value)
if (confident) {
// console.log(path.node);
path.replaceInline(t.valueToNode(value))
}
}
}
const visitor2 =
{
IfStatement(path) {
let {test, consequent, alternate} = path.node;
if (!t.isBlockStatement(consequent)) {//添加中括号
path.node.consequent = t.BlockStatement([consequent]);
}
if (alternate !== null && !t.isBlockStatement(alternate)) {//添加中括号
path.node.alternate = t.BlockStatement([alternate]);
}
//特征判断,if语句里面的test是否为字面量
if (!t.isLiteral(test)) return;
let value = test.value;
consequent = path.node.consequent;
alternate = path.node.alternate;
if (value) {//替换
path.replaceInline(consequent.body);
} else {//替换
alternate === null ? path.remove() : path.replaceInline(alternate.body);
}
},
}
//some function code
traverse(ast, visitor1);
traverse(ast, visitor2);
let {code} = generator(ast);
console.log(code);
/*
if (true)
//do something;
else
//do something;
与
if (true)
{
//do something;
}
else
{
//do something;
}
解析出来的结构会有小小的差异,不利于处理,因此先将上面的代码转变为下面的代码,再进行处理即可
1.先将if语句块中没有 中括号的处理成 包含中括号的
2.判断if条件里面的值,获取应该执行的语句块
3.用语句块替换整个if表达式即可。
*/
删除相关
删除多余的空行和空语句
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var a = 123;\n" +
"\n" +
";\n" +
"\n" +
"var b = 456;";
let ast = parse(jscode);
const visitor =
{
EmptyStatement(path)
{
path.remove();
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
删除所有的代码注释
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var a = 123; //this is single line comment\n" +
"\n" +
"/*\n" +
"\n" +
"This is a multiline comments;\n" +
"\n" +
"test\n" +
"\n" +
"test\n" +
"\n" +
"test\n" +
"\n" +
"*/\n" +
"\n" +
"var b = 456;";
let ast = parse(jscode);
const visitor =
{
//TODO write your code here!
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast, {comments:false});
console.log(code);
删除未被使用的变量
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var a = 123,b;\n" +
"\n" +
"let c = 4 + 5;\n" +
"\n" +
"d = a + 12;";
let ast = parse(jscode);
const visitor =
{
VariableDeclarator(path) {
const {id} = path.node;
const binding = path.scope.getBinding(id.name);
//如果变量被修改过,则不能进行删除动作。
if (!binding || binding.constantViolations.length > 0) {
return;
}
//长度为0,说明变量没有被使用过。
if (binding.referencePaths.length === 0) {
path.remove();
}
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
删除未被调用的函数
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "function i()\n" +
"\n" +
"{\n" +
"\n" +
" var i = 123;\n" +
"\n" +
" i += 2;\n" +
"\n" +
" \n" +
"\n" +
" return 123;\n" +
"\n" +
"}";
let ast = parse(jscode);
const visitor =
{
FunctionDeclaration(path) {
// path.scope.dump();
const {id} = path.node;
const binding = path.scope.parent.getBinding(id.name);
if (!binding || binding.constantViolations.length > 0) {
return;
}
if (binding.referencePaths.length === 0) {
path.remove();
}
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
还原相关
还原Array对象
/*
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var _2$SS = function (_SSz, _1111) {\n" +
" var _l1L1 = [46222, '\x74\x61\x43\x61\x70\x74\x63\x68\x61\x42\x6c\x6f\x62', '\x74', '\x61', '\x73', '\x6c', '\x64', '\x69', .3834417654519915, '\x65\x6e\x63\x72\x79\x70\x74\x4a', '\x73\x6f', '\x6e', 49344];\n" +
"\n" +
" var _2Szs = _l1L1[5] + _l1L1[7] + (_l1L1[4] + _l1L1[2]),\n" +
" _I1il1 = _l1L1[9] + (_l1L1[10] + _l1L1[11]);\n" +
"\n" +
" var _0ooQoO = _l1L1[0];\n" +
" var _$Z22 = _l1L1[12],\n" +
" _2sS2 = _l1L1[8];\n" +
" return _l1L1[6] + _l1L1[3] + _l1L1[1];\n" +
"};";
let ast = parse(jscode);
const visitor =
{
VariableDeclarator(path){
// 还原数组对象
const {id, init} = path.node;
// 非Array或者没有元素, 返回
if (!t.isArrayExpression(init) || init.elements.length===0) return;
let elements = init.elements;
// 获取binding实例
const binding = path.scope.getBinding(id.name);
for ( const ref_path of binding.referencePaths){
// 获取 MemberExpression 父节点
let member_path = ref_path.findParent(p=>p.isMemberExpression());
let property = member_path.get('property');
// 索引值不是 NumericLiteral 类型的不处理
if(!property.isNumericLiteral()){
continue;
}
// 获取索引值
let index = property.node.value;
// 获取索引值对应的节点, 并替换
let arr_ele = elements[index];
member_path.replaceWith(arr_ele)
}
}
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
还原object对象
/*
date : 2020/8/11
desc :
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var a = {\n" +
" \"YJJox\": \"object\",\n" +
" \"sbTga\": function (b, c) {\n" +
" return b | c;\n" +
" },\n" +
" \"iwvEK\": function (b, c) {\n" +
" return b << c;\n" +
" },\n" +
" \"HqkiD\": function (b, c) {\n" +
" return b(c);\n" +
" }\n" +
"};\n" +
"b = a[\"iwvEK\"](1, 3), c = a[\"sbTga\"](111, 222), d = a[\"YJJox\"], e = a[\"HqkiD\"](String.fromCharCode, 49);";
let ast = parse(jscode);
const visitor =
{
VariableDeclarator(path) {
const {id, init} = path.node;
//特征判断,对象为空则不处理
if (!t.isObjectExpression(init) || init.properties.length === 0) return;
let name = id.name;
let scope = path.scope;
for (const property of init.properties) {//遍历key、value
let key = property.key.value;
let value = property.value;
//一般ob混淆,key长度都是5,也有是3的,自行调整即可。
if (key.length !== 5) return;
//如果是字面量
if (t.isLiteral(value)) {
scope.traverse(scope.block, {
//遍历MemberExpression,找出与key相同的表达式
MemberExpression(_path) {
let _node = _path.node;
if (!t.isIdentifier(_node.object, {name: name})) return;
if (!t.isLiteral(_node.property, {value: key})) return;
_path.replaceWith(value);
},
})
}
//如果是函数表达式
else if (t.isFunctionExpression(value)) {
let ret_state = value.body.body[0];
//特征判断,如果不是return表达式
if (!t.isReturnStatement(ret_state)) continue;
scope.traverse(scope.block, {
CallExpression: function (_path) {
//遍历CallExpression
let {callee, arguments} = _path.node;
if (!t.isMemberExpression(callee)) return;
if (!t.isIdentifier(callee.object, {name: name})) return;
if (!t.isLiteral(callee.property, {value: key})) return;
if (t.isCallExpression(ret_state.argument) && arguments.length > 0) {
//构造节点
_path.replaceWith(t.CallExpression(arguments[0], arguments.slice(1)));
} else if (t.isBinaryExpression(ret_state.argument) && arguments.length === 2) {
//构造节点
let replace_node = t.BinaryExpression(ret_state.argument.operator, arguments[0], arguments[1]);
_path.replaceWith(replace_node);
} else if (t.isLogicalExpression(ret_state.argument) && arguments.length === 2) {
//构造节点
let replace_node = t.LogicalExpression(ret_state.argument.operator, arguments[0], arguments[1]);
_path.replaceWith(replace_node);
}
}
})
}
}
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
/*
1.这里是 VariableDeclarator 节点,子节点 init 是 ObjectExpression 类型的表达式,因此我们可以遍历VariableDeclarator节点,便于获取 对象名 及整个对象
2.遍历 ObjectExpression 节点的 properties 属性,它是一个 数组,遍历这个数组,获取key和value
3.判断 value 的节点类型,如果是字面量,则可以直接进行替换;如果是函数表达式,则需要通过返回的表达式类型构造相应的表达式。然后在作用域块内遍历 MemberExpression (PS:a["sbTga"]) 节点,如果是对象名,并且当前的key值也相等,则进行节点替换。
4.遍历 properties 完毕后,可以试着删除整个 VariableDeclarator 节点,如果不报错就没事。
*/
还原定义的字面量
/*
date : 2020/8/11
desc :
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var s = 92;\n" +
"b = Z(1324801, 92);";
let ast = parse(jscode);
const visitor =
{
"Identifier"(path)
{
const {confident,value} = path.evaluate();
confident && path.replaceInline(t.valueToNode(value));
},
// 替换完了就没用了,将其删除
VariableDeclarator(path)
{
const {id,init} = path.node;
if (!t.isLiteral(init)) return;//只处理字面量
const binding = path.scope.getBinding(id.name);
if (!binding || binding.constantViolations.length > 0)
{//如果该变量的值被修改则不能处理
return;
}
for (const refer_path of binding.referencePaths)
{
refer_path.replaceWith(init);
}
path.remove();
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
还原成中文字符
/*
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var s = \"\u4f60\u597d\uff0c\u4e16\u754c;\"\n" +
"var a = \"\u0068\u0065\u006c\u006c\u006f\u002c\u0020\u0077\u006f\u0072\u0064\";";
let ast = parse(jscode);
const visitor =
{
StringLiteral(path)
{
path.get('extra').remove();
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast, {jsescOption:{"minimal":true}});
console.log(code);
还原自执行函数的实参
/*
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "!function (a, b) {\n" +
" c = a | b;\n" +
"}(111, 222);";
let ast = parse(jscode);
const visitor =
{
CallExpression(path){
let callee = path.get('callee');
let arguments = path.get('arguments');
if(!t.isFunctionExpression(callee) || arguments.length ===0){
// 实参的长度判断可以写死
return;
}
// 获取形参
let params = callee.get('params');
let scope = callee.scope;
for ( let i =0; i< arguments.length; i++){
// 遍历实参, 因为形参可能比实参长
let arg = params[i];
let {name} = arg.node;
const binding = scope.getBinding(name);
if(!binding || binding.constantViolations.length > 0){
// 形参发生改变,不能被还原
continue;
}
for(refer_path of binding.referencePaths){
// 字面量可以直接替换
refer_path.replaceWith(arguments[i]);
}
arg.remove();
arguments[i].remove();
}
}
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
// 替换完之后可以通过无参的自执行插件再替换
合并定义在object对象外面的key、value
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode =
`
var h = {};
h["aaa"] = "hello wolrd";
h["bbb"] = function (a,b)
{
return a | b;
}
`;
let ast = parse(jscode);
const visitor =
{
VariableDeclarator(path)
{
const {id,init} = path.node;
if (!t.isObjectExpression(init)) return;
let name = id.name;
let properties = init.properties;
let all_next_siblings = path.parentPath.getAllNextSiblings();
for (let next_sibling of all_next_siblings)
{
if (!next_sibling.isExpressionStatement()) break;
let expression = next_sibling.get('expression');
if (!expression.isAssignmentExpression()) break;
let {operator,left,right} = expression.node;
if (operator !== '=' || !t.isMemberExpression(left) || !t.isIdentifier(left.object,{name:name}))
{
break;
}
properties.push(t.ObjectProperty(left.property,right));
next_sibling.remove();
}
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
/*
1.object对象使用var定义的,因此遍历 VariableDeclarator 节点即可
2.依次判断后续节点,是否为定义在外面的key和value
3.收集key和value,用于构造ObjectProperty 节点
4. properties 属性是Array对象,只用push方法来增加节点
5.处理完成后删除后续节点。
*/
表达式相关
逗号表达式
/*
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const types = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode =
`
b = (0,g.o)(a);
c = (a=1,b=2,c=3,d=4,e=5,f);
function get()
{
return a=1,b=2,a +=2,a;
}
`;
let ast = parse(jscode);
const visitor =
{
SequenceExpression: {
exit(path){
let expressions = path.get('expressions');
let last_expression = expressions.pop();
let statement = path.getStatementParent();
if(statement){
for(let expression of expressions)
{
// 删除无用的干扰代码
if(expression.isLiteral() ||expression.isIdentifier())
{
expression.remove();
continue;
}
statement.insertBefore(types.ExpressionStatement(expression=expression.node));
}
path.replaceInline(last_expression);
}
}
}
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
/*******************************************
b = (0,g.o)(a);
c = (a=1,b=2,c=3,d=4,e=5,f);
function get()
{
return a=1,b=2,a +=2,a;
}
===>
b = g.o(a);
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
c = f;
function get() {
a = 1;
b = 2;
a += 2;
return a;
}
******************************************/
三目运算符
/*
desc : 把 a = m?11:22; 转成 m ? a = 11 : a = 22;
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "a = m ? 11 : 22;";
let ast = parse(jscode);
const visitor =
{
ConditionalExpression(path){
let {test, consequent, alternate} = path.node;
const ParentPath = path.parentPath;
if(t.isAssignmentExpression(ParentPath)){
let {operator, left} = ParentPath.node;
if (operator === '='){
consequent = t.AssignmentExpression('=', left, consequent);
alternate = t.AssignmentExpression('=', left, alternate);
ParentPath.replaceWith(t.ConditionalExpression(test, consequent, alternate))
}
}
}
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
/*
1.节点类型变了,由 AssignmentExpression 类型变成了ConditionalExpression 类型。
2.ConditionalExpression 子节点的 consequent 和 alternate 都变成了 AssignmentExpression 类型。
*/
处理条件已知的三元表达式
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "a = 5 ? 11 : 22;\n" +
"b = 0 ? 33 : 44;";
let ast = parse(jscode);
const visitor =
{
ConditionalExpression(path) {
let { test, consequent, alternate } = path.node;
if (!t.isLiteral(test)) return;
if (test.value) {
path.replaceInline(consequent);
} else {
path.replaceInline(alternate);
}
}
}
//some function code
traverse(ast, visitor);
let {code} = generator(ast);
console.log(code);
// 和常见表达式的计算类似
节点
对同一节点使用多个方法
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "";
let ast = parse(jscode);
const visitor =
{
CallExpression:
{// 注意顺序
enter: [reduce_call_express, delete_empty_params]
},
}
//some function code
function reduce_call_express(path) {
}
function delete_empty_params(path) {
}
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
/*
假设想对一个函数表达式的 CallExpression 节点进行进行实参还原,
如果还原后,没有了实参和形参,假如又可以将函数体里面的代码提取出来。
这个时候就需要两个方法来分别进行处理了
reduce_call_express 还原函数的实参
delete_empty_params 将参数为空的函数进行处理,将函数体里面的代码直接提取出来
*/
构造节点
/*
* 将 var a; 转换为 var a = 123 + 456;
*
* */
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var a";
let ast = parse(jscode);
const visitor =
{
// 方法一
VariableDeclarator(path){
const {init} = path.node;
let node = {
type: "BinaryExpression",
operator: "+",
left: {
type: "NumericLiteral",
value: 123,
},
right: {
type: "NumericLiteral",
value: 456,
}
}
init || path.set("init", node)
},
// 方法二
/* VariableDeclarator(path){
const {init} = path.node;
init || path.set("init", t.binaryExpression('+',t.valueToNode(123),t.valueToNode(456)))
},*/
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
节点类型转换
/*
desc : 将 BinaryExpression 类型转换为 CallExpression 类型
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "var a = 123 | 456;";
let ast = parse(jscode);
const visitor =
{
"VariableDeclarator"(path)
{
const init_path = path.get('init');
if (!init_path.isBinaryExpression()) return
init_path.node.type = "CallExpression";
let {operator,left,right} = init_path.node;
init_path.node.arguments = [left,right];
let id = null;
let frist_arg = t.Identifier('s');
let second_arg = t.Identifier('h');
let params = [frist_arg,second_arg];
let args = t.BinaryExpression(operator,frist_arg,second_arg);
let return_state = t.ReturnStatement(args);
let body = t.BlockStatement([return_state]);
init_path.node.callee = t.FunctionExpression(id ,params,body);
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
函数调用处自动替换计算值
/*
*/
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "function add(a, b) {\n" +
" return a + b;\n" +
"}\n" +
"\n" +
"let c = add(1, 2);";
let ast = parse(jscode);
const visitor =
{
FunctionDeclaration(path) {
let {id} = path.node;
let code = path.toString();
if (code.indexOf("try") !== -1 || code.indexOf("random") !== -1 || code.indexOf("Date") !== -1) {
// 不是纯函数,不处理
return
}
eval(code);
let scope = path.scope;
const binding = path.scope.parent.getBinding(id.name);
if (!binding || binding.constantViolations.length > 0) {
return
}
for (const refer_path of binding.referencePaths) {
// 查找父节点
let call_express = refer_path.findParent(p => p.isCallExpression());
let arguments = call_express.get("arguments");
let args = [];
// 判断参数是否为 Literal 类型
arguments.forEach(arg => {
args.push(arg.isLiteral())
})
// 自行编写判断条件,example
if (args.length === 0 || args.indexOf(false) !== -1) {
continue
}
try {
// 计算值
let value = eval(call_express.toString());
value && call_express.replaceWith(t.valueToNode(value));
} catch (e) {
}
}
}
}
//some function code
traverse(ast, visitor);
let {code} = generator(ast);
console.log(code);
/*
1.函数需要满足在任意位置都能执行。就是一个函数声明的代码,随便拷贝到任意的地方都能直接运行,不会报错。说白了其实也就是函数体内的所有变量或者对象,其作用域只在函数体里面。
2.定义的函数,对于实参是固定的,其结果也是固定的。请大家自行百度 纯函数 的概念
3.实参必须是字面量,因为对于遍历的节点来说。只有是字面量,才能给你计算出具体的字,如果不是字面量,实参对它来说就是 undefined 的,是无法计算的。
4.将函数定义eval到本地环境,然后根据作用域找到函数调用的位置。再eval该表达式即可。注意需要使用try...catch语句,避免错误。
*/
处理eval函数
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "eval('var a = 123;');";
let ast = parse(jscode);
const visitor =
{
CallExpression(path)
{
let {callee,arguments} = path.node;
if (!t.isIdentifier(callee,{name:'eval'})) return;
if (arguments.length !== 1 || !t.isStringLiteral(arguments[0])) return;
let value = arguments[0].value;
path.replaceWith(t.Identifier(value));
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);
处理没有实参的自执行函数
const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
let jscode = "!function () {\n" +
" a = 123;\n" +
"}();";
let ast = parse(jscode);
const visitor = {
UnaryExpression(path) {
const {operator, argument} = path.node;
if (operator !== "!" || !t.isCallExpression(argument)) return;
let {callee, arguments} = argument;
if (!t.isFunctionExpression(callee) || arguments.length !== 0) return;
path.replaceInline(callee.body.body);
},
}
//some function code
traverse(ast,visitor);
let {code} = generator(ast);
console.log(code);