Files
cursornew2026/remove_dead_code.js

252 lines
8.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 移除死代码分支
* 处理 if ("xxx" === "xxx") 和 if ("xxx" !== "yyy") 等模式
*/
const fs = require('fs');
const babel = require('@babel/core');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const t = require('@babel/types');
const inputPath = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out/webview/provider.js';
const outputPath = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out/webview/provider.js';
console.log('╔════════════════════════════════════════════════════╗');
console.log('║ 死代码移除工具 ║');
console.log('╚════════════════════════════════════════════════════╝');
const code = fs.readFileSync(inputPath, 'utf8');
console.log(`读取文件: ${(code.length / 1024).toFixed(2)} KB`);
console.log('\n[1] 解析 AST...');
let ast;
try {
ast = babel.parseSync(code, {
sourceType: 'script',
plugins: []
});
console.log('AST 解析成功');
} catch (e) {
console.error('AST 解析失败:', e.message);
process.exit(1);
}
console.log('\n[2] 移除死代码分支...');
let removedBranches = 0;
// 评估常量条件
function evaluateCondition(node) {
// "xxx" === "xxx" -> true
if (t.isBinaryExpression(node, { operator: '===' }) ||
t.isBinaryExpression(node, { operator: '==' })) {
if (t.isStringLiteral(node.left) && t.isStringLiteral(node.right)) {
return node.left.value === node.right.value;
}
}
// "xxx" !== "yyy" -> true if different
if (t.isBinaryExpression(node, { operator: '!==' }) ||
t.isBinaryExpression(node, { operator: '!=' })) {
if (t.isStringLiteral(node.left) && t.isStringLiteral(node.right)) {
return node.left.value !== node.right.value;
}
}
return null;
}
traverse(ast, {
IfStatement(path) {
const result = evaluateCondition(path.node.test);
if (result === true) {
// 条件永远为 true保留 consequent移除 if 结构
if (t.isBlockStatement(path.node.consequent)) {
path.replaceWithMultiple(path.node.consequent.body);
} else {
path.replaceWith(path.node.consequent);
}
removedBranches++;
} else if (result === false) {
// 条件永远为 false保留 alternate 或移除整个 if
if (path.node.alternate) {
if (t.isBlockStatement(path.node.alternate)) {
path.replaceWithMultiple(path.node.alternate.body);
} else {
path.replaceWith(path.node.alternate);
}
} else {
path.remove();
}
removedBranches++;
}
},
ConditionalExpression(path) {
const result = evaluateCondition(path.node.test);
if (result === true) {
path.replaceWith(path.node.consequent);
removedBranches++;
} else if (result === false) {
path.replaceWith(path.node.alternate);
removedBranches++;
}
}
});
console.log(`移除了 ${removedBranches} 个死代码分支`);
console.log('\n[3] 简化剩余控制流对象...');
let simplifiedProps = 0;
// 再次处理剩余的控制流对象
const objectMappings = new Map();
traverse(ast, {
VariableDeclarator(path) {
if (!t.isIdentifier(path.node.id)) return;
if (!t.isObjectExpression(path.node.init)) return;
const varName = path.node.id.name;
const props = {};
for (const prop of path.node.init.properties) {
if (!t.isObjectProperty(prop)) continue;
let key;
if (t.isStringLiteral(prop.key)) {
key = prop.key.value;
} else if (t.isIdentifier(prop.key)) {
key = prop.key.name;
} else {
continue;
}
if (t.isFunctionExpression(prop.value)) {
const func = prop.value;
if (func.body.body.length === 1 && t.isReturnStatement(func.body.body[0])) {
const ret = func.body.body[0].argument;
if (t.isBinaryExpression(ret) && func.params.length === 2) {
const [p1, p2] = func.params;
if (t.isIdentifier(ret.left) && t.isIdentifier(ret.right) &&
ret.left.name === p1.name && ret.right.name === p2.name) {
props[key] = { type: 'binary', operator: ret.operator };
}
} else if (t.isLogicalExpression(ret) && func.params.length === 2) {
const [p1, p2] = func.params;
if (t.isIdentifier(ret.left) && t.isIdentifier(ret.right) &&
ret.left.name === p1.name && ret.right.name === p2.name) {
props[key] = { type: 'logical', operator: ret.operator };
}
} else if (t.isCallExpression(ret)) {
if (t.isIdentifier(ret.callee) && ret.callee.name === func.params[0]?.name) {
props[key] = { type: 'call', argCount: ret.arguments.length };
}
}
}
} else if (t.isStringLiteral(prop.value)) {
props[key] = { type: 'string', value: prop.value.value };
}
}
if (Object.keys(props).length > 0) {
objectMappings.set(varName, props);
}
}
});
traverse(ast, {
CallExpression(path) {
const callee = path.node.callee;
if (!t.isMemberExpression(callee)) return;
if (!t.isIdentifier(callee.object)) return;
const objName = callee.object.name;
const mapping = objectMappings.get(objName);
if (!mapping) return;
let propName;
if (t.isStringLiteral(callee.property)) {
propName = callee.property.value;
} else if (t.isIdentifier(callee.property) && !callee.computed) {
propName = callee.property.name;
} else {
return;
}
const propInfo = mapping[propName];
if (!propInfo) return;
const args = path.node.arguments;
if (propInfo.type === 'binary' && args.length === 2) {
path.replaceWith(t.binaryExpression(propInfo.operator, args[0], args[1]));
simplifiedProps++;
} else if (propInfo.type === 'logical' && args.length === 2) {
path.replaceWith(t.logicalExpression(propInfo.operator, args[0], args[1]));
simplifiedProps++;
} else if (propInfo.type === 'call' && args.length >= 1) {
const fn = args[0];
const fnArgs = args.slice(1);
path.replaceWith(t.callExpression(fn, fnArgs));
simplifiedProps++;
}
},
MemberExpression(path) {
if (path.parent && t.isCallExpression(path.parent) && path.parent.callee === path.node) {
return;
}
if (!t.isIdentifier(path.node.object)) return;
const objName = path.node.object.name;
const mapping = objectMappings.get(objName);
if (!mapping) return;
let propName;
if (t.isStringLiteral(path.node.property)) {
propName = path.node.property.value;
} else if (t.isIdentifier(path.node.property) && !path.node.computed) {
propName = path.node.property.name;
} else {
return;
}
const propInfo = mapping[propName];
if (!propInfo) return;
if (propInfo.type === 'string') {
path.replaceWith(t.stringLiteral(propInfo.value));
simplifiedProps++;
}
}
});
console.log(`简化了 ${simplifiedProps} 处属性访问`);
console.log('\n[4] 生成代码...');
const output = generate(ast, {
comments: false,
compact: false,
concise: false
}, code);
// 后处理
let finalCode = output.code;
finalCode = finalCode.replace(/\["([a-zA-Z_$][a-zA-Z0-9_$]*)"\]/g, '.$1');
finalCode = finalCode.replace(/'(enumerable|value|get|writable|configurable)':/g, '$1:');
console.log('\n[5] 保存文件...');
fs.writeFileSync(outputPath, finalCode);
console.log(`保存到: ${outputPath}`);
console.log(`新文件大小: ${(finalCode.length / 1024).toFixed(2)} KB`);
// 统计
const deadCodeRemaining = (finalCode.match(/"[A-Za-z]{5}"\s*===\s*"[A-Za-z]{5}"/g) || []).length;
console.log(`\n剩余死代码条件: ${deadCodeRemaining}`);
console.log('\n✅ 完成!');