reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
const { clone, isPrimitiveNode, parseArgs, parseFile, reparse, saveAst, t, traverse } = require("./shared");
|
||||
|
||||
function getReplacementTarget(path) {
|
||||
let current = path;
|
||||
while (current.parentPath.isMemberExpression() && current.key === "object") {
|
||||
current = current.parentPath;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function isUnsafeReplacementTarget(path) {
|
||||
const target = getReplacementTarget(path);
|
||||
if (target.parentPath.isUpdateExpression() && target.key === "argument") {
|
||||
return true;
|
||||
}
|
||||
if ((target.parentPath.isAssignmentExpression() || target.parentPath.isAssignmentPattern()) && target.key === "left") {
|
||||
return true;
|
||||
}
|
||||
if ((target.parentPath.isForInStatement() || target.parentPath.isForOfStatement()) && target.key === "left") {
|
||||
return true;
|
||||
}
|
||||
if (target.parentPath.isUnaryExpression({ operator: "delete" }) && target.key === "argument") {
|
||||
return true;
|
||||
}
|
||||
if (target.parentPath.isObjectProperty() && target.key === "key" && !target.parent.computed) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function replaceSafely(path, node) {
|
||||
if (isUnsafeReplacementTarget(path)) {
|
||||
return false;
|
||||
}
|
||||
path.replaceWith(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
function allRefsAfter(binding, position) {
|
||||
return binding.referencePaths.every(refPath => typeof refPath.node.start !== "number" || refPath.node.start > position);
|
||||
}
|
||||
|
||||
function inlineLiterals(ast) {
|
||||
let changed = false;
|
||||
|
||||
traverse(ast, {
|
||||
MemberExpression(path) {
|
||||
if (t.isArrayExpression(path.node.object) && path.node.object.elements.length === 0 && t.isArrayExpression(path.node.property) && path.node.property.elements.length === 0) {
|
||||
if (replaceSafely(path, t.identifier("undefined"))) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
VariableDeclarator(path) {
|
||||
if (!path.get("id").isIdentifier()) {
|
||||
return;
|
||||
}
|
||||
const name = path.node.id.name;
|
||||
const init = path.node.init;
|
||||
const binding = path.scope.getBinding(name);
|
||||
if (!binding) {
|
||||
return;
|
||||
}
|
||||
if (t.isStringLiteral(init) || t.isNumericLiteral(init) || t.isBooleanLiteral(init)) {
|
||||
if (!binding.constant) {
|
||||
return;
|
||||
}
|
||||
let replacedAll = binding.referencePaths.length > 0;
|
||||
let replacedAny = false;
|
||||
binding.referencePaths.forEach(refPath => {
|
||||
if (!replaceSafely(refPath, clone(init))) {
|
||||
replacedAll = false;
|
||||
return;
|
||||
}
|
||||
replacedAny = true;
|
||||
});
|
||||
if (replacedAny) {
|
||||
changed = true;
|
||||
}
|
||||
if (replacedAll) {
|
||||
path.remove();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (t.isSequenceExpression(init) && init.expressions.length > 0 && init.expressions.every(expr => t.isStringLiteral(expr))) {
|
||||
path.get("init").replaceWith(clone(init.expressions[init.expressions.length - 1]));
|
||||
changed = true;
|
||||
return;
|
||||
}
|
||||
if (t.isArrayExpression(init) && init.elements.length > 0 && init.elements.every(el => el && isPrimitiveNode(el))) {
|
||||
let replacedAll = binding.referencePaths.length > 0;
|
||||
let replacedCount = 0;
|
||||
binding.referencePaths.forEach(refPath => {
|
||||
const parent = refPath.parentPath;
|
||||
if (!parent.isMemberExpression({ object: refPath.node }) || !parent.get("property").isNumericLiteral()) {
|
||||
replacedAll = false;
|
||||
return;
|
||||
}
|
||||
const index = parent.node.property.value;
|
||||
if (!init.elements[index]) {
|
||||
replacedAll = false;
|
||||
return;
|
||||
}
|
||||
if (!replaceSafely(parent, clone(init.elements[index]))) {
|
||||
replacedAll = false;
|
||||
return;
|
||||
}
|
||||
replacedCount += 1;
|
||||
changed = true;
|
||||
});
|
||||
if (replacedAll && replacedCount > 0) {
|
||||
path.remove();
|
||||
}
|
||||
}
|
||||
},
|
||||
AssignmentExpression(path) {
|
||||
if (!path.get("left").isIdentifier() || path.node.operator !== "=") {
|
||||
return;
|
||||
}
|
||||
if (!path.parentPath.isExpressionStatement()) {
|
||||
return;
|
||||
}
|
||||
const binding = path.scope.getBinding(path.node.left.name);
|
||||
if (!binding) {
|
||||
return;
|
||||
}
|
||||
if (typeof path.node.start === "number" && !allRefsAfter(binding, path.node.start)) {
|
||||
return;
|
||||
}
|
||||
if (path.get("right").isStringLiteral() || path.get("right").isNumericLiteral() || path.get("right").isBooleanLiteral()) {
|
||||
const right = clone(path.node.right);
|
||||
let replacedAll = binding.referencePaths.length > 0;
|
||||
let replacedAny = false;
|
||||
binding.referencePaths.forEach(refPath => {
|
||||
if (!replaceSafely(refPath, clone(right))) {
|
||||
replacedAll = false;
|
||||
return;
|
||||
}
|
||||
replacedAny = true;
|
||||
});
|
||||
if (replacedAny) {
|
||||
changed = true;
|
||||
}
|
||||
if (replacedAll) {
|
||||
path.remove();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (path.get("right").isArrayExpression() && path.node.right.elements.every(el => el && isPrimitiveNode(el))) {
|
||||
const elements = path.node.right.elements;
|
||||
let replacedAll = binding.referencePaths.length > 0;
|
||||
let replacedCount = 0;
|
||||
binding.referencePaths.forEach(refPath => {
|
||||
const parent = refPath.parentPath;
|
||||
if (!parent.isMemberExpression({ object: refPath.node }) || !parent.get("property").isNumericLiteral()) {
|
||||
replacedAll = false;
|
||||
return;
|
||||
}
|
||||
const index = parent.node.property.value;
|
||||
if (!elements[index]) {
|
||||
replacedAll = false;
|
||||
return;
|
||||
}
|
||||
if (!replaceSafely(parent, clone(elements[index]))) {
|
||||
replacedAll = false;
|
||||
return;
|
||||
}
|
||||
replacedCount += 1;
|
||||
changed = true;
|
||||
});
|
||||
if (replacedAll && replacedCount > 0) {
|
||||
path.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { ast, changed };
|
||||
}
|
||||
|
||||
const { inputPath, outputPath } = parseArgs();
|
||||
let ast = parseFile(inputPath);
|
||||
let changed = false;
|
||||
do {
|
||||
({ ast, changed } = inlineLiterals(ast));
|
||||
if (changed) {
|
||||
ast = reparse(ast);
|
||||
}
|
||||
} while (changed);
|
||||
saveAst(ast, outputPath);
|
||||
Reference in New Issue
Block a user