Skip to content

Commit 54a6953

Browse files
committed
Rewrite Moved Declarations to obfuscate much faster
1 parent 52c413f commit 54a6953

File tree

2 files changed

+59
-105
lines changed

2 files changed

+59
-105
lines changed
Lines changed: 50 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
import Transform from "../transform";
2-
import { isBlock, walk } from "../../traverse";
2+
import { isBlock } from "../../traverse";
33
import {
4-
Location,
54
ExpressionStatement,
65
AssignmentExpression,
76
Identifier,
87
Node,
98
VariableDeclarator,
10-
VariableDeclaration,
119
} from "../../util/gen";
1210
import { isForInitialize, prepend } from "../../util/insert";
1311
import { ok } from "assert";
1412
import { ObfuscateOrder } from "../../order";
15-
import { getIdentifierInfo } from "../../util/identifiers";
16-
import { isLexicalScope, getLexicalScope } from "../../util/scope";
1713

1814
/**
1915
* Defines all the names at the top of every lexical block.
@@ -24,110 +20,68 @@ export default class MovedDeclarations extends Transform {
2420
}
2521

2622
match(object, parents) {
27-
return isLexicalScope(object);
23+
return (
24+
object.type === "VariableDeclaration" &&
25+
object.kind === "var" &&
26+
object.declarations.length === 1 &&
27+
object.declarations[0].id.type === "Identifier"
28+
);
2829
}
2930

3031
transform(object: Node, parents: Node[]) {
3132
return () => {
32-
var body = isBlock(object) ? object.body : object.consequent;
33-
ok(Array.isArray(body));
34-
35-
var illegal = new Set<string>();
36-
var defined = new Set<string>();
37-
var variableDeclarations: {
38-
[name: string]: {
39-
location: Location;
40-
replace: Node;
33+
var forInitializeType = isForInitialize(object, parents);
34+
35+
// Get the block statement or Program node
36+
var blockIndex = parents.findIndex((x) => isBlock(x));
37+
var block = parents[blockIndex];
38+
var body = block.body;
39+
var bodyObject = parents[blockIndex - 2] || object;
40+
41+
// Make sure in the block statement, and not already at the top of it
42+
var index = body.indexOf(bodyObject);
43+
if (index === -1 || index === 0) return;
44+
45+
var topVariableDeclaration;
46+
if (body[0].type === "VariableDeclaration" && body[0].kind === "var") {
47+
topVariableDeclaration = body[0];
48+
} else {
49+
topVariableDeclaration = {
50+
type: "VariableDeclaration",
51+
declarations: [],
52+
kind: "var",
4153
};
42-
} = Object.create(null);
43-
44-
walk(object, parents, (o, p) => {
45-
if (o.type == "Identifier") {
46-
if (o.hidden || getLexicalScope(o, p) !== object) {
47-
illegal.add(o.name);
48-
} else {
49-
var info = getIdentifierInfo(o, p);
50-
if (!info.spec.isReferenced) {
51-
return;
52-
}
5354

54-
if (info.spec.isDefined) {
55-
if (info.isFunctionDeclaration || info.isClassDeclaration) {
56-
illegal.add(o.name);
57-
} else {
58-
if (defined.has(o.name)) {
59-
illegal.add(o.name);
60-
} else {
61-
defined.add(o.name);
62-
}
63-
}
64-
}
65-
}
66-
}
55+
prepend(block, topVariableDeclaration);
56+
}
6757

68-
if (o.type == "VariableDeclaration") {
69-
return () => {
70-
if (
71-
o.declarations.length === 1 &&
72-
o.declarations[0].id.type === "Identifier"
73-
) {
74-
var name = o.declarations[0].id.name;
58+
var varName = object.declarations[0].id.name;
59+
ok(typeof varName === "string");
7560

76-
// Check if duplicate
77-
if (variableDeclarations[name] || o.kind !== "var") {
78-
illegal.add(name);
79-
return;
80-
}
61+
// Add `var x` at the top of the block
62+
topVariableDeclaration.declarations.push(
63+
VariableDeclarator(Identifier(varName))
64+
);
8165

82-
// Check if already at top
83-
if (body[0] === o) {
84-
illegal.add(name);
85-
return;
86-
}
66+
var assignmentExpression = AssignmentExpression(
67+
"=",
68+
Identifier(varName),
69+
object.declarations[0].init || Identifier("undefined")
70+
);
8771

88-
var replace: Node = AssignmentExpression(
89-
"=",
90-
Identifier(name),
91-
o.declarations[0].init || Identifier("undefined")
92-
);
72+
if (forInitializeType) {
73+
if (forInitializeType === "initializer") {
74+
// Replace `for (var i = 0...)` to `for (i = 0...)`
75+
this.replace(object, assignmentExpression);
76+
} else if (forInitializeType === "left-hand") {
77+
// Replace `for (var k in...)` to `for (k in ...)`
9378

94-
var forType = isForInitialize(o, p);
95-
if (forType === "left-hand") {
96-
replace = Identifier(name);
97-
} else if (!forType) {
98-
replace = ExpressionStatement(replace);
99-
}
100-
variableDeclarations[name] = {
101-
location: [o, p],
102-
replace: replace,
103-
};
104-
}
105-
};
79+
this.replace(object, Identifier(varName));
10680
}
107-
});
108-
109-
illegal.forEach((name) => {
110-
delete variableDeclarations[name];
111-
});
112-
113-
var movingNames = Object.keys(variableDeclarations);
114-
115-
if (movingNames.length === 0) {
116-
return;
81+
} else {
82+
// Replace `var x = value` to `x = value`
83+
this.replace(object, ExpressionStatement(assignmentExpression));
11784
}
118-
119-
var variableDeclaration = VariableDeclaration(
120-
movingNames.map((name) => {
121-
return VariableDeclarator(name);
122-
})
123-
);
124-
125-
prepend(object, variableDeclaration);
126-
127-
movingNames.forEach((name) => {
128-
var { location, replace } = variableDeclarations[name];
129-
this.replace(location[0], replace);
130-
});
13185
};
13286
}
13387
}

test/transforms/identifier/movedDeclarations.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ test("Variant #1: Move variable 'y' to top", async () => {
1212
movedDeclarations: true,
1313
});
1414

15-
expect(output).toContain("var y;");
15+
expect(output).toContain("var x=10,y;");
1616
expect(output).toContain("y=15");
1717

1818
var TEST_VARIABLE;
@@ -34,7 +34,7 @@ test("Variant #2: Move variable 'y' and 'z' to top", async () => {
3434
movedDeclarations: true,
3535
});
3636

37-
expect(output).toContain("var y,z");
37+
expect(output).toContain("var x=10,y,z;");
3838
expect(output).toContain("y=15");
3939
expect(output).toContain("z=5");
4040

@@ -44,7 +44,7 @@ test("Variant #2: Move variable 'y' and 'z' to top", async () => {
4444
expect(TEST_VARIABLE).toStrictEqual(30);
4545
});
4646

47-
test("Variant #2: Don't move 'y' (destructuring)", async () => {
47+
test("Variant #3: Don't move 'y' (destructuring)", async () => {
4848
var code = `
4949
var x = 10;
5050
var [y] = [15];
@@ -64,7 +64,7 @@ test("Variant #2: Don't move 'y' (destructuring)", async () => {
6464
expect(TEST_VARIABLE).toStrictEqual(25);
6565
});
6666

67-
test("Variant #3: Don't move 'y' (nested lexical scope)", async () => {
67+
test("Variant #4: Move 'y' (nested lexical scope)", async () => {
6868
var code = `
6969
var x = 10;
7070
var y = 15;
@@ -81,15 +81,15 @@ test("Variant #3: Don't move 'y' (nested lexical scope)", async () => {
8181
movedDeclarations: true,
8282
});
8383

84-
expect(output).toContain("var y=15");
84+
expect(output).toContain("var x=10,y;");
8585

8686
var TEST_VARIABLE;
8787
eval(output);
8888

8989
expect(TEST_VARIABLE).toStrictEqual(20);
9090
});
9191

92-
test("Variant #4: Move 'y' (for statement initializer)", async () => {
92+
test("Variant #5: Move 'y' (for statement initializer)", async () => {
9393
var code = `
9494
var x = 10;
9595
for ( var y = 0; y < 15; y++ ) {
@@ -111,7 +111,7 @@ test("Variant #4: Move 'y' (for statement initializer)", async () => {
111111
expect(TEST_VARIABLE).toStrictEqual(25);
112112
});
113113

114-
test("Variant #5: Move 'y' (for-in left-hand initializer)", async () => {
114+
test("Variant #6: Move 'y' (for-in left-hand initializer)", async () => {
115115
var code = `
116116
var x = 10;
117117
for ( var y in [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] ) {
@@ -134,7 +134,7 @@ test("Variant #5: Move 'y' (for-in left-hand initializer)", async () => {
134134
expect(TEST_VARIABLE).toStrictEqual(25);
135135
});
136136

137-
test("Variant #6: Don't move const or let variables", async () => {
137+
test("Variant #7: Don't move const or let variables", async () => {
138138
var code = `
139139
var fillerExpr;
140140
@@ -158,7 +158,7 @@ test("Variant #6: Don't move const or let variables", async () => {
158158
expect(TEST_VARIABLE).toStrictEqual(25);
159159
});
160160

161-
test("Variant #7: Work with 'use strict'", async () => {
161+
test("Variant #8: Work with 'use strict'", async () => {
162162
var code = `
163163
function myFunction(){
164164
'use strict';

0 commit comments

Comments
 (0)