js-confuser 1.7.0 → 1.7.2

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.
Files changed (128) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +12 -29
  3. package/dist/compiler.js +2 -8
  4. package/dist/constants.js +17 -10
  5. package/dist/index.js +7 -30
  6. package/dist/obfuscator.js +15 -62
  7. package/dist/options.js +21 -38
  8. package/dist/order.js +4 -7
  9. package/dist/parser.js +5 -13
  10. package/dist/precedence.js +6 -8
  11. package/dist/presets.js +4 -6
  12. package/dist/probability.js +13 -24
  13. package/dist/templates/bufferToString.js +100 -5
  14. package/dist/templates/crash.js +51 -9
  15. package/dist/templates/es5.js +125 -6
  16. package/dist/templates/functionLength.js +24 -6
  17. package/dist/templates/globals.js +9 -0
  18. package/dist/templates/template.js +71 -30
  19. package/dist/transforms/antiTooling.js +26 -22
  20. package/dist/transforms/calculator.js +18 -54
  21. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +236 -333
  22. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +46 -25
  23. package/dist/transforms/deadCode.js +528 -27
  24. package/dist/transforms/dispatcher.js +110 -108
  25. package/dist/transforms/es5/antiClass.js +70 -44
  26. package/dist/transforms/es5/antiDestructuring.js +14 -38
  27. package/dist/transforms/es5/antiES6Object.js +39 -48
  28. package/dist/transforms/es5/antiSpreadOperator.js +5 -14
  29. package/dist/transforms/es5/antiTemplate.js +10 -19
  30. package/dist/transforms/es5/es5.js +7 -40
  31. package/dist/transforms/extraction/classExtraction.js +83 -0
  32. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +45 -79
  33. package/dist/transforms/extraction/objectExtraction.js +27 -54
  34. package/dist/transforms/finalizer.js +6 -20
  35. package/dist/transforms/flatten.js +51 -99
  36. package/dist/transforms/identifier/globalAnalysis.js +8 -26
  37. package/dist/transforms/identifier/globalConcealing.js +35 -54
  38. package/dist/transforms/identifier/movedDeclarations.js +66 -38
  39. package/dist/transforms/identifier/renameVariables.js +29 -68
  40. package/dist/transforms/identifier/variableAnalysis.js +21 -48
  41. package/dist/transforms/lock/antiDebug.js +20 -25
  42. package/dist/transforms/lock/integrity.js +48 -47
  43. package/dist/transforms/lock/lock.js +62 -113
  44. package/dist/transforms/minify.js +77 -108
  45. package/dist/transforms/opaquePredicates.js +11 -48
  46. package/dist/transforms/preparation.js +17 -50
  47. package/dist/transforms/renameLabels.js +5 -22
  48. package/dist/transforms/rgf.js +98 -66
  49. package/dist/transforms/shuffle.js +41 -46
  50. package/dist/transforms/stack.js +35 -98
  51. package/dist/transforms/string/encoding.js +73 -27
  52. package/dist/transforms/string/stringCompression.js +44 -68
  53. package/dist/transforms/string/stringConcealing.js +125 -134
  54. package/dist/transforms/string/stringEncoding.js +6 -26
  55. package/dist/transforms/string/stringSplitting.js +5 -30
  56. package/dist/transforms/transform.js +50 -100
  57. package/dist/traverse.js +11 -18
  58. package/dist/util/compare.js +27 -29
  59. package/dist/util/gen.js +32 -86
  60. package/dist/util/guard.js +0 -1
  61. package/dist/util/identifiers.js +11 -74
  62. package/dist/util/insert.js +27 -77
  63. package/dist/util/math.js +0 -3
  64. package/dist/util/object.js +3 -7
  65. package/dist/util/random.js +5 -36
  66. package/dist/util/scope.js +6 -3
  67. package/docs/ControlFlowFlattening.md +1 -1
  68. package/docs/ES5.md +197 -0
  69. package/package.json +3 -3
  70. package/src/constants.ts +12 -0
  71. package/src/options.ts +13 -0
  72. package/src/order.ts +2 -2
  73. package/src/templates/bufferToString.ts +49 -11
  74. package/src/templates/functionLength.ts +21 -3
  75. package/src/templates/globals.ts +3 -0
  76. package/src/templates/template.ts +85 -25
  77. package/src/transforms/antiTooling.ts +33 -11
  78. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +2 -2
  79. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +46 -10
  80. package/src/transforms/deadCode.ts +0 -16
  81. package/src/transforms/dispatcher.ts +101 -69
  82. package/src/transforms/es5/antiClass.ts +10 -1
  83. package/src/transforms/extraction/classExtraction.ts +168 -0
  84. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +13 -10
  85. package/src/transforms/extraction/objectExtraction.ts +7 -14
  86. package/src/transforms/flatten.ts +20 -5
  87. package/src/transforms/identifier/globalConcealing.ts +29 -65
  88. package/src/transforms/identifier/movedDeclarations.ts +90 -24
  89. package/src/transforms/minify.ts +27 -12
  90. package/src/transforms/rgf.ts +103 -5
  91. package/src/transforms/stack.ts +12 -3
  92. package/src/transforms/string/encoding.ts +85 -51
  93. package/src/transforms/string/stringCompression.ts +5 -8
  94. package/src/transforms/string/stringConcealing.ts +139 -113
  95. package/src/transforms/string/stringEncoding.ts +1 -2
  96. package/src/transforms/string/stringSplitting.ts +1 -2
  97. package/src/transforms/transform.ts +30 -1
  98. package/src/util/compare.ts +39 -5
  99. package/src/util/gen.ts +10 -3
  100. package/src/util/identifiers.ts +6 -3
  101. package/src/util/insert.ts +17 -0
  102. package/src/util/scope.ts +14 -2
  103. package/test/code/Cash.test.ts +10 -4
  104. package/test/code/StrictMode.src.js +65 -0
  105. package/test/code/StrictMode.test.js +37 -0
  106. package/test/compare.test.ts +62 -2
  107. package/test/options.test.ts +111 -55
  108. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +37 -18
  109. package/test/transforms/dispatcher.test.ts +82 -0
  110. package/test/transforms/extraction/classExtraction.test.ts +86 -0
  111. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +29 -8
  112. package/test/transforms/extraction/objectExtraction.test.ts +37 -15
  113. package/test/transforms/identifier/globalConcealing.test.ts +42 -2
  114. package/test/transforms/identifier/movedDeclarations.test.ts +61 -0
  115. package/test/transforms/lock/integrity.test.ts +24 -0
  116. package/test/transforms/minify.test.ts +37 -0
  117. package/test/transforms/rgf.test.ts +50 -0
  118. package/test/util/identifiers.test.ts +21 -0
  119. package/dist/transforms/controlFlowFlattening/choiceFlowObfuscation.js +0 -62
  120. package/dist/transforms/controlFlowFlattening/controlFlowObfuscation.js +0 -159
  121. package/dist/transforms/controlFlowFlattening/switchCaseObfuscation.js +0 -106
  122. package/dist/transforms/eval.js +0 -84
  123. package/dist/transforms/hexadecimalNumbers.js +0 -63
  124. package/dist/transforms/hideInitializingCode.js +0 -270
  125. package/dist/transforms/identifier/nameRecycling.js +0 -218
  126. package/dist/transforms/label.js +0 -67
  127. package/dist/transforms/preparation/nameConflicts.js +0 -116
  128. package/dist/transforms/preparation/preparation.js +0 -188
