Skip to content

Commit

Permalink
perf: optimize unminify
Browse files Browse the repository at this point in the history
up to 14x faster string merging
  • Loading branch information
j4k0xb committed Apr 19, 2024
1 parent d042684 commit 78f8e30
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ test('keep divisions if it results in a decimal number', () =>
'(-0x152f + 0x1281 * -0x1 + -0x18 * -0x1d1) / (0x83 * -0x1a + -0x19ea + 0x5f * 0x6a)',
).toMatchInlineSnapshot('1000 / 30;'));

test('ignore string results', () =>
expectJS('0x1021e + "test"').toMatchInlineSnapshot('0x1021e + "test";'));
test('string and number concatenation', () =>
expectJS('0x1021e + "test"').toMatchInlineSnapshot('"66078test";'));
4 changes: 1 addition & 3 deletions packages/webcrack/src/unminify/transforms/for-to-while.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ export default {
const { test, body, init, update } = path.node;
if (init || update) return;
path.replaceWith(
test
? t.whileStatement(test, body)
: t.whileStatement(t.booleanLiteral(true), body),
t.whileStatement(test ?? t.booleanLiteral(true), body),
);
this.changes++;
},
Expand Down
11 changes: 4 additions & 7 deletions packages/webcrack/src/unminify/transforms/logical-to-if.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import { statement } from '@babel/template';
import type * as t from '@babel/types';
import * as m from '@codemod/matchers';
import * as t from '@babel/types';
import type { Transform } from '../../ast-utils';

export default {
name: 'logical-to-if',
tags: ['safe'],
visitor: () => {
const andMatcher = m.expressionStatement(m.logicalExpression('&&'));
const orMatcher = m.expressionStatement(m.logicalExpression('||'));

const buildIf = statement`if (TEST) { BODY; }`;
const buildIfNot = statement`if (!TEST) { BODY; }`;

return {
ExpressionStatement: {
exit(path) {
const expression = path.node.expression as t.LogicalExpression;
if (andMatcher.match(path.node)) {
if (!t.isLogicalExpression(expression)) return;
if (expression.operator === '&&') {
path.replaceWith(
buildIf({
TEST: expression.left,
BODY: expression.right,
}),
);
this.changes++;
} else if (orMatcher.match(path.node)) {
} else if (expression.operator === '||') {
path.replaceWith(
buildIfNot({
TEST: expression.left,
Expand Down
37 changes: 13 additions & 24 deletions packages/webcrack/src/unminify/transforms/merge-strings.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,31 @@
import * as t from '@babel/types';
import * as m from '@codemod/matchers';
import type { Transform } from '../../ast-utils';

// "a" + "b" -> "ab"
// (a + "b") + "c" -> a + "bc"
// left ^ ^ right (path)
export default {
name: 'merge-strings',
tags: ['safe'],
visitor() {
const left = m.capture(m.stringLiteral(m.anyString()));
const right = m.capture(m.stringLiteral(m.anyString()));
const left = m.capture(m.stringLiteral());
const right = m.capture(m.stringLiteral());

const matcher = m.binaryExpression('+', left, right);
const nestedMatcher = m.binaryExpression(
const matcher = m.binaryExpression(
'+',
m.binaryExpression('+', m.anything(), left),
m.or(left, m.binaryExpression('+', m.anything(), left)),
right,
);

return {
BinaryExpression: {
exit(path) {
if (matcher.match(path.node)) {
// "a" + "b" -> "ab"
path.replaceWith(
t.stringLiteral(left.current!.value + right.current!.value),
);
this.changes++;
}
},
},
StringLiteral: {
exit(path) {
if (nestedMatcher.match(path.parent)) {
// (a + "b") + "c" -> a + "bc"
// left ^ ^ right (path)
left.current!.value += right.current!.value;
path.remove();
this.changes++;
}
if (!matcher.match(path.node)) return;
left.current!.value += right.current!.value;
right.current!.value = ''; // Otherwise it concatenates multiple times for some reason
path.replaceWith(path.node.left);
path.skip();
this.changes++;
},
},
};
Expand Down
50 changes: 17 additions & 33 deletions packages/webcrack/src/unminify/transforms/number-expressions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,35 @@ export default {
visitor: () => ({
'BinaryExpression|UnaryExpression': {
exit(path) {
if (matcher.match(path.node)) {
const evaluated = path.evaluate();
if (evaluated.confident) {
// Heuristic: Simplifying a division that results in a non-integer probably doesn't increase readability
if (
path.node.type === 'BinaryExpression' &&
path.node.operator === '/' &&
!Number.isInteger(evaluated.value)
) {
return;
}

path.replaceWith(t.valueToNode(evaluated.value));
path.skip();
this.changes++;
}
if (!matcher.match(path.node)) return;
const evaluated = path.evaluate();
if (
t.isBinaryExpression(path.node, { operator: '/' }) &&
!Number.isInteger(evaluated.value)
) {
return;
}
path.replaceWith(t.valueToNode(evaluated.value));
path.skip();
this.changes++;
},
},
}),
} satisfies Transform;

const matcher: m.Matcher<t.Expression> = m.or(
m.binaryExpression(
m.or('+', '-', '*', '/'),
m.matcher((node) => matcher.match(node)),
m.matcher((node) => matcher.match(node)),
),
const matcher = m.or(
m.unaryExpression('-', m.or(m.stringLiteral(), m.numericLiteral())),
m.binaryExpression(
'-',
m.or('+', '-', '/', '%', '*', '**', '&', '|', '>>', '>>>', '<<', '^'),
m.or(
m.stringLiteral(),
m.matcher((node) => matcher.match(node)),
m.numericLiteral(),
m.unaryExpression('-', m.numericLiteral()),
),
m.or(
m.stringLiteral(),
m.matcher((node) => matcher.match(node)),
),
),
m.unaryExpression(
'-',
m.or(
m.stringLiteral(),
m.matcher((node) => matcher.match(node)),
m.numericLiteral(),
m.unaryExpression('-', m.numericLiteral()),
),
),
m.numericLiteral(),
);

0 comments on commit 78f8e30

Please sign in to comment.