203 lines
6.0 KiB
JavaScript
203 lines
6.0 KiB
JavaScript
const { clone } = require("../shared");
|
|
const { runPatternPass } = require("./shared-pattern-pass");
|
|
const { createSandbox, evaluateExpression, evaluateNodes, generateCode, traverse, t } = require("./pattern-utils");
|
|
|
|
function unwrapTopLevelIife(ast) {
|
|
const first = ast.program.body[0];
|
|
if (!first || first.type !== "ExpressionStatement" || first.expression.type !== "CallExpression") {
|
|
return false;
|
|
}
|
|
const callee = first.expression.callee;
|
|
if (callee.type !== "FunctionExpression") {
|
|
return false;
|
|
}
|
|
ast.program.body = callee.body.body.map((node) => clone(node));
|
|
return true;
|
|
}
|
|
|
|
function expandSequenceExpressions(ast) {
|
|
let changed = false;
|
|
|
|
traverse(ast, {
|
|
SequenceExpression(path) {
|
|
if (path.parentPath.isExpressionStatement()) {
|
|
path.replaceWithMultiple(path.node.expressions.map((expr) => t.expressionStatement(clone(expr))));
|
|
changed = true;
|
|
return;
|
|
}
|
|
|
|
if (path.parentPath.isReturnStatement()) {
|
|
const expressions = path.node.expressions.map((expr) => clone(expr));
|
|
const last = expressions.pop();
|
|
if (!last) {
|
|
return;
|
|
}
|
|
path.parentPath.replaceWithMultiple([
|
|
...expressions.map((expr) => t.expressionStatement(expr)),
|
|
t.returnStatement(last)
|
|
]);
|
|
changed = true;
|
|
return;
|
|
}
|
|
|
|
if (path.parentPath.isVariableDeclarator()) {
|
|
const declaratorPath = path.parentPath;
|
|
const declarationPath = declaratorPath.parentPath;
|
|
if (!declarationPath || !declarationPath.isVariableDeclaration()) {
|
|
return;
|
|
}
|
|
|
|
const expressions = path.node.expressions.map((expr) => clone(expr));
|
|
const last = expressions.pop();
|
|
if (!last) {
|
|
return;
|
|
}
|
|
|
|
const inserted = [
|
|
...expressions.map((expr) => t.expressionStatement(expr)),
|
|
t.variableDeclaration(declarationPath.node.kind, [
|
|
t.variableDeclarator(clone(declaratorPath.node.id), last)
|
|
])
|
|
];
|
|
|
|
declarationPath.insertAfter(inserted);
|
|
declaratorPath.remove();
|
|
if (declarationPath.get("declarations").length === 0) {
|
|
declarationPath.remove();
|
|
}
|
|
changed = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
return changed;
|
|
}
|
|
|
|
function unwrapUnaryIifes(ast) {
|
|
let changed = false;
|
|
|
|
traverse(ast, {
|
|
UnaryExpression(path) {
|
|
if (
|
|
path.node.operator !== "!" ||
|
|
!path.get("argument").isCallExpression() ||
|
|
path.get("argument.arguments").length !== 0 ||
|
|
!path.get("argument.callee").isFunctionExpression() ||
|
|
path.get("argument.callee.params").length !== 0
|
|
) {
|
|
return;
|
|
}
|
|
|
|
path.replaceWithMultiple(path.get("argument.callee.body").node.body.map((node) => clone(node)));
|
|
changed = true;
|
|
}
|
|
});
|
|
|
|
return changed;
|
|
}
|
|
|
|
function yidunDispatcherPass(ast) {
|
|
let changed = false;
|
|
|
|
if (unwrapTopLevelIife(ast)) {
|
|
changed = true;
|
|
}
|
|
|
|
if (expandSequenceExpressions(ast)) {
|
|
changed = true;
|
|
}
|
|
|
|
if (unwrapUnaryIifes(ast)) {
|
|
changed = true;
|
|
}
|
|
|
|
const body = ast.program.body;
|
|
if (body.length >= 2 && body[1].type === "FunctionDeclaration" && body[1].id && body[1].params.length === 2) {
|
|
const sandbox = createSandbox();
|
|
const decoderName = body[1].id.name;
|
|
if (evaluateNodes(body.slice(0, 2), sandbox) && typeof sandbox[decoderName] === "function") {
|
|
traverse(ast, {
|
|
CallExpression(path) {
|
|
if (!path.get("callee").isIdentifier({ name: decoderName }) || path.node.arguments.length !== 1 || !path.get("arguments.0").isNumericLiteral()) {
|
|
return;
|
|
}
|
|
const result = evaluateExpression(path.toString(), sandbox);
|
|
if (!result.ok) {
|
|
return;
|
|
}
|
|
path.replaceWith(t.valueToNode(result.value));
|
|
changed = true;
|
|
}
|
|
});
|
|
|
|
ast.program.body = ast.program.body.slice(2).map((node) => clone(node));
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
traverse(ast, {
|
|
VariableDeclaration(path) {
|
|
if (
|
|
path.node.declarations.length !== 5 ||
|
|
!path.get("declarations").every((declarationPath) => {
|
|
return (
|
|
declarationPath.get("init").isMemberExpression() &&
|
|
declarationPath.get("init.object").isNewExpression() &&
|
|
declarationPath.get("init.property").isStringLiteral()
|
|
);
|
|
})
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const prev = path.getPrevSibling();
|
|
const next1 = path.getNextSibling();
|
|
const next2 = next1 && next1.getNextSibling();
|
|
if (!prev || !next1 || !next2 || !prev.node || !next1.node || !next2.node) {
|
|
return;
|
|
}
|
|
|
|
const sandbox = createSandbox();
|
|
if (!evaluateNodes([prev.node, path.node, next1.node, next2.node], sandbox)) {
|
|
return;
|
|
}
|
|
|
|
const names = []
|
|
.concat(next1.node.declarations || [])
|
|
.concat(next2.node.declarations || [])
|
|
.map((declaration) => declaration.id)
|
|
.filter((idNode) => t.isIdentifier(idNode))
|
|
.map((idNode) => idNode.name);
|
|
|
|
names.forEach((name) => {
|
|
const binding = path.scope.getBinding(name);
|
|
if (!binding || !binding.constant) {
|
|
return;
|
|
}
|
|
binding.referencePaths.forEach((refPath) => {
|
|
const parent = refPath.parentPath;
|
|
if (!parent.isMemberExpression({ object: refPath.node }) || !parent.get("property").isNumericLiteral()) {
|
|
return;
|
|
}
|
|
const result = evaluateExpression(generateCode(parent.node), sandbox);
|
|
if (!result.ok) {
|
|
return;
|
|
}
|
|
parent.replaceWith(t.valueToNode(result.value));
|
|
changed = true;
|
|
});
|
|
});
|
|
|
|
prev.remove();
|
|
path.remove();
|
|
next1.remove();
|
|
next2.remove();
|
|
changed = true;
|
|
}
|
|
});
|
|
|
|
return { ast, changed };
|
|
}
|
|
|
|
runPatternPass(yidunDispatcherPass);
|