ast处理之前的js源码:链接:https://pan.baidu.com/s/1Duf7G8i2W3GQ1vt46NnxWg?pwd=x9rd
提取码:x9rd
文章是学习明妃ast的笔记,加了学习时的一些注释,对照学习看不懂的地方进行了debugger方式查看运行结果,内容不多,慢慢敲一遍。
平时主要是自学,不系统,getbinding和scope这些玩意儿拿捏不住,不过,两层for循环我还是能看得懂滴!
const parser = require("@babel/parser");
const generator = require("@babel/generator").default;
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const fs = require("fs");
const js_code = fs.readFileSync(
"./boos_01_源码.js", {encoding: "utf-8"}
);
const ast = parser.parse(js_code, {sourceType: "module"});
function character_decoding_array(path) {
let node = path.node;
if (!t.isIdentifier(node.object.object)) {
return
}
if (t.isStringLiteral(node.property)) {
// node.property.extra.raw = node.property.extra.rawValue
delete node.property.extra.raw
}
if (t.isStringLiteral(node.object.property)) {
// node.object.property.extra.raw = node.object.property.extra.rawValue
delete node.object.property.extra.raw
}
}
function character_decoding_string(path) {
let node = path.node;
if (!t.isIdentifier(node.object)) {
return
}
if (t.isStringLiteral(node.property)) {
// node.property.extra.raw = node.property.extra.rawValue
delete node.property.extra.raw
}
}
function case_handle_one(path) {
// 控制流平坦化 主要针对在case中通过+=运算符修改流程的控制流
path.traverse({
// 只处理分发器时单个声明的
VariableDeclaration(p) {
// 第一步:通过特征定位分发器 var J3I = 11;
let declarations = p.node.declarations;
if (!(declarations.length === 1 && t.isNumericLiteral(declarations[0].init))) {
return
}
// // key: 有兄弟节点时,为当前节点,在容器中的索引,没有时,是 当前节点属性名
let whilePath = p.getSibling(p.key + 1);
if (!t.isWhileStatement(whilePath)) {
// 声明语句后面不是while节点就退出
return;
}
// 第二步:获取控制器的名称和值
let shufferVar = {name: declarations[0].id.name, value: declarations[0].init.value}
console.log("分发器初始值:", shufferVar);
// 第三步:映射case,为了方便获取case代码块
let _case = {};
whilePath.node.body.body[0].cases.map((v) => {
// v就是一个一个的 SwitchCase 节点,v.consequent时case里的代码块 数组结构
let switchCase = v.consequent;
// v.test.value 这个获取的是case后面的数值 case 1: 里面的1
_case[v.test.value] = switchCase
});
// console.log(_case);
// 第四步:构造分支流程 构造出前后分支的代码
let shufferFlow = [], // 分支流程数组
flag = false; // 没有 break 标识
whilePath.node.body.body[0].cases.map((v) => {
// 获取最后一个代码块 大多数是 break ,不是 break 的话,该 cases 执行完就 跳到下一个 case
let n = v.consequent.slice(-1)[0];
// console.log(n);
if (t.isBreakStatement(n)) {
let caseNum = v.test.value;
console.log("遍历分发器的值:", caseNum);
let nextNode = v.consequent.slice(-2)[0];
if (nextNode.expression.left.name === shufferVar.name) {
// 变量名 与 分发器起始值一样 更新
let nextNum = caseNum; // 当前流程 eval计算下依次流程
eval(`nextNum
${nextNode.expression.operator}
${nextNode.expression.right.value}`);
flag && (nextNum -= 1) && (flag = false);
shufferFlow.push([caseNum, nextNum])
}
} else {
if (!t.isReturnStatement(n)) {
// 不是 return 执行完直接到 下一个 cases,但 下一个 case 需要单独处理
let caseNum = v.test.value;
let nextNode = caseNum + 1;
flag = true;
shufferFlow.push([caseNum, nextNode])
}
}
});
console.log("前后分支关系:", JSON.stringify(shufferFlow), shufferFlow.length)
// 通过 节点起始 流程还原
let shufferFlow_ = [];
shufferFlow.map((v1, i1) => {
if (shufferVar.value === v1[0]) {
shufferFlow_.push(v1); // 起始值 和 下一个值
for (let i = 0; i < shufferFlow.length; i++) {
for (let j = 0; j < shufferFlow.length; j++) {
if (shufferFlow_[i][1] === shufferFlow[j][0]) {
shufferFlow_.push(shufferFlow[j]);
break
}
}
}
}
});
console.log("正常分支流程:", JSON.stringify(shufferFlow_));
// 构造分发器
let shufferArr = [];
shufferFlow_.reduce((k1, k2) => {
k1 && shufferArr.push(...k1);
k2[1] > 0 && shufferArr.push(k2[1])
});
console.log("分发器数组:", JSON.stringify(shufferArr), shufferArr.length)
// 获取完整 分发器后,只需要把 每个 case 里面的代码,按照分发器 提取,添加到 父节点的 body 里面就好,
// 这个过程使用判断 删除一点代码就好了
p.remove(); // 删除分发器初始值
whilePath.remove(); // 删除 while 节点
let parentPath = whilePath.parent; // while循环的父节点
shufferArr.map((v) => {
if (t.isBreakStatement(_case[v].slice(-1)[0])) {
_case[v].pop(); // 删除 break 节点
_case[v].pop() // 删除 更新流程 节点
}
parentPath.body.push(..._case[v]) 添加到 父path 节点
});
console.log("新生父节点 body 数量:", parentPath.body.length);
}
})
}
function case_handle_two(path) {
// 控制流平坦化 主要针对i++类型的流程
path.traverse({
VariableDeclaration(p) {
// 第一步:通过特征 定位分发器位置 var Hr2 = 0
// , sAW = [6, 5, 19, 37, 38, 35, 20, 30, 17, 27, 12, 42, 15];
// 在搞这个之前,要先知道Array.prototype 属性表示 Array 构造函数的原型,并允许您向所有Array对象添加新的属性和方法
// 字符解码后Array["prototype"]["p"] = Array["prototype"]["push"]; 所有array对象都有一个p属性,p有是继承原先的push属性
// var Rfa = ZhV.p; ==> var Rfa = ZhV.push
// Rfa.apply(ZhV, l5s) 表示在原有分发器的基础上push了一组array类型的分发器,明白了这些就可以继续往下看了
let declarations = p.node.declarations;
if (declarations.length != 2) { // 判断长度
return
}
if (!t.isNumericLiteral(declarations[0].init, {value: 0})) {
// 条件是初始值为0的赋值是否是数值类型的判断
return;
}
if (!t.isArrayExpression(declarations[1].init)) {
// 第二个是数组类型的判断
return;
}
// 获取while 循环 p的下一个兄弟节点
let whilePath = p.getSibling(p.key + 1);
if (!t.isWhileStatement(whilePath)) {
return; //
}
// 第二步:获取分发器初始数组
// 先通过 定位 while 节点,获取前兄弟节点,判断是否是 初始分发器数组
let shufferVAr = declarations[1].id.name; // 分发器名称
let shufferArr = {value: declarations[1].init.elements};
console.log("初始 分发器:", {name: shufferVAr, lenght: shufferArr.value.length});
// 第三步:映射 case 代码块,并 记录 更新分发器变量名
// 映射为了 方面获取 case 代码块,映射过程,可以通过 apply 的特征,记录其他分发器数组的变量名
// return;
let _case = {};
let shufferVArName = {arrName: {}};
whilePath.node.body.body[0].cases.map((v) => { // v 是每一个 case
let switchCase = v.consequent; // case里面的代码块, 数组结构
if (t.isExpressionStatement(switchCase.slice(-2)[0])) {
let n = switchCase.slice(-2)[0]; // h = M4l;
if (n.expression.callee // Rfa.apply(ZhV, j10); // push 新的分发器
&& t.isMemberExpression(n.expression.callee) // CBf() 过滤直接调用函数的形式
&& n.expression.callee.property.name === "apply"
&& n.expression.arguments[0].name === shufferVAr
) {
// // 记录 另一部分 分发器变量名
shufferVArName.arrName[n.expression.arguments[1].name] = true;
switchCase.splice(-2, 1) // 删除 apply 语句行
}
}
// _case[6] = 对应的语句块
_case[v.test.value] = switchCase
});
// 第四步:组件完整分发器
// 每次取从 tmpShufferArr 删除头部值并获得,来进行过一遍流程分支,
// 过的过程中动态添加 tmpShufferArr 值,来获得完整的分发器
let tmpShufferArr = shufferArr.value.concat(); // 深复制
shufferArr = []; // 分发器置空
let a = 0;
while (tmpShufferArr.length !== 0) { // 这样写 每次更新临时分发器时,能增加循环
// shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
let index = tmpShufferArr.shift().value; // 每次从 临时分发器 头部获取 流程值
let consequent = _case[index]; // 从_case中获取对应的语句块
shufferArr.push(index); // 从 临时分发器 获得的流程值 才是真实的
let n = consequent.slice(-1)[0]; // 获取最后一个代码块
// console.log(a++);
if (!t.isBreakStatement(n) && !t.isReturnStatement(n)) {
// 为了处理:结尾 不是 break 和 return 的 case,下一次直接进入下一个 case 代码块
// unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
tmpShufferArr.unshift({value: index + 1}) // 更新临时分发器
}
// var l5s = 获取倒数第二个语句块,判断是否是分发器
let b = consequent.slice(-2)[0];
if (t.isVariableDeclaration(b) && b.declarations[0]
&& shufferVArName.arrName[b.declarations[0].id.name]) {
// 更新临时分发器
tmpShufferArr.push(...b.declarations[0].init.elements);
_case[index].splice(-2, 1)
}
if (t.isVariableDeclaration(b) && b.declarations[0]
&& b.declarations[0].init && b.declarations[0].init.object
&& b.declarations[0].init.object.name === shufferVAr // 形式 var Rfa = ZhV.p;
&& b.declarations[0].init.property.name === "p") {
_case[index].splice(-2, 1) // 删除 push 声明
}
}
console.log("完整分发器:", JSON.stringify(shufferArr))
// 第五步:还原 遍历分发器,获取case代码块,添加到父节点的body里,并删除break
p.remove(); // 删除分发器
whilePath.remove(); // 删除 while 节点
let parentPath = whilePath.parent;
shufferArr.map((v) => {
if (t.isBreakStatement(_case[v].slice(-1)[0])) {
_case[v].pop() // 删除分发器
}
// _case[v] 是一个数组 ...解构之后一次性push到body
parentPath.body.push(..._case[v])
})
}
})
}
let visitor = {
MemberExpression: {
enter: [character_decoding_array, character_decoding_string]
},
"FunctionDeclaration|FunctionExpression": {
enter: [
case_handle_one,
case_handle_two
]
}
};
// 平坦流处理
// charCodeAt() 返回在指定的位置的字符的 Unicode 编码
// charAt() 返回在指定位置的字符
traverse(ast, visitor);
let {code} = generator(ast, opts = {jsescOption: {"minimal": true}});
// 文件保存
fs.writeFile('./boos_01_new.js', code, (err) => {
});