@@ -1,12 +1,11 @@
1
1
  import { ok } from "assert";
2
2
  import { ObfuscateOrder } from "../../order";
3
3
  import Template from "../../templates/template";
4
- import { isBlock } from "../../traverse";
5
- import { isDirective } from "../../util/compare";
4
+ import { getBlock } from "../../traverse";
5
+ import { isDirective, isModuleSource } from "../../util/compare";
6
6
  import {
7
7
  ArrayExpression,
8
8
  CallExpression,
9
- FunctionExpression,
10
9
  Identifier,
11
10
  Literal,
12
11
  MemberExpression,
@@ -22,54 +21,36 @@ import {
22
21
  choice,
23
22
  getRandomInteger,
24
23
  getRandomString,
24
+ shuffle,
25
25
  } from "../../util/random";
26
26
  import Transform from "../transform";
27
- import Encoding from "./encoding";
27
+ import {
28
+ EncodingImplementation,
29
+ EncodingImplementations,
30
+ createEncodingImplementation,
31
+ hasAllEncodings,
32
+ } from "./encoding";
28
33
  import { ComputeProbabilityMap } from "../../probability";
29
34
  import { BufferToStringTemplate } from "../../templates/bufferToString";
35
+ import { criticalFunctionTag, predictableFunctionTag } from "../../constants";
30
36
 
31
- export function isModuleSource(object: Node, parents: Node[]) {
32
- if (!parents[0]) {
33
- return false;
34
- }
35
-
36
- if (parents[0].type == "ImportDeclaration" && parents[0].source == object) {
37
- return true;
38
- }
39
-
40
- if (parents[0].type == "ImportExpression" && parents[0].source == object) {
41
- return true;
42
- }
43
-
44
- if (
45
- parents[1] &&
46
- parents[1].type == "CallExpression" &&
47
- parents[1].arguments[0] === object &&
48
- parents[1].callee.type == "Identifier"
49
- ) {
50
- if (
51
- parents[1].callee.name == "require" ||
52
- parents[1].callee.name == "import"
53
- ) {
54
- return true;
55
- }
56
- }
57
-
58
- return false;
37
+ interface FunctionObject {
38
+ block: Node;
39
+ fnName: string;
40
+ encodingImplementation: EncodingImplementation;
59
41
  }
60
42
 
61
43
  export default class StringConcealing extends Transform {
62
44
  arrayExpression: Node;
63
45
  set: Set<string>;
64
- index: { [str: string]: [number, string] };
46
+ index: { [str: string]: [number, string, Node] }; // index, fnName, block
65
47
 
66
48
  arrayName = this.getPlaceholder();
67
49
  ignore = new Set<string>();
68
50
  variablesMade = 1;
69
- encoding: { [type: string]: string } = Object.create(null);
70
51
  gen: ReturnType<Transform["getGenerator"]>;
71
52
 
72
- hasAllEncodings: boolean;
53
+ functionObjects: FunctionObject[] = [];
73
54
 
74
55
  constructor(o) {
75
56
  super(o, ObfuscateOrder.StringConcealing);
@@ -77,84 +58,110 @@ export default class StringConcealing extends Transform {
77
58
  this.set = new Set();
78
59
  this.index = Object.create(null);
79
60
  this.arrayExpression = ArrayExpression([]);
80
- this.hasAllEncodings = false;
81
61
  this.gen = this.getGenerator();
62
+ }
63
+
64
+ apply(tree) {
65
+ super.apply(tree);
82
66
 
83
67
  // Pad array with useless strings
84
68
  var dead = getRandomInteger(5, 15);
85
69
  for (var i = 0; i < dead; i++) {
86
70
  var str = getRandomString(getRandomInteger(5, 40));
87
- var fn = this.transform(Literal(str), []);
71
+ var fn = this.transform(Literal(str), [tree]);
88
72
  if (fn) {
89
73
  fn();
90
74
  }
91
75
  }
92
- }
93
-
94
- apply(tree) {
95
- super.apply(tree);
96
76
 
97
77
  var cacheName = this.getPlaceholder();
98
- var bufferToStringName = this.getPlaceholder();
78
+ var bufferToStringName = this.getPlaceholder() + predictableFunctionTag;
99
79
 
100
80
  // This helper functions convert UInt8 Array to UTf-string
101
81
  prepend(
102
82
  tree,
103
- ...BufferToStringTemplate.compile({ name: bufferToStringName })
83
+ ...BufferToStringTemplate.compile({
84
+ name: bufferToStringName,
85
+ getGlobalFnName: this.getPlaceholder() + predictableFunctionTag,
86
+ })
104
87
  );
105
88
 
106
- Object.keys(this.encoding).forEach((type) => {
107
- var { template } = Encoding[type];
108
- var decodeFn = this.getPlaceholder();
109
- var getterFn = this.encoding[type];
89
+ for (var functionObject of this.functionObjects) {
90
+ var {
91
+ block,
92
+ fnName: getterFnName,
93
+ encodingImplementation,
94
+ } = functionObject;
95
+
96
+ var decodeFn =
97
+ this.getPlaceholder() + predictableFunctionTag + criticalFunctionTag;
110
98
 
111
99
  append(
112
- tree,
113
- template.single({ name: decodeFn, bufferToString: bufferToStringName })
100
+ block,
101
+ encodingImplementation.template.single({
102
+ __fnName__: decodeFn,
103
+ __bufferToString__: bufferToStringName,
104
+ })
114
105
  );
106
+ // All these are fake and never ran
107
+ var ifStatements = Template(`if ( z == x ) {
108
+ return y[${cacheName}[z]] = ${getterFnName}(x, y);
109
+ }
110
+ if ( y ) {
111
+ [b, y] = [a(b), x || z]
112
+ return ${getterFnName}(x, b, z)
113
+ }
114
+ if ( z && a !== ${decodeFn} ) {
115
+ ${getterFnName} = ${decodeFn}
116
+ return ${getterFnName}(x, -1, z, a, b)
117
+ }
118
+ if ( a === ${getterFnName} ) {
119
+ ${decodeFn} = y
120
+ return ${decodeFn}(z)
121
+ }
122
+ if( a === undefined ) {
123
+ ${getterFnName} = b
124
+ }
125
+ if( z == a ) {
126
+ return y ? x[b[y]] : ${cacheName}[x] || (z=(b[x] || a), ${cacheName}[x] = z(${this.arrayName}[x]))
127
+ }
128
+ `).compile();
115
129
 
116
- append(
117
- tree,
130
+ // Not all fake if-statements are needed
131
+ ifStatements = ifStatements.filter(() => chance(50));
132
+
133
+ // This one is always used
134
+ ifStatements.push(
118
135
  Template(`
119
-
120
- function ${getterFn}(x, y, z, a = ${decodeFn}, b = ${cacheName}){
121
- if ( z ) {
122
- return y[${cacheName}[z]] = ${getterFn}(x, y);
123
- } else if ( y ) {
124
- [b, y] = [a(b), x || z]
125
- }
126
-
127
- return y ? x[b[y]] : ${cacheName}[x] || (z=(b[x], a), ${cacheName}[x] = z(${this.arrayName}[x]))
128
- }
129
-
130
- `).single()
136
+ if ( x !== y ) {
137
+ return b[x] || (b[x] = a(${this.arrayName}[x]))
138
+ }
139
+ `).single()
131
140
  );
132
- });
133
141
 
134
- var flowIntegrity = this.getPlaceholder();
142
+ shuffle(ifStatements);
143
+
144
+ var varDeclaration = Template(`
145
+ var ${getterFnName} = (x, y, z, a, b)=>{
146
+ if(typeof a === "undefined") {
147
+ a = ${decodeFn}
148
+ }
149
+ if(typeof b === "undefined") {
150
+ b = ${cacheName}
151
+ }
152
+ }
153
+ `).single();
154
+
155
+ varDeclaration.declarations[0].init.body.body.push(...ifStatements);
156
+
157
+ prepend(block, varDeclaration);
158
+ }
135
159
 
136
160
  prepend(
137
161
  tree,
138
162
  VariableDeclaration([
139
163
  VariableDeclarator(cacheName, ArrayExpression([])),
140
- VariableDeclarator(flowIntegrity, Literal(0)),
141
- VariableDeclarator(
142
- this.arrayName,
143
- CallExpression(
144
- FunctionExpression(
145
- [],
146
- [
147
- VariableDeclaration(
148
- VariableDeclarator("a", this.arrayExpression)
149
- ),
150
- Template(
151
- `return (${flowIntegrity} ? a["pop"]() : ${flowIntegrity}++, a)`
152
- ).single(),
153
- ]
154
- ),
155
- []
156
- )
157
- ),
164
+ VariableDeclarator(this.arrayName, this.arrayExpression),
158
165
  ])
159
166
  );
160
167
  }
@@ -192,48 +199,67 @@ export default class StringConcealing extends Transform {
192
199
  return;
193
200
  }
194
201
 
195
- // HARD CODED LIMIT of 10,000 (after 1,000 elements)
196
- if (this.set.size > 1000 && !chance(this.set.size / 100)) return;
202
+ var currentBlock = getBlock(object, parents);
197
203
 
198
- var types = Object.keys(this.encoding);
204
+ // Find created functions
205
+ var functionObjects: FunctionObject[] = parents
206
+ .filter((node) => node.$stringConcealingFunctionObject)
207
+ .map((item) => item.$stringConcealingFunctionObject);
199
208
 
200
- var type = choice(types);
201
- if (!type || (!this.hasAllEncodings && chance(10))) {
202
- var allowed = Object.keys(Encoding).filter(
203
- (type) => !this.encoding[type]
204
- );
209
+ // Choose random functionObject to use
210
+ var functionObject = choice(functionObjects);
211
+
212
+ if (
213
+ !functionObject ||
214
+ (!hasAllEncodings() &&
215
+ chance(25 / this.functionObjects.length) &&
216
+ !currentBlock.$stringConcealingFunctionObject)
217
+ ) {
218
+ // No functions, create one
205
219
 
206
- if (!allowed.length) {
207
- this.hasAllEncodings = true;
208
- } else {
209
- var random = choice(allowed);
210
- type = random;
220
+ var newFunctionObject: FunctionObject = {
221
+ block: currentBlock,
222
+ encodingImplementation: createEncodingImplementation(),
223
+ fnName: this.getPlaceholder() + predictableFunctionTag,
224
+ };
211
225
 
212
- this.encoding[random] = this.getPlaceholder();
213
- }
226
+ this.functionObjects.push(newFunctionObject);
227
+ currentBlock.$stringConcealingFunctionObject = newFunctionObject;
228
+ functionObject = newFunctionObject;
214
229
  }
215
230
 
216
- var fnName = this.encoding[type];
217
- var encoder = Encoding[type];
231
+ var { fnName, encodingImplementation } = functionObject;
218
232
 
219
- // The decode function must return correct result
220
- var encoded = encoder.encode(object.value);
221
- if (encoder.decode(encoded) != object.value) {
222
- this.ignore.add(object.value);
223
- this.warn(type, object.value.slice(0, 100));
224
- return;
233
+ var index = -1;
234
+
235
+ // String already decoded?
236
+ if (this.set.has(object.value)) {
237
+ var row = this.index[object.value];
238
+ if (parents.includes(row[2])) {
239
+ [index, fnName] = row;
240
+ ok(typeof index === "number");
241
+ }
225
242
  }
226
243
 
227
- var index = -1;
228
- if (!this.set.has(object.value)) {
244
+ if (index == -1) {
245
+ // The decode function must return correct result
246
+ var encoded = encodingImplementation.encode(object.value);
247
+ if (encodingImplementation.decode(encoded) !== object.value) {
248
+ this.ignore.add(object.value);
249
+ this.warn(
250
+ encodingImplementation.identity,
251
+ object.value.slice(0, 100)
252
+ );
253
+ delete EncodingImplementations[encodingImplementation.identity];
254
+
255
+ return;
256
+ }
257
+
229
258
  this.arrayExpression.elements.push(Literal(encoded));
230
259
  index = this.arrayExpression.elements.length - 1;
231
- this.index[object.value] = [index, fnName];
260
+ this.index[object.value] = [index, fnName, currentBlock];
232
261
 
233
262
  this.set.add(object.value);
234
- } else {
235
- [index, fnName] = this.index[object.value];
236
- ok(typeof index === "number");
237
263
  }
238
264
 
239
265
  ok(index != -1, "index == -1");
@@ -269,7 +295,7 @@ export default class StringConcealing extends Transform {
269
295
 
270
296
  var constantReferenceType = choice(["variable", "array", "object"]);
271
297
 
272
- var place = choice(parents.filter((node) => isBlock(node)));
298
+ var place = currentBlock;
273
299
  if (!place) {
274
300
  this.error(new Error("No lexical block to insert code"));
275
301
  }
@@ -1,7 +1,6 @@
1
1
  import Transform from "../transform";
2
2
  import { choice } from "../../util/random";
3
- import { isDirective } from "../../util/compare";
4
- import { isModuleSource } from "./stringConcealing";
3
+ import { isDirective, isModuleSource } from "../../util/compare";
5
4
  import { ComputeProbabilityMap } from "../../probability";
6
5
  import { Identifier } from "../../util/gen";
7
6
 
@@ -3,8 +3,7 @@ import { Node, Literal, BinaryExpression } from "../../util/gen";
3
3
  import { clone } from "../../util/insert";
4
4
  import { getRandomInteger, shuffle, splitIntoChunks } from "../../util/random";
5
5
  import { ObfuscateOrder } from "../../order";
6
- import { isModuleSource } from "./stringConcealing";
7
- import { isDirective } from "../../util/compare";
6
+ import { isDirective, isModuleSource } from "../../util/compare";
8
7
  import { ok } from "assert";
9
8
  import { ComputeProbabilityMap } from "../../probability";
10
9
 
@@ -1,5 +1,10 @@
1
1
  import traverse, { ExitCallback } from "../traverse";
2
- import { AddComment, Node } from "../util/gen";
2
+ import {
3
+ AddComment,
4
+ Node,
5
+ VariableDeclaration,
6
+ VariableDeclarator,
7
+ } from "../util/gen";
3
8
  import {
4
9
  alphabeticalGenerator,
5
10
  choice,
@@ -15,6 +20,8 @@ import {
15
20
  reservedKeywords,
16
21
  } from "../constants";
17
22
  import { ObfuscateOrder } from "../order";
23
+ import { ITemplate } from "../templates/template";
24
+ import { prepend } from "../util/insert";
18
25
 
19
26
  /**
20
27
  * Base-class for all transformations.
@@ -78,6 +85,8 @@ export default class Transform {
78
85
  */
79
86
  after: Transform[];
80
87
 
88
+ initVariables = new Map<string, string>();
89
+
81
90
  constructor(obfuscator, priority: number = -1) {
82
91
  ok(obfuscator instanceof Obfuscator, "obfuscator should be an Obfuscator");
83
92
 
@@ -336,6 +345,26 @@ export default class Transform {
336
345
  return identifier;
337
346
  }
338
347
 
348
+ createInitVariable = (value: ITemplate, parents: Node[]) => {
349
+ var key = value.templates[0];
350
+ if (this.initVariables.has(key)) {
351
+ return this.initVariables.get(key);
352
+ }
353
+
354
+ var root = parents[parents.length - 1];
355
+ ok(root.type === "Program");
356
+
357
+ var name = this.getPlaceholder();
358
+ this.initVariables.set(key, name);
359
+
360
+ prepend(
361
+ root,
362
+ VariableDeclaration(VariableDeclarator(name, value.single().expression))
363
+ );
364
+
365
+ return name;
366
+ };
367
+
339
368
  /**
340
369
  * Smartly appends a comment to a Node.
341
370
  * - Includes the transformation's name.
@@ -74,19 +74,52 @@ export function isDirective(object: Node, parents: Node[]) {
74
74
  return parents[dIndex].expression == (parents[dIndex - 1] || object);
75
75
  }
76
76
 
77
+ export function isModuleSource(object: Node, parents: Node[]) {
78
+ if (!parents[0]) {
79
+ return false;
80
+ }
81
+
82
+ if (parents[0].type == "ImportDeclaration" && parents[0].source == object) {
83
+ return true;
84
+ }
85
+
86
+ if (parents[0].type == "ImportExpression" && parents[0].source == object) {
87
+ return true;
88
+ }
89
+
90
+ if (
91
+ parents[1] &&
92
+ parents[1].type == "CallExpression" &&
93
+ parents[1].arguments[0] === object &&
94
+ parents[1].callee.type == "Identifier"
95
+ ) {
96
+ if (
97
+ parents[1].callee.name == "require" ||
98
+ parents[1].callee.name == "import"
99
+ ) {
100
+ return true;
101
+ }
102
+ }
103
+
104
+ return false;
105
+ }
106
+
107
+ export function isMoveable(object: Node, parents: Node[]) {
108
+ return !isDirective(object, parents) && !isModuleSource(object, parents);
109
+ }
110
+
77
111
  export function isIndependent(object: Node, parents: Node[]) {
78
112
  if (object.type == "Literal") {
79
113
  return true;
80
114
  }
81
115
 
82
- var parent = parents[0];
83
-
84
116
  if (object.type == "Identifier") {
85
- var set = new Set(["null", "undefined"]);
86
- if (set.has(object.name)) {
117
+ if (primitiveIdentifiers.has(object.name)) {
87
118
  return true;
88
119
  }
89
- if (parent.type == "Property") {
120
+
121
+ var parent = parents[0];
122
+ if (parent && parent.type == "Property") {
90
123
  if (!parent.computed && parent.key == object) {
91
124
  return true;
92
125
  }
@@ -105,6 +138,7 @@ export function isIndependent(object: Node, parents: Node[]) {
105
138
  if (object != $object) {
106
139
  if (!Array.isArray($object) && !isIndependent($object, $parents)) {
107
140
  allowIt = false;
141
+ return "EXIT";
108
142
  }
109
143
  }
110
144
  });
package/src/util/gen.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ok } from "assert";
2
+ import { predictableFunctionTag } from "../constants";
2
3
  import { isValidIdentifier } from "./compare";
3
4
 
4
5
  export type Type =
@@ -306,6 +307,7 @@ export function FunctionExpression(params: Node[], body: any[]) {
306
307
  generator: false,
307
308
  expression: false,
308
309
  async: false,
310
+ [predictableFunctionTag]: true,
309
311
  };
310
312
  }
311
313
 
@@ -340,6 +342,7 @@ export function FunctionDeclaration(
340
342
  generator: false,
341
343
  expression: false,
342
344
  async: false,
345
+ [predictableFunctionTag]: true,
343
346
  };
344
347
  }
345
348
 
@@ -529,16 +532,20 @@ export function AddComment(node: Node, text: string) {
529
532
  return node;
530
533
  }
531
534
 
535
+ export function Super() {
536
+ return { type: "Super" };
537
+ }
538
+
532
539
  export function MethodDefinition(
533
- identifier: Node,
540
+ key: Node,
534
541
  functionExpression: Node,
535
542
  kind: "method" | "constructor" | "get" | "set",
536
- isStatic = true,
543
+ isStatic = false,
537
544
  computed = false
538
545
  ) {
539
546
  return {
540
547
  type: "MethodDefinition",
541
- key: identifier,
548
+ key: key,
542
549
  computed: computed,
543
550
  value: functionExpression,
544
551
  kind: kind,
@@ -94,9 +94,14 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
94
94
  parents.find((x) => x.type == "VariableDeclaration") &&
95
95
  objectPatternCheck(object, parents);
96
96
 
97
+ var functionIndex = parents.findIndex((x) => isFunction(x));
98
+
97
99
  // Assignment pattern check!
98
100
  if (isVariableDeclaration) {
99
- var slicedParents = parents.slice(0, varIndex - 1);
101
+ var slicedParents = parents.slice(
102
+ 0,
103
+ functionIndex != -1 ? Math.min(varIndex, functionIndex) : varIndex
104
+ );
100
105
  var i = 0;
101
106
  for (var parent of slicedParents) {
102
107
  var childNode = slicedParents[i - 1] || object;
@@ -113,8 +118,6 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
113
118
  forIndex != -1 &&
114
119
  parents[forIndex].init == (parents[forIndex - 1] || object);
115
120
 
116
- var functionIndex = parents.findIndex((x) => isFunction(x));
117
-
118
121
  var isFunctionDeclaration =
119
122
  functionIndex != -1 &&
120
123
  parents[functionIndex].type == "FunctionDeclaration" &&
@@ -3,6 +3,12 @@ import { getBlock, isBlock } from "../traverse";
3
3
  import { Node } from "./gen";
4
4
  import { getIdentifierInfo, validateChain } from "./identifiers";
5
5
 
6
+ export function isClass(object: Node): boolean {
7
+ return (
8
+ object.type === "ClassDeclaration" || object.type === "ClassExpression"
9
+ );
10
+ }
11
+
6
12
  /**
7
13
  * - `FunctionDeclaration`
8
14
  * - `FunctionExpression`
@@ -18,6 +24,17 @@ export function isFunction(object: Node): boolean {
18
24
  ].includes(object && object.type);
19
25
  }
20
26
 
27
+ export function isStrictModeFunction(object: Node): boolean {
28
+ ok(isFunction(object));
29
+
30
+ return (
31
+ object.body.type === "BlockStatement" &&
32
+ object.body.body[0] &&
33
+ object.body.body[0].type === "ExpressionStatement" &&
34
+ object.body.body[0].directive === "use strict"
35
+ );
36
+ }
37
+
21
38
  /**
22
39
  * The function context where the object is.
23
40
  *
package/src/util/scope.ts CHANGED
@@ -1,9 +1,21 @@
1
+ import { ok } from "assert";
1
2
  import { isBlock } from "../traverse";
3
+ import { Node } from "./gen";
2
4
 
3
- export function isLexicalScope(object) {
5
+ export function isLexicalScope(object: Node) {
4
6
  return isBlock(object) || object.type == "SwitchCase";
5
7
  }
6
8
 
7
- export function getLexicalScope(object, parents) {
9
+ export function getLexicalScope(object: Node, parents: Node[]): Node {
8
10
  return [object, ...parents].find((node) => isLexicalScope(node));
9
11
  }
12
+
13
+ export function getLexicalScopeBody(object: Node): Node[] {
14
+ ok(isLexicalScope(object));
15
+
16
+ return isBlock(object)
17
+ ? object.body
18
+ : object.type === "SwitchCase"
19
+ ? object.consequent
20
+ : ok("Unhandled case");
21
+ }
@@ -32,12 +32,18 @@ test("Variant #1: Cash.js on High Preset (Strict Mode)", async () => {
32
32
  $: false,
33
33
  } as any;
34
34
  window.window = window;
35
+ global.window = window;
35
36
 
36
- // writeFileSync(join(__dirname, "Cash.output.js"), output, {
37
- // encoding: "utf-8",
38
- // });
37
+ try {
38
+ eval(output);
39
+ } catch (e) {
40
+ console.error(e);
41
+ writeFileSync("dev.output.js", output, {
42
+ encoding: "utf-8",
43
+ });
39
44
 
40
- eval(output);
45
+ expect(true).toStrictEqual(false);
46
+ }
41
47
 
42
48
  expect(window).toHaveProperty("cash");
43
49
  });