js-confuser 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +215 -170
- package/dist/constants.js +6 -2
- package/dist/obfuscator.js +0 -6
- package/dist/options.js +4 -4
- package/dist/presets.js +6 -7
- package/dist/templates/crash.js +2 -2
- package/dist/templates/functionLength.js +16 -0
- package/dist/transforms/dispatcher.js +4 -1
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +89 -58
- package/dist/transforms/flatten.js +224 -147
- package/dist/transforms/identifier/movedDeclarations.js +38 -85
- package/dist/transforms/identifier/renameVariables.js +94 -41
- package/dist/transforms/lock/lock.js +0 -37
- package/dist/transforms/minify.js +2 -2
- package/dist/transforms/rgf.js +139 -246
- package/dist/transforms/stack.js +42 -1
- package/dist/transforms/transform.js +1 -1
- package/dist/util/gen.js +2 -1
- package/dist/util/identifiers.js +37 -3
- package/dist/util/insert.js +24 -3
- package/docs/ControlFlowFlattening.md +595 -0
- package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
- package/{Integrity.md → docs/Integrity.md} +2 -2
- package/docs/RGF.md +419 -0
- package/package.json +1 -1
- package/src/constants.ts +3 -0
- package/src/obfuscator.ts +0 -4
- package/src/options.ts +9 -86
- package/src/presets.ts +6 -7
- package/src/templates/crash.ts +10 -10
- package/src/templates/functionLength.ts +14 -0
- package/src/transforms/dispatcher.ts +5 -1
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +130 -129
- package/src/transforms/flatten.ts +357 -290
- package/src/transforms/identifier/movedDeclarations.ts +50 -96
- package/src/transforms/identifier/renameVariables.ts +120 -56
- package/src/transforms/lock/lock.ts +1 -42
- package/src/transforms/minify.ts +11 -2
- package/src/transforms/rgf.ts +214 -404
- package/src/transforms/stack.ts +62 -0
- package/src/transforms/transform.ts +6 -2
- package/src/util/gen.ts +7 -2
- package/src/util/identifiers.ts +43 -2
- package/src/util/insert.ts +26 -2
- package/test/code/ES6.src.js +24 -0
- package/test/transforms/flatten.test.ts +352 -88
- package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
- package/test/transforms/identifier/renameVariables.test.ts +37 -0
- package/test/transforms/lock/lock.test.ts +1 -48
- package/test/transforms/minify.test.ts +19 -0
- package/test/transforms/rgf.test.ts +262 -353
- package/test/transforms/stack.test.ts +52 -0
- package/test/util/identifiers.test.ts +113 -1
- package/test/util/insert.test.ts +57 -3
- package/src/transforms/eval.ts +0 -89
- package/src/transforms/identifier/nameRecycling.ts +0 -280
- package/test/transforms/eval.test.ts +0 -131
- package/test/transforms/identifier/nameRecycling.test.ts +0 -205
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import Transform from "../transform";
|
|
2
|
-
import { isBlock
|
|
2
|
+
import { isBlock } from "../../traverse";
|
|
3
3
|
import {
|
|
4
|
-
Location,
|
|
5
4
|
ExpressionStatement,
|
|
6
5
|
AssignmentExpression,
|
|
7
6
|
Identifier,
|
|
8
7
|
Node,
|
|
9
8
|
VariableDeclarator,
|
|
10
|
-
VariableDeclaration,
|
|
11
9
|
} from "../../util/gen";
|
|
12
10
|
import { isForInitialize, prepend } from "../../util/insert";
|
|
13
11
|
import { ok } from "assert";
|
|
14
12
|
import { ObfuscateOrder } from "../../order";
|
|
15
|
-
import { getIdentifierInfo } from "../../util/identifiers";
|
|
16
|
-
import { isLexicalScope, getLexicalScope } from "../../util/scope";
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Defines all the names at the top of every lexical block.
|
|
@@ -24,110 +20,68 @@ export default class MovedDeclarations extends Transform {
|
|
|
24
20
|
}
|
|
25
21
|
|
|
26
22
|
match(object, parents) {
|
|
27
|
-
return
|
|
23
|
+
return (
|
|
24
|
+
object.type === "VariableDeclaration" &&
|
|
25
|
+
object.kind === "var" &&
|
|
26
|
+
object.declarations.length === 1 &&
|
|
27
|
+
object.declarations[0].id.type === "Identifier"
|
|
28
|
+
);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
transform(object: Node, parents: Node[]) {
|
|
31
32
|
return () => {
|
|
32
|
-
var
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var
|
|
36
|
-
var
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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",
|
|
41
53
|
};
|
|
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
|
-
}
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
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
|
+
}
|
|
67
57
|
|
|
68
|
-
|
|
69
|
-
|
|
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");
|
|
75
60
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
61
|
+
// Add `var x` at the top of the block
|
|
62
|
+
topVariableDeclaration.declarations.push(
|
|
63
|
+
VariableDeclarator(Identifier(varName))
|
|
64
|
+
);
|
|
81
65
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
66
|
+
var assignmentExpression = AssignmentExpression(
|
|
67
|
+
"=",
|
|
68
|
+
Identifier(varName),
|
|
69
|
+
object.declarations[0].init || Identifier(varName)
|
|
70
|
+
);
|
|
87
71
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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 ...)`
|
|
93
78
|
|
|
94
|
-
|
|
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));
|
|
106
80
|
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
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));
|
|
117
84
|
}
|
|
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
|
-
});
|
|
131
85
|
};
|
|
132
86
|
}
|
|
133
87
|
}
|
|
@@ -8,9 +8,14 @@ import {
|
|
|
8
8
|
isContext,
|
|
9
9
|
isLexContext,
|
|
10
10
|
clone,
|
|
11
|
+
isFunction,
|
|
11
12
|
} from "../../util/insert";
|
|
12
13
|
import Transform from "../transform";
|
|
13
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
noRenameVariablePrefix,
|
|
16
|
+
placeholderVariablePrefix,
|
|
17
|
+
reservedIdentifiers,
|
|
18
|
+
} from "../../constants";
|
|
14
19
|
import { ComputeProbabilityMap } from "../../probability";
|
|
15
20
|
import VariableAnalysis from "./variableAnalysis";
|
|
16
21
|
|
|
@@ -33,6 +38,9 @@ export default class RenameVariables extends Transform {
|
|
|
33
38
|
// Ref to VariableAnalysis data
|
|
34
39
|
variableAnalysis: VariableAnalysis;
|
|
35
40
|
|
|
41
|
+
// Option to re-use previously generated names
|
|
42
|
+
reusePreviousNames = true;
|
|
43
|
+
|
|
36
44
|
constructor(o) {
|
|
37
45
|
super(o, ObfuscateOrder.RenameVariables);
|
|
38
46
|
|
|
@@ -45,10 +53,10 @@ export default class RenameVariables extends Transform {
|
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
match(object: Node, parents: Node[]) {
|
|
48
|
-
return isContext(object);
|
|
56
|
+
return isContext(object) || object.type === "Identifier";
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
transformContext(object: Node, parents: Node[]) {
|
|
52
60
|
// 2. Notice this is on 'onEnter' (top-down)
|
|
53
61
|
var isGlobal = object.type == "Program";
|
|
54
62
|
var type = isGlobal
|
|
@@ -76,7 +84,7 @@ export default class RenameVariables extends Transform {
|
|
|
76
84
|
var possible = new Set<string>();
|
|
77
85
|
|
|
78
86
|
// 3. Try to re-use names when possible
|
|
79
|
-
if (this.generated.length && !isGlobal) {
|
|
87
|
+
if (this.reusePreviousNames && this.generated.length && !isGlobal) {
|
|
80
88
|
var allReferences = new Set<string>();
|
|
81
89
|
var nope = new Set(defined);
|
|
82
90
|
walk(object, [], (o, p) => {
|
|
@@ -115,8 +123,8 @@ export default class RenameVariables extends Transform {
|
|
|
115
123
|
// 4. Defined names to new names
|
|
116
124
|
for (var name of defined) {
|
|
117
125
|
if (
|
|
118
|
-
!name.startsWith(
|
|
119
|
-
(isGlobal && !name.startsWith(
|
|
126
|
+
!name.startsWith(noRenameVariablePrefix) && // Variables prefixed with '__NO_JS_CONFUSER_RENAME__' are never renamed
|
|
127
|
+
(isGlobal && !name.startsWith(placeholderVariablePrefix) // Variables prefixed with '__p_' are created by the obfuscator, always renamed
|
|
120
128
|
? ComputeProbabilityMap(this.options.renameGlobals, (x) => x, name)
|
|
121
129
|
: true) &&
|
|
122
130
|
ComputeProbabilityMap(
|
|
@@ -151,71 +159,127 @@ export default class RenameVariables extends Transform {
|
|
|
151
159
|
}
|
|
152
160
|
}
|
|
153
161
|
|
|
162
|
+
// console.log(object.type, newNames);
|
|
154
163
|
this.changed.set(object, newNames);
|
|
164
|
+
}
|
|
155
165
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
166
|
+
transformIdentifier(object: Node, parents: Node[]) {
|
|
167
|
+
const identifierName = object.name;
|
|
168
|
+
if (
|
|
169
|
+
reservedIdentifiers.has(identifierName) ||
|
|
170
|
+
this.options.globalVariables.has(identifierName)
|
|
171
|
+
) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
165
174
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
175
|
+
if (object.$renamed) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
169
178
|
|
|
170
|
-
|
|
179
|
+
var info = getIdentifierInfo(object, parents);
|
|
171
180
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
181
|
+
if (info.spec.isExported) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
175
184
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
185
|
+
if (!info.spec.isReferenced) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
var contexts = [object, ...parents].filter((x) => isContext(x));
|
|
190
|
+
var newName = null;
|
|
191
|
+
|
|
192
|
+
// Function default parameter check!
|
|
193
|
+
var functionIndices = [];
|
|
194
|
+
for (var i in parents) {
|
|
195
|
+
if (isFunction(parents[i])) {
|
|
196
|
+
functionIndices.push(i);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
for (var functionIndex of functionIndices) {
|
|
201
|
+
if (parents[functionIndex].id === object) {
|
|
202
|
+
// This context is not referenced, so remove it
|
|
203
|
+
contexts = contexts.filter(
|
|
204
|
+
(context) => context != parents[functionIndex]
|
|
205
|
+
);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (parents[functionIndex].params === parents[functionIndex - 1]) {
|
|
209
|
+
var isReferencedHere = true;
|
|
179
210
|
|
|
180
|
-
var
|
|
181
|
-
var
|
|
211
|
+
var slicedParents = parents.slice(0, functionIndex);
|
|
212
|
+
var forIndex = 0;
|
|
213
|
+
for (var parent of slicedParents) {
|
|
214
|
+
var childNode = slicedParents[forIndex - 1] || object;
|
|
182
215
|
|
|
183
|
-
for (var check of contexts) {
|
|
184
216
|
if (
|
|
185
|
-
|
|
186
|
-
|
|
217
|
+
parent.type === "AssignmentPattern" &&
|
|
218
|
+
parent.right === childNode
|
|
187
219
|
) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
break;
|
|
191
|
-
}
|
|
220
|
+
isReferencedHere = false;
|
|
221
|
+
break;
|
|
192
222
|
}
|
|
223
|
+
|
|
224
|
+
forIndex++;
|
|
193
225
|
}
|
|
194
226
|
|
|
195
|
-
if (
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
p[importSpecifierIndex].imported ===
|
|
204
|
-
(p[importSpecifierIndex - 1] || o) &&
|
|
205
|
-
p[importSpecifierIndex].imported &&
|
|
206
|
-
p[importSpecifierIndex].imported.type === "Identifier"
|
|
207
|
-
) {
|
|
208
|
-
p[importSpecifierIndex].imported = clone(
|
|
209
|
-
p[importSpecifierIndex - 1] || o
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
227
|
+
if (!isReferencedHere) {
|
|
228
|
+
// This context is not referenced, so remove it
|
|
229
|
+
contexts = contexts.filter(
|
|
230
|
+
(context) => context != parents[functionIndex]
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
213
235
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
236
|
+
for (var check of contexts) {
|
|
237
|
+
if (
|
|
238
|
+
this.variableAnalysis.defined.has(check) &&
|
|
239
|
+
this.variableAnalysis.defined.get(check).has(identifierName)
|
|
240
|
+
) {
|
|
241
|
+
if (
|
|
242
|
+
this.changed.has(check) &&
|
|
243
|
+
this.changed.get(check)[identifierName]
|
|
244
|
+
) {
|
|
245
|
+
newName = this.changed.get(check)[identifierName];
|
|
246
|
+
break;
|
|
217
247
|
}
|
|
218
248
|
}
|
|
219
|
-
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (newName && typeof newName === "string") {
|
|
252
|
+
// Strange behavior where the `local` and `imported` objects are the same
|
|
253
|
+
if (info.isImportSpecifier) {
|
|
254
|
+
var importSpecifierIndex = parents.findIndex(
|
|
255
|
+
(x) => x.type === "ImportSpecifier"
|
|
256
|
+
);
|
|
257
|
+
if (
|
|
258
|
+
importSpecifierIndex != -1 &&
|
|
259
|
+
parents[importSpecifierIndex].imported ===
|
|
260
|
+
(parents[importSpecifierIndex - 1] || object) &&
|
|
261
|
+
parents[importSpecifierIndex].imported &&
|
|
262
|
+
parents[importSpecifierIndex].imported.type === "Identifier"
|
|
263
|
+
) {
|
|
264
|
+
parents[importSpecifierIndex].imported = clone(
|
|
265
|
+
parents[importSpecifierIndex - 1] || object
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// console.log(o.name, "->", newName);
|
|
271
|
+
// 5. Update Identifier node's 'name' property
|
|
272
|
+
object.name = newName;
|
|
273
|
+
object.$renamed = true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
transform(object: Node, parents: Node[]) {
|
|
278
|
+
var matchType = object.type === "Identifier" ? "Identifier" : "Context";
|
|
279
|
+
if (matchType === "Identifier") {
|
|
280
|
+
this.transformIdentifier(object, parents);
|
|
281
|
+
} else {
|
|
282
|
+
this.transformContext(object, parents);
|
|
283
|
+
}
|
|
220
284
|
}
|
|
221
285
|
}
|
|
@@ -219,9 +219,7 @@ export default class Lock extends Transform {
|
|
|
219
219
|
if (this.options.lock.domainLock && this.options.lock.domainLock.length) {
|
|
220
220
|
choices.push("domainLock");
|
|
221
221
|
}
|
|
222
|
-
|
|
223
|
-
choices.push("nativeFunction");
|
|
224
|
-
}
|
|
222
|
+
|
|
225
223
|
if (this.options.lock.context && this.options.lock.context.length) {
|
|
226
224
|
choices.push("context");
|
|
227
225
|
}
|
|
@@ -312,45 +310,6 @@ export default class Lock extends Transform {
|
|
|
312
310
|
|
|
313
311
|
break;
|
|
314
312
|
|
|
315
|
-
case "nativeFunction":
|
|
316
|
-
var set = this.options.lock.nativeFunctions;
|
|
317
|
-
if (set === true) {
|
|
318
|
-
if (this.options.target == "node") {
|
|
319
|
-
set = new Set(["Function", "String"]);
|
|
320
|
-
} else {
|
|
321
|
-
set = new Set(["Function", "String", "fetch"]);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
if (Array.isArray(set)) {
|
|
325
|
-
set = new Set(set);
|
|
326
|
-
}
|
|
327
|
-
if (!set) {
|
|
328
|
-
set = new Set();
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
var fn = choice(Array.from(set));
|
|
332
|
-
if (fn) {
|
|
333
|
-
test = Template(
|
|
334
|
-
`(${fn}+"").indexOf("[native code]") == -1`
|
|
335
|
-
).single().expression;
|
|
336
|
-
|
|
337
|
-
if (Math.random() > 0.5) {
|
|
338
|
-
test = Template(
|
|
339
|
-
`${fn}.toString().split("{ [native code] }").length <= 1`
|
|
340
|
-
).single().expression;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
nodes.push(
|
|
344
|
-
IfStatement(
|
|
345
|
-
test,
|
|
346
|
-
this.getCounterMeasuresCode(object, parents) || [],
|
|
347
|
-
null
|
|
348
|
-
)
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
break;
|
|
353
|
-
|
|
354
313
|
case "startDate":
|
|
355
314
|
test = BinaryExpression(
|
|
356
315
|
"<",
|
package/src/transforms/minify.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
isForInitialize,
|
|
23
23
|
append,
|
|
24
24
|
isVarContext,
|
|
25
|
+
computeFunctionLength,
|
|
25
26
|
} from "../util/insert";
|
|
26
27
|
import { isValidIdentifier, isEquivalent } from "../util/compare";
|
|
27
28
|
import { walk, isBlock } from "../traverse";
|
|
@@ -258,8 +259,15 @@ export default class Minify extends Transform {
|
|
|
258
259
|
append(
|
|
259
260
|
parents[parents.length - 1] || object,
|
|
260
261
|
Template(`
|
|
261
|
-
function ${this.arrowFunctionName}(arrowFn){
|
|
262
|
-
|
|
262
|
+
function ${this.arrowFunctionName}(arrowFn, functionLength){
|
|
263
|
+
var functionObject = function(){ return arrowFn(...arguments) };
|
|
264
|
+
|
|
265
|
+
Object["defineProperty"](functionObject, "length", {
|
|
266
|
+
"value": functionLength,
|
|
267
|
+
"configurable": true
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return functionObject;
|
|
263
271
|
}
|
|
264
272
|
`).single()
|
|
265
273
|
);
|
|
@@ -268,6 +276,7 @@ export default class Minify extends Transform {
|
|
|
268
276
|
const wrap = (object: Node) => {
|
|
269
277
|
return CallExpression(Identifier(this.arrowFunctionName), [
|
|
270
278
|
clone(object),
|
|
279
|
+
Literal(computeFunctionLength(object.params)),
|
|
271
280
|
]);
|
|
272
281
|
};
|
|
273
282
|
|