js-confuser 1.6.0 → 1.7.1
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 +42 -0
- package/README.md +215 -172
- 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 +10 -1
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +95 -59
- package/dist/transforms/extraction/objectExtraction.js +6 -1
- 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 +145 -244
- 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 +38 -4
- package/dist/util/insert.js +24 -3
- package/docs/ControlFlowFlattening.md +595 -0
- package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
- package/docs/ES5.md +197 -0
- package/{Integrity.md → docs/Integrity.md} +2 -2
- package/docs/RGF.md +419 -0
- package/package.json +2 -2
- 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 +15 -1
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +135 -130
- package/src/transforms/extraction/objectExtraction.ts +4 -0
- 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 +221 -402
- 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 +48 -4
- package/src/util/insert.ts +26 -2
- package/test/code/ES6.src.js +24 -0
- package/test/transforms/dispatcher.test.ts +27 -0
- package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +21 -8
- package/test/transforms/extraction/objectExtraction.test.ts +35 -15
- package/test/transforms/flatten.test.ts +352 -88
- package/test/transforms/identifier/globalConcealing.test.ts +23 -2
- package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
- package/test/transforms/identifier/renameVariables.test.ts +37 -0
- package/test/transforms/lock/integrity.test.ts +24 -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 +134 -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
package/src/options.ts
CHANGED
|
@@ -141,39 +141,6 @@ export interface ObfuscateOptions {
|
|
|
141
141
|
"hexadecimal" | "randomized" | "zeroWidth" | "mangled" | "number"
|
|
142
142
|
>;
|
|
143
143
|
|
|
144
|
-
/**
|
|
145
|
-
* ### `nameRecycling`
|
|
146
|
-
*
|
|
147
|
-
* (Experimental feature)
|
|
148
|
-
*
|
|
149
|
-
* Attempts to reuse released names.
|
|
150
|
-
*
|
|
151
|
-
* - Potency Medium
|
|
152
|
-
* - Resilience High
|
|
153
|
-
* - Cost Low
|
|
154
|
-
*
|
|
155
|
-
* ```js
|
|
156
|
-
* // Input
|
|
157
|
-
* function percentage(x) {
|
|
158
|
-
* var multiplied = x * 100;
|
|
159
|
-
* var floored = Math.floor(multiplied);
|
|
160
|
-
* var output = floored + "%"
|
|
161
|
-
* return output;
|
|
162
|
-
* }
|
|
163
|
-
*
|
|
164
|
-
* // Output
|
|
165
|
-
* function percentage(x) {
|
|
166
|
-
* var multiplied = x * 100;
|
|
167
|
-
* var floored = Math.floor(multiplied);
|
|
168
|
-
* multiplied = floored + "%";
|
|
169
|
-
* return multiplied;
|
|
170
|
-
* }
|
|
171
|
-
* ```
|
|
172
|
-
*
|
|
173
|
-
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
174
|
-
*/
|
|
175
|
-
nameRecycling?: ProbabilityMap<boolean>;
|
|
176
|
-
|
|
177
144
|
/**
|
|
178
145
|
* ### `controlFlowFlattening`
|
|
179
146
|
*
|
|
@@ -286,48 +253,15 @@ export interface ObfuscateOptions {
|
|
|
286
253
|
*/
|
|
287
254
|
dispatcher?: ProbabilityMap<boolean>;
|
|
288
255
|
|
|
289
|
-
/**
|
|
290
|
-
* ### `eval`
|
|
291
|
-
*
|
|
292
|
-
* #### **`Security Warning`**
|
|
293
|
-
*
|
|
294
|
-
* From [MDN](<(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)**>): Executing JavaScript from a string is an enormous security risk. It is far too easy
|
|
295
|
-
* for a bad actor to run arbitrary code when you use eval(). Never use eval()!
|
|
296
|
-
*
|
|
297
|
-
* Wraps defined functions within eval statements.
|
|
298
|
-
*
|
|
299
|
-
* - **`false`** - Avoids using the `eval` function. _Default_.
|
|
300
|
-
* - **`true`** - Wraps function's code into an `eval` statement.
|
|
301
|
-
*
|
|
302
|
-
* ```js
|
|
303
|
-
* // Output.js
|
|
304
|
-
* var Q4r1__ = {
|
|
305
|
-
* Oo$Oz8t: eval(
|
|
306
|
-
* "(function(YjVpAp){var gniSBq6=kHmsJrhOO;switch(gniSBq6){case'RW11Hj5x':return console;}});"
|
|
307
|
-
* ),
|
|
308
|
-
* };
|
|
309
|
-
* Q4r1__.Oo$Oz8t("RW11Hj5x");
|
|
310
|
-
* ```
|
|
311
|
-
*
|
|
312
|
-
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
313
|
-
*/
|
|
314
|
-
eval?: ProbabilityMap<boolean>;
|
|
315
|
-
|
|
316
256
|
/**
|
|
317
257
|
* ### `rgf`
|
|
318
258
|
*
|
|
319
259
|
* RGF (Runtime-Generated-Functions) uses the [`new Function(code...)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function) syntax to construct executable code from strings. (`"all"/true/false`)
|
|
320
260
|
*
|
|
321
|
-
* - **This can break your code.
|
|
261
|
+
* - **This can break your code.
|
|
322
262
|
* - **Due to the security concerns of arbitrary code execution, you must enable this yourself.**
|
|
323
263
|
* - The arbitrary code is also obfuscated.
|
|
324
264
|
*
|
|
325
|
-
* | Mode | Description |
|
|
326
|
-
* | --- | --- |
|
|
327
|
-
* | `"all"` | Recursively applies to every scope (slow) |
|
|
328
|
-
* | `true` | Applies to the top level only |
|
|
329
|
-
* | `false` | Feature disabled |
|
|
330
|
-
*
|
|
331
265
|
* ```js
|
|
332
266
|
* // Input
|
|
333
267
|
* function log(x){
|
|
@@ -342,7 +276,7 @@ export interface ObfuscateOptions {
|
|
|
342
276
|
*
|
|
343
277
|
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
344
278
|
*/
|
|
345
|
-
rgf?: ProbabilityMap<boolean
|
|
279
|
+
rgf?: ProbabilityMap<boolean>;
|
|
346
280
|
|
|
347
281
|
/**
|
|
348
282
|
* ### `stack`
|
|
@@ -472,20 +406,6 @@ export interface ObfuscateOptions {
|
|
|
472
406
|
*/
|
|
473
407
|
context?: string[];
|
|
474
408
|
|
|
475
|
-
/**
|
|
476
|
-
* ### `lock.nativeFunctions`
|
|
477
|
-
*
|
|
478
|
-
* Set of global functions that are native. Such as `require`, `fetch`. If these variables are modified the program crashes.
|
|
479
|
-
* Set to `true` to use the default set of native functions. (`string[]/true/false`)
|
|
480
|
-
*
|
|
481
|
-
* - Potency Low
|
|
482
|
-
* - Resilience Medium
|
|
483
|
-
* - Cost Medium
|
|
484
|
-
*
|
|
485
|
-
* [See all settings here](https://github.com/MichaelXF/js-confuser/blob/master/README.md#options)
|
|
486
|
-
*/
|
|
487
|
-
nativeFunctions?: string[] | Set<string> | boolean;
|
|
488
|
-
|
|
489
409
|
/**
|
|
490
410
|
* ### `lock.startDate`
|
|
491
411
|
*
|
|
@@ -678,7 +598,6 @@ const validProperties = new Set([
|
|
|
678
598
|
"renameVariables",
|
|
679
599
|
"renameGlobals",
|
|
680
600
|
"identifierGenerator",
|
|
681
|
-
"nameRecycling",
|
|
682
601
|
"controlFlowFlattening",
|
|
683
602
|
"globalConcealing",
|
|
684
603
|
"stringCompression",
|
|
@@ -687,7 +606,6 @@ const validProperties = new Set([
|
|
|
687
606
|
"stringSplitting",
|
|
688
607
|
"duplicateLiteralsRemoval",
|
|
689
608
|
"dispatcher",
|
|
690
|
-
"eval",
|
|
691
609
|
"rgf",
|
|
692
610
|
"objectExtraction",
|
|
693
611
|
"flatten",
|
|
@@ -868,6 +786,10 @@ export async function correctOptions(
|
|
|
868
786
|
"alert",
|
|
869
787
|
"confirm",
|
|
870
788
|
"location",
|
|
789
|
+
"btoa",
|
|
790
|
+
"atob",
|
|
791
|
+
"unescape",
|
|
792
|
+
"encodeURIComponent",
|
|
871
793
|
].forEach((x) => options.globalVariables.add(x));
|
|
872
794
|
} else {
|
|
873
795
|
// node
|
|
@@ -876,6 +798,8 @@ export async function correctOptions(
|
|
|
876
798
|
"Buffer",
|
|
877
799
|
"require",
|
|
878
800
|
"process",
|
|
801
|
+
"exports",
|
|
802
|
+
"module",
|
|
879
803
|
"__dirname",
|
|
880
804
|
"__filename",
|
|
881
805
|
].forEach((x) => options.globalVariables.add(x));
|
|
@@ -887,6 +811,7 @@ export async function correctOptions(
|
|
|
887
811
|
"parseInt",
|
|
888
812
|
"parseFloat",
|
|
889
813
|
"Math",
|
|
814
|
+
"JSON",
|
|
890
815
|
"Promise",
|
|
891
816
|
"String",
|
|
892
817
|
"Boolean",
|
|
@@ -906,8 +831,6 @@ export async function correctOptions(
|
|
|
906
831
|
"setImmediate",
|
|
907
832
|
"clearImmediate",
|
|
908
833
|
"queueMicrotask",
|
|
909
|
-
"exports",
|
|
910
|
-
"module",
|
|
911
834
|
"isNaN",
|
|
912
835
|
"isFinite",
|
|
913
836
|
"Set",
|
package/src/presets.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { ObfuscateOptions } from "./options";
|
|
|
17
17
|
* 10. Minified output
|
|
18
18
|
*
|
|
19
19
|
* ## **`Disabled features`**
|
|
20
|
-
* - `
|
|
20
|
+
* - `rgf` Use at your own risk!
|
|
21
21
|
*
|
|
22
22
|
* ### Potential Issues
|
|
23
23
|
* 1. *String Encoding* can corrupt files. Disable `stringEncoding` manually if this happens.
|
|
@@ -51,7 +51,6 @@ const highPreset: ObfuscateOptions = {
|
|
|
51
51
|
stringSplitting: 0.75,
|
|
52
52
|
|
|
53
53
|
// Use at own risk
|
|
54
|
-
eval: false,
|
|
55
54
|
rgf: false,
|
|
56
55
|
};
|
|
57
56
|
|
|
@@ -66,9 +65,9 @@ const mediumPreset: ObfuscateOptions = {
|
|
|
66
65
|
calculator: true,
|
|
67
66
|
compact: true,
|
|
68
67
|
hexadecimalNumbers: true,
|
|
69
|
-
controlFlowFlattening: 0.
|
|
68
|
+
controlFlowFlattening: 0.25,
|
|
70
69
|
deadCode: 0.025,
|
|
71
|
-
dispatcher: 0.
|
|
70
|
+
dispatcher: 0.5,
|
|
72
71
|
duplicateLiteralsRemoval: 0.5,
|
|
73
72
|
globalConcealing: true,
|
|
74
73
|
identifierGenerator: "randomized",
|
|
@@ -95,10 +94,10 @@ const lowPreset: ObfuscateOptions = {
|
|
|
95
94
|
calculator: true,
|
|
96
95
|
compact: true,
|
|
97
96
|
hexadecimalNumbers: true,
|
|
98
|
-
controlFlowFlattening: 0.
|
|
97
|
+
controlFlowFlattening: 0.1,
|
|
99
98
|
deadCode: 0.01,
|
|
100
|
-
dispatcher: 0.
|
|
101
|
-
duplicateLiteralsRemoval:
|
|
99
|
+
dispatcher: 0.25,
|
|
100
|
+
duplicateLiteralsRemoval: 0.5,
|
|
102
101
|
identifierGenerator: "randomized",
|
|
103
102
|
minify: true,
|
|
104
103
|
movedDeclarations: true,
|
package/src/templates/crash.ts
CHANGED
|
@@ -3,7 +3,7 @@ import Template from "./template";
|
|
|
3
3
|
export const CrashTemplate1 = Template(`
|
|
4
4
|
var {var} = "a";
|
|
5
5
|
while(1){
|
|
6
|
-
{var} = {var} += "a";
|
|
6
|
+
{var} = {var} += "a";
|
|
7
7
|
}
|
|
8
8
|
`);
|
|
9
9
|
|
|
@@ -40,16 +40,16 @@ try {
|
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
{$1}();
|
|
43
|
-
} catch ( e ) {
|
|
44
|
-
while(e ? e : !e){
|
|
45
|
-
var b;
|
|
46
|
-
var c = 0;
|
|
47
|
-
(e ? !e : e) ? (function(e){
|
|
48
|
-
c = e ? 0 : !e ? 1 : 0;
|
|
49
|
-
})(e) : b = 1;
|
|
43
|
+
} catch ( {$1}e ) {
|
|
44
|
+
while({$1}e ? {$1}e : !{$1}e){
|
|
45
|
+
var {$1}b;
|
|
46
|
+
var {$1}c = 0;
|
|
47
|
+
({$1}e ? !{$1}e : {$1}e) ? (function({$1}e){
|
|
48
|
+
{$1}c = {$1}e ? 0 : !{$1}e ? 1 : 0;
|
|
49
|
+
})({$1}e) : {$1}b = 1;
|
|
50
50
|
|
|
51
|
-
if(b&&c){break;}
|
|
52
|
-
if(b){continue;}
|
|
51
|
+
if({$1}b&&{$1}c){break;}
|
|
52
|
+
if({$1}b){continue;}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Template from "./template";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper function to set `function.length` property.
|
|
5
|
+
*/
|
|
6
|
+
export const FunctionLengthTemplate = Template(`
|
|
7
|
+
function {name}(functionObject, functionLength){
|
|
8
|
+
Object["defineProperty"](functionObject, "length", {
|
|
9
|
+
"value": functionLength,
|
|
10
|
+
"configurable": true
|
|
11
|
+
});
|
|
12
|
+
return functionObject;
|
|
13
|
+
}
|
|
14
|
+
`);
|
|
@@ -68,6 +68,8 @@ import Template from "../templates/template";
|
|
|
68
68
|
* 3. using `this`
|
|
69
69
|
*/
|
|
70
70
|
export default class Dispatcher extends Transform {
|
|
71
|
+
// Debug mode preserves function names
|
|
72
|
+
isDebug = false;
|
|
71
73
|
count: number;
|
|
72
74
|
|
|
73
75
|
constructor(o) {
|
|
@@ -155,6 +157,16 @@ export default class Dispatcher extends Transform {
|
|
|
155
157
|
return "EXIT";
|
|
156
158
|
}
|
|
157
159
|
}
|
|
160
|
+
|
|
161
|
+
// Avoid functions with function expressions as they have a different scope
|
|
162
|
+
if (
|
|
163
|
+
(oo.type === "FunctionExpression" ||
|
|
164
|
+
oo.type === "ArrowFunctionExpression") &&
|
|
165
|
+
pp.find((x) => x == o.params)
|
|
166
|
+
) {
|
|
167
|
+
illegalFnNames.add(name);
|
|
168
|
+
return "EXIT";
|
|
169
|
+
}
|
|
158
170
|
});
|
|
159
171
|
|
|
160
172
|
functionDeclarations[name] = [o, p];
|
|
@@ -196,7 +208,9 @@ export default class Dispatcher extends Transform {
|
|
|
196
208
|
// map original name->new game
|
|
197
209
|
var gen = this.getGenerator();
|
|
198
210
|
Object.keys(functionDeclarations).forEach((name) => {
|
|
199
|
-
newFnNames[name] =
|
|
211
|
+
newFnNames[name] = this.isDebug
|
|
212
|
+
? "_dispatcher_" + this.count + "_" + name
|
|
213
|
+
: gen.generate();
|
|
200
214
|
});
|
|
201
215
|
// set containing new name
|
|
202
216
|
var set = new Set(Object.keys(newFnNames));
|
|
@@ -13,15 +13,9 @@ import {
|
|
|
13
13
|
BinaryExpression,
|
|
14
14
|
FunctionDeclaration,
|
|
15
15
|
ThisExpression,
|
|
16
|
-
|
|
16
|
+
ConditionalExpression,
|
|
17
17
|
} from "../../util/gen";
|
|
18
|
-
import {
|
|
19
|
-
append,
|
|
20
|
-
clone,
|
|
21
|
-
getLexContext,
|
|
22
|
-
isLexContext,
|
|
23
|
-
prepend,
|
|
24
|
-
} from "../../util/insert";
|
|
18
|
+
import { append, clone, prepend } from "../../util/insert";
|
|
25
19
|
import { isDirective, isPrimitive } from "../../util/compare";
|
|
26
20
|
|
|
27
21
|
import { ObfuscateOrder } from "../../order";
|
|
@@ -29,6 +23,8 @@ import { isModuleSource } from "../string/stringConcealing";
|
|
|
29
23
|
import { ComputeProbabilityMap } from "../../probability";
|
|
30
24
|
import { ok } from "assert";
|
|
31
25
|
import { chance, choice, getRandomInteger } from "../../util/random";
|
|
26
|
+
import { getBlock } from "../../traverse";
|
|
27
|
+
import { getIdentifierInfo } from "../../util/identifiers";
|
|
32
28
|
|
|
33
29
|
/**
|
|
34
30
|
* [Duplicate Literals Removal](https://docs.jscrambler.com/code-integrity/documentation/transformations/duplicate-literals-removal) replaces duplicate literals with a variable name.
|
|
@@ -49,20 +45,25 @@ import { chance, choice, getRandomInteger } from "../../util/random";
|
|
|
49
45
|
* ```
|
|
50
46
|
*/
|
|
51
47
|
export default class DuplicateLiteralsRemoval extends Transform {
|
|
48
|
+
// The array holding all the duplicate literals
|
|
52
49
|
arrayName: string;
|
|
50
|
+
// The array expression node to be inserted into the program
|
|
53
51
|
arrayExpression: Node;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Literals in the array
|
|
55
|
+
*/
|
|
54
56
|
map: Map<string, number>;
|
|
55
|
-
first: Map<string, Location | null>;
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
|
-
*
|
|
59
|
+
* Literals are saved here the first time they are seen.
|
|
59
60
|
*/
|
|
60
|
-
|
|
61
|
+
first: Map<string, Location>;
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
|
-
*
|
|
64
|
+
* Block -> { functionName, indexShift }
|
|
64
65
|
*/
|
|
65
|
-
|
|
66
|
+
functions: Map<Node, { functionName: string; indexShift: number }>;
|
|
66
67
|
|
|
67
68
|
constructor(o) {
|
|
68
69
|
super(o, ObfuscateOrder.DuplicateLiteralsRemoval);
|
|
@@ -70,14 +71,14 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
70
71
|
this.map = new Map();
|
|
71
72
|
this.first = new Map();
|
|
72
73
|
|
|
73
|
-
this.
|
|
74
|
-
this.fnGetters = new Map();
|
|
74
|
+
this.functions = new Map();
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
apply(tree) {
|
|
78
78
|
super.apply(tree);
|
|
79
79
|
|
|
80
|
-
if (this.arrayName && this.arrayExpression.elements.length) {
|
|
80
|
+
if (this.arrayName && this.arrayExpression.elements.length > 0) {
|
|
81
|
+
// This function simply returns the array
|
|
81
82
|
var getArrayFn = this.getPlaceholder();
|
|
82
83
|
append(
|
|
83
84
|
tree,
|
|
@@ -88,22 +89,79 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
88
89
|
)
|
|
89
90
|
);
|
|
90
91
|
|
|
92
|
+
// This variable holds the array
|
|
91
93
|
prepend(
|
|
92
94
|
tree,
|
|
93
95
|
VariableDeclaration(
|
|
94
96
|
VariableDeclarator(
|
|
95
97
|
this.arrayName,
|
|
96
98
|
CallExpression(
|
|
97
|
-
MemberExpression(
|
|
98
|
-
Identifier(getArrayFn),
|
|
99
|
-
Identifier("call"),
|
|
100
|
-
false
|
|
101
|
-
),
|
|
99
|
+
MemberExpression(Identifier(getArrayFn), Literal("call"), true),
|
|
102
100
|
[ThisExpression()]
|
|
103
101
|
)
|
|
104
102
|
)
|
|
105
103
|
)
|
|
106
104
|
);
|
|
105
|
+
|
|
106
|
+
// Create all the functions needed
|
|
107
|
+
for (var blockNode of this.functions.keys()) {
|
|
108
|
+
var { functionName, indexShift } = this.functions.get(blockNode);
|
|
109
|
+
|
|
110
|
+
var propertyNode: Node = BinaryExpression(
|
|
111
|
+
"-",
|
|
112
|
+
Identifier("index_param"),
|
|
113
|
+
Literal(indexShift)
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
var indexRangeInclusive = [
|
|
117
|
+
0 + indexShift - 1,
|
|
118
|
+
this.map.size + indexShift,
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
// The function uses mangling to hide the index being accessed
|
|
122
|
+
var mangleCount = getRandomInteger(1, 10);
|
|
123
|
+
for (var i = 0; i < mangleCount; i++) {
|
|
124
|
+
var operator = choice([">", "<"]);
|
|
125
|
+
var compareValue = choice(indexRangeInclusive);
|
|
126
|
+
|
|
127
|
+
var test = BinaryExpression(
|
|
128
|
+
operator,
|
|
129
|
+
Identifier("index_param"),
|
|
130
|
+
Literal(compareValue)
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
var alternate = BinaryExpression(
|
|
134
|
+
"-",
|
|
135
|
+
Identifier("index_param"),
|
|
136
|
+
Literal(getRandomInteger(-100, 100))
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
var testValue =
|
|
140
|
+
(operator === ">" && compareValue === indexRangeInclusive[0]) ||
|
|
141
|
+
(operator === "<" && compareValue === indexRangeInclusive[1]);
|
|
142
|
+
|
|
143
|
+
propertyNode = ConditionalExpression(
|
|
144
|
+
test,
|
|
145
|
+
testValue ? propertyNode : alternate,
|
|
146
|
+
!testValue ? propertyNode : alternate
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
var returnArgument = MemberExpression(
|
|
151
|
+
Identifier(this.arrayName),
|
|
152
|
+
propertyNode,
|
|
153
|
+
true
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
prepend(
|
|
157
|
+
blockNode,
|
|
158
|
+
FunctionDeclaration(
|
|
159
|
+
functionName,
|
|
160
|
+
[Identifier("index_param")],
|
|
161
|
+
[ReturnStatement(returnArgument)]
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
107
165
|
}
|
|
108
166
|
}
|
|
109
167
|
|
|
@@ -122,109 +180,52 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
122
180
|
* @param parents
|
|
123
181
|
* @param index
|
|
124
182
|
*/
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// create one if none are available (or by random chance if none are here locally)
|
|
140
|
-
var shouldCreateNew =
|
|
141
|
-
!getterName || (!hasGetterHere && Math.random() > 0.9);
|
|
142
|
-
|
|
143
|
-
if (shouldCreateNew) {
|
|
144
|
-
ok(!this.fnGetters.has(lexContext));
|
|
145
|
-
|
|
146
|
-
var lexContextIndex = parents.findIndex(
|
|
147
|
-
(x) => x !== lexContext && isLexContext(x)
|
|
148
|
-
);
|
|
149
|
-
var basedOn =
|
|
150
|
-
lexContextIndex !== -1
|
|
151
|
-
? choice(
|
|
152
|
-
parents
|
|
153
|
-
.slice(lexContextIndex + 1)
|
|
154
|
-
.map((x) => this.fnGetters.get(x))
|
|
155
|
-
.filter((x) => x)
|
|
156
|
-
)
|
|
157
|
-
: null;
|
|
158
|
-
|
|
159
|
-
var body = [];
|
|
160
|
-
var thisShift = getRandomInteger(-250, 250);
|
|
161
|
-
// the name of the getter
|
|
162
|
-
getterName = this.getPlaceholder() + "_dLR_" + this.fnGetters.size;
|
|
163
|
-
|
|
164
|
-
if (basedOn) {
|
|
165
|
-
var shift = this.fnShifts.get(basedOn);
|
|
166
|
-
ok(typeof shift === "number");
|
|
167
|
-
|
|
168
|
-
body = [
|
|
169
|
-
ReturnStatement(
|
|
170
|
-
CallExpression(Identifier(basedOn), [
|
|
171
|
-
BinaryExpression("+", Identifier("index"), Literal(thisShift)),
|
|
172
|
-
])
|
|
173
|
-
),
|
|
174
|
-
];
|
|
183
|
+
transformLiteral(object: Node, parents: Node[], index: number) {
|
|
184
|
+
var blockNode = choice(parents.filter((x) => this.functions.has(x)));
|
|
185
|
+
|
|
186
|
+
// Create initial function if none exist
|
|
187
|
+
if (this.functions.size === 0) {
|
|
188
|
+
var root = parents[parents.length - 1];
|
|
189
|
+
var rootFunctionName = this.getPlaceholder() + "_dLR_0";
|
|
190
|
+
this.functions.set(root, {
|
|
191
|
+
functionName: rootFunctionName,
|
|
192
|
+
indexShift: getRandomInteger(-100, 100),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
blockNode = root;
|
|
196
|
+
}
|
|
175
197
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
ReturnStatement(
|
|
182
|
-
MemberExpression(
|
|
183
|
-
Identifier(this.arrayName),
|
|
184
|
-
BinaryExpression("+", Identifier("index"), Literal(thisShift)),
|
|
185
|
-
true
|
|
186
|
-
)
|
|
187
|
-
),
|
|
188
|
-
];
|
|
198
|
+
// If no function here exist, possibly create new chained function
|
|
199
|
+
var block = getBlock(object, parents);
|
|
200
|
+
if (!this.functions.has(block) && chance(50 - this.functions.size)) {
|
|
201
|
+
var newFunctionName =
|
|
202
|
+
this.getPlaceholder() + "_dLR_" + this.functions.size;
|
|
189
203
|
|
|
190
|
-
|
|
191
|
-
|
|
204
|
+
this.functions.set(block, {
|
|
205
|
+
functionName: newFunctionName,
|
|
206
|
+
indexShift: getRandomInteger(-100, 100),
|
|
207
|
+
});
|
|
192
208
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
prepend(
|
|
196
|
-
lexContext,
|
|
197
|
-
VariableDeclaration(
|
|
198
|
-
VariableDeclarator(
|
|
199
|
-
getterName,
|
|
200
|
-
CallExpression(
|
|
201
|
-
FunctionExpression(
|
|
202
|
-
[],
|
|
203
|
-
[
|
|
204
|
-
ReturnStatement(
|
|
205
|
-
FunctionExpression([Identifier("index")], body)
|
|
206
|
-
),
|
|
207
|
-
]
|
|
208
|
-
),
|
|
209
|
-
[]
|
|
210
|
-
)
|
|
211
|
-
)
|
|
212
|
-
)
|
|
213
|
-
);
|
|
209
|
+
blockNode = block;
|
|
214
210
|
}
|
|
215
211
|
|
|
216
|
-
|
|
212
|
+
// Derive the function to call from the selected blockNode
|
|
213
|
+
var { functionName, indexShift } = this.functions.get(blockNode);
|
|
217
214
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
215
|
+
// Call the function given it's indexShift
|
|
216
|
+
var callExpression = CallExpression(Identifier(functionName), [
|
|
217
|
+
Literal(index + indexShift),
|
|
218
|
+
]);
|
|
219
|
+
|
|
220
|
+
this.replaceIdentifierOrLiteral(object, callExpression, parents);
|
|
223
221
|
}
|
|
224
222
|
|
|
225
223
|
transform(object: Node, parents: Node[]) {
|
|
226
224
|
return () => {
|
|
227
|
-
|
|
225
|
+
if (object.type === "Identifier") {
|
|
226
|
+
var info = getIdentifierInfo(object, parents);
|
|
227
|
+
if (info.isLabel || info.spec.isDefined) return;
|
|
228
|
+
}
|
|
228
229
|
if (object.regex) {
|
|
229
230
|
return;
|
|
230
231
|
}
|
|
@@ -234,7 +235,7 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
234
235
|
}
|
|
235
236
|
|
|
236
237
|
// HARD CODED LIMIT of 10,000 (after 1,000 elements)
|
|
237
|
-
if (this.map.size > 1000 &&
|
|
238
|
+
if (this.map.size > 1000 && chance(this.map.size / 100)) return;
|
|
238
239
|
|
|
239
240
|
if (
|
|
240
241
|
this.arrayName &&
|
|
@@ -244,11 +245,11 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
244
245
|
return;
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
var
|
|
248
|
+
var stringValue;
|
|
248
249
|
if (object.type == "Literal") {
|
|
249
|
-
|
|
250
|
+
stringValue = typeof object.value + ":" + object.value;
|
|
250
251
|
if (object.value === null) {
|
|
251
|
-
|
|
252
|
+
stringValue = "null:null";
|
|
252
253
|
} else {
|
|
253
254
|
// Skip empty strings
|
|
254
255
|
if (typeof object.value === "string" && !object.value) {
|
|
@@ -256,42 +257,46 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
259
|
} else if (object.type == "Identifier") {
|
|
259
|
-
|
|
260
|
+
stringValue = "identifier:" + object.name;
|
|
260
261
|
} else {
|
|
261
262
|
throw new Error("Unsupported primitive type: " + object.type);
|
|
262
263
|
}
|
|
263
264
|
|
|
264
|
-
ok(
|
|
265
|
+
ok(stringValue);
|
|
265
266
|
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
} else {
|
|
267
|
+
if (this.map.has(stringValue) || this.first.has(stringValue)) {
|
|
268
|
+
// Create the array if not already made
|
|
269
269
|
if (!this.arrayName) {
|
|
270
270
|
this.arrayName = this.getPlaceholder();
|
|
271
271
|
this.arrayExpression = ArrayExpression([]);
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
|
|
274
|
+
// Delete with first location
|
|
275
|
+
var firstLocation = this.first.get(stringValue);
|
|
275
276
|
if (firstLocation) {
|
|
276
|
-
this.first.set(value, null);
|
|
277
277
|
var index = this.map.size;
|
|
278
278
|
|
|
279
|
-
ok(!this.map.has(
|
|
280
|
-
this.map.set(
|
|
279
|
+
ok(!this.map.has(stringValue));
|
|
280
|
+
this.map.set(stringValue, index);
|
|
281
|
+
this.first.delete(stringValue);
|
|
281
282
|
|
|
282
283
|
var pushing = clone(object);
|
|
283
284
|
this.arrayExpression.elements.push(pushing);
|
|
284
285
|
|
|
285
286
|
ok(this.arrayExpression.elements[index] === pushing);
|
|
286
287
|
|
|
287
|
-
this.
|
|
288
|
+
this.transformLiteral(firstLocation[0], firstLocation[1], index);
|
|
288
289
|
}
|
|
289
290
|
|
|
290
|
-
var index = this.map.get(
|
|
291
|
+
var index = this.map.get(stringValue);
|
|
291
292
|
ok(typeof index === "number");
|
|
292
293
|
|
|
293
|
-
this.
|
|
294
|
+
this.transformLiteral(object, parents, index);
|
|
295
|
+
return;
|
|
294
296
|
}
|
|
297
|
+
|
|
298
|
+
// Save this, maybe a duplicate will be found.
|
|
299
|
+
this.first.set(stringValue, [object, parents]);
|
|
295
300
|
};
|
|
296
301
|
}
|
|
297
302
|
}
|