js-confuser 1.7.2 → 2.0.0-alpha.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.
Files changed (263) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +6 -4
  2. package/.github/workflows/node.js.yml +1 -1
  3. package/CHANGELOG.md +105 -0
  4. package/Migration.md +57 -0
  5. package/README.md +23 -913
  6. package/dist/constants.js +69 -13
  7. package/dist/index.js +108 -152
  8. package/dist/obfuscator.js +316 -118
  9. package/dist/options.js +1 -109
  10. package/dist/order.js +30 -30
  11. package/dist/presets.js +47 -45
  12. package/dist/probability.js +25 -32
  13. package/dist/templates/bufferToStringTemplate.js +9 -0
  14. package/dist/templates/deadCodeTemplates.js +9 -0
  15. package/dist/templates/getGlobalTemplate.js +19 -0
  16. package/dist/templates/integrityTemplate.js +30 -0
  17. package/dist/templates/setFunctionLengthTemplate.js +9 -0
  18. package/dist/templates/stringCompressionTemplate.js +10 -0
  19. package/dist/templates/tamperProtectionTemplates.js +21 -0
  20. package/dist/templates/template.js +213 -93
  21. package/dist/transforms/astScrambler.js +100 -0
  22. package/dist/transforms/calculator.js +70 -127
  23. package/dist/transforms/controlFlowFlattening.js +1182 -0
  24. package/dist/transforms/deadCode.js +62 -577
  25. package/dist/transforms/dispatcher.js +300 -309
  26. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +88 -189
  27. package/dist/transforms/extraction/objectExtraction.js +131 -215
  28. package/dist/transforms/finalizer.js +56 -59
  29. package/dist/transforms/flatten.js +275 -276
  30. package/dist/transforms/functionOutlining.js +230 -0
  31. package/dist/transforms/identifier/globalConcealing.js +217 -103
  32. package/dist/transforms/identifier/movedDeclarations.js +167 -91
  33. package/dist/transforms/identifier/renameVariables.js +240 -187
  34. package/dist/transforms/lock/integrity.js +61 -184
  35. package/dist/transforms/lock/lock.js +263 -303
  36. package/dist/transforms/minify.js +431 -436
  37. package/dist/transforms/opaquePredicates.js +65 -118
  38. package/dist/transforms/pack.js +160 -0
  39. package/dist/transforms/plugin.js +179 -0
  40. package/dist/transforms/preparation.js +263 -163
  41. package/dist/transforms/renameLabels.js +132 -56
  42. package/dist/transforms/rgf.js +142 -240
  43. package/dist/transforms/shuffle.js +52 -145
  44. package/dist/transforms/string/encoding.js +45 -173
  45. package/dist/transforms/string/stringCompression.js +81 -126
  46. package/dist/transforms/string/stringConcealing.js +189 -224
  47. package/dist/transforms/string/stringEncoding.js +32 -40
  48. package/dist/transforms/string/stringSplitting.js +54 -55
  49. package/dist/transforms/variableMasking.js +232 -0
  50. package/dist/utils/ControlObject.js +125 -0
  51. package/dist/utils/IntGen.js +46 -0
  52. package/dist/utils/NameGen.js +106 -0
  53. package/dist/utils/ast-utils.js +560 -0
  54. package/dist/utils/function-utils.js +56 -0
  55. package/dist/utils/gen-utils.js +48 -0
  56. package/dist/utils/node.js +77 -0
  57. package/dist/utils/object-utils.js +21 -0
  58. package/dist/utils/random-utils.js +91 -0
  59. package/dist/utils/static-utils.js +64 -0
  60. package/dist/validateOptions.js +122 -0
  61. package/index.d.ts +1 -17
  62. package/package.json +27 -22
  63. package/src/constants.ts +139 -77
  64. package/src/index.ts +70 -163
  65. package/src/obfuscationResult.ts +43 -0
  66. package/src/obfuscator.ts +328 -135
  67. package/src/options.ts +154 -623
  68. package/src/order.ts +14 -14
  69. package/src/presets.ts +39 -34
  70. package/src/probability.ts +21 -36
  71. package/src/templates/{bufferToString.ts → bufferToStringTemplate.ts} +5 -54
  72. package/src/templates/deadCodeTemplates.ts +1185 -0
  73. package/src/templates/getGlobalTemplate.ts +72 -0
  74. package/src/templates/integrityTemplate.ts +69 -0
  75. package/src/templates/setFunctionLengthTemplate.ts +11 -0
  76. package/src/templates/stringCompressionTemplate.ts +42 -0
  77. package/src/templates/tamperProtectionTemplates.ts +116 -0
  78. package/src/templates/template.ts +183 -92
  79. package/src/transforms/astScrambler.ts +99 -0
  80. package/src/transforms/calculator.ts +96 -224
  81. package/src/transforms/controlFlowFlattening.ts +1594 -0
  82. package/src/transforms/deadCode.ts +85 -628
  83. package/src/transforms/dispatcher.ts +431 -636
  84. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +147 -299
  85. package/src/transforms/extraction/objectExtraction.ts +160 -333
  86. package/src/transforms/finalizer.ts +63 -64
  87. package/src/transforms/flatten.ts +439 -557
  88. package/src/transforms/functionOutlining.ts +225 -0
  89. package/src/transforms/identifier/globalConcealing.ts +261 -189
  90. package/src/transforms/identifier/movedDeclarations.ts +228 -142
  91. package/src/transforms/identifier/renameVariables.ts +252 -258
  92. package/src/transforms/lock/integrity.ts +84 -260
  93. package/src/transforms/lock/lock.ts +342 -491
  94. package/src/transforms/minify.ts +523 -663
  95. package/src/transforms/opaquePredicates.ts +90 -229
  96. package/src/transforms/pack.ts +195 -0
  97. package/src/transforms/plugin.ts +185 -0
  98. package/src/transforms/preparation.ts +337 -215
  99. package/src/transforms/renameLabels.ts +176 -77
  100. package/src/transforms/rgf.ts +293 -386
  101. package/src/transforms/shuffle.ts +80 -254
  102. package/src/transforms/string/encoding.ts +26 -129
  103. package/src/transforms/string/stringCompression.ts +118 -236
  104. package/src/transforms/string/stringConcealing.ts +255 -339
  105. package/src/transforms/string/stringEncoding.ts +28 -47
  106. package/src/transforms/string/stringSplitting.ts +61 -75
  107. package/src/transforms/variableMasking.ts +257 -0
  108. package/src/utils/ControlObject.ts +141 -0
  109. package/src/utils/IntGen.ts +33 -0
  110. package/src/utils/NameGen.ts +106 -0
  111. package/src/utils/ast-utils.ts +667 -0
  112. package/src/utils/function-utils.ts +50 -0
  113. package/src/utils/gen-utils.ts +48 -0
  114. package/src/utils/node.ts +78 -0
  115. package/src/utils/object-utils.ts +21 -0
  116. package/src/utils/random-utils.ts +79 -0
  117. package/src/utils/static-utils.ts +66 -0
  118. package/src/validateOptions.ts +256 -0
  119. package/tsconfig.json +13 -8
  120. package/babel.config.js +0 -12
  121. package/dev.js +0 -8
  122. package/dist/compiler.js +0 -34
  123. package/dist/parser.js +0 -59
  124. package/dist/precedence.js +0 -66
  125. package/dist/templates/bufferToString.js +0 -108
  126. package/dist/templates/crash.js +0 -59
  127. package/dist/templates/es5.js +0 -137
  128. package/dist/templates/functionLength.js +0 -34
  129. package/dist/templates/globals.js +0 -9
  130. package/dist/transforms/antiTooling.js +0 -88
  131. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +0 -1281
  132. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +0 -131
  133. package/dist/transforms/es5/antiClass.js +0 -164
  134. package/dist/transforms/es5/antiDestructuring.js +0 -193
  135. package/dist/transforms/es5/antiES6Object.js +0 -185
  136. package/dist/transforms/es5/antiSpreadOperator.js +0 -35
  137. package/dist/transforms/es5/antiTemplate.js +0 -66
  138. package/dist/transforms/es5/es5.js +0 -123
  139. package/dist/transforms/extraction/classExtraction.js +0 -83
  140. package/dist/transforms/identifier/globalAnalysis.js +0 -70
  141. package/dist/transforms/identifier/variableAnalysis.js +0 -104
  142. package/dist/transforms/lock/antiDebug.js +0 -76
  143. package/dist/transforms/stack.js +0 -343
  144. package/dist/transforms/transform.js +0 -350
  145. package/dist/traverse.js +0 -110
  146. package/dist/util/compare.js +0 -145
  147. package/dist/util/gen.js +0 -564
  148. package/dist/util/guard.js +0 -9
  149. package/dist/util/identifiers.js +0 -355
  150. package/dist/util/insert.js +0 -362
  151. package/dist/util/math.js +0 -19
  152. package/dist/util/object.js +0 -40
  153. package/dist/util/random.js +0 -130
  154. package/dist/util/scope.js +0 -20
  155. package/docs/ControlFlowFlattening.md +0 -595
  156. package/docs/Countermeasures.md +0 -63
  157. package/docs/ES5.md +0 -197
  158. package/docs/Integrity.md +0 -75
  159. package/docs/RGF.md +0 -419
  160. package/samples/example.js +0 -15
  161. package/samples/high.js +0 -1
  162. package/samples/input.js +0 -3
  163. package/samples/javascriptobfuscator.com.js +0 -8
  164. package/samples/jscrambler_advanced.js +0 -1894
  165. package/samples/jscrambler_light.js +0 -1134
  166. package/samples/low.js +0 -1
  167. package/samples/medium.js +0 -1
  168. package/samples/obfuscator.io.js +0 -1686
  169. package/samples/preemptive.com.js +0 -16
  170. package/src/compiler.ts +0 -35
  171. package/src/parser.ts +0 -49
  172. package/src/precedence.ts +0 -61
  173. package/src/templates/crash.ts +0 -55
  174. package/src/templates/es5.ts +0 -131
  175. package/src/templates/functionLength.ts +0 -32
  176. package/src/templates/globals.ts +0 -3
  177. package/src/transforms/antiTooling.ts +0 -102
  178. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +0 -2146
  179. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +0 -179
  180. package/src/transforms/es5/antiClass.ts +0 -272
  181. package/src/transforms/es5/antiDestructuring.ts +0 -294
  182. package/src/transforms/es5/antiES6Object.ts +0 -267
  183. package/src/transforms/es5/antiSpreadOperator.ts +0 -56
  184. package/src/transforms/es5/antiTemplate.ts +0 -98
  185. package/src/transforms/es5/es5.ts +0 -149
  186. package/src/transforms/extraction/classExtraction.ts +0 -168
  187. package/src/transforms/identifier/globalAnalysis.ts +0 -85
  188. package/src/transforms/identifier/variableAnalysis.ts +0 -118
  189. package/src/transforms/lock/antiDebug.ts +0 -112
  190. package/src/transforms/stack.ts +0 -551
  191. package/src/transforms/transform.ts +0 -453
  192. package/src/traverse.ts +0 -120
  193. package/src/types.ts +0 -131
  194. package/src/util/compare.ts +0 -181
  195. package/src/util/gen.ts +0 -651
  196. package/src/util/guard.ts +0 -7
  197. package/src/util/identifiers.ts +0 -494
  198. package/src/util/insert.ts +0 -419
  199. package/src/util/math.ts +0 -15
  200. package/src/util/object.ts +0 -39
  201. package/src/util/random.ts +0 -141
  202. package/src/util/scope.ts +0 -21
  203. package/test/code/Cash.src.js +0 -1011
  204. package/test/code/Cash.test.ts +0 -49
  205. package/test/code/Dynamic.src.js +0 -118
  206. package/test/code/Dynamic.test.ts +0 -49
  207. package/test/code/ES6.src.js +0 -235
  208. package/test/code/ES6.test.ts +0 -42
  209. package/test/code/NewFeatures.test.ts +0 -19
  210. package/test/code/StrictMode.src.js +0 -65
  211. package/test/code/StrictMode.test.js +0 -37
  212. package/test/compare.test.ts +0 -104
  213. package/test/index.test.ts +0 -249
  214. package/test/options.test.ts +0 -132
  215. package/test/presets.test.ts +0 -22
  216. package/test/probability.test.ts +0 -44
  217. package/test/templates/template.test.ts +0 -14
  218. package/test/transforms/antiTooling.test.ts +0 -52
  219. package/test/transforms/calculator.test.ts +0 -78
  220. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +0 -1274
  221. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +0 -192
  222. package/test/transforms/deadCode.test.ts +0 -85
  223. package/test/transforms/dispatcher.test.ts +0 -457
  224. package/test/transforms/es5/antiClass.test.ts +0 -427
  225. package/test/transforms/es5/antiDestructuring.test.ts +0 -157
  226. package/test/transforms/es5/antiES6Object.test.ts +0 -245
  227. package/test/transforms/es5/antiTemplate.test.ts +0 -116
  228. package/test/transforms/es5/es5.test.ts +0 -110
  229. package/test/transforms/extraction/classExtraction.test.ts +0 -86
  230. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +0 -200
  231. package/test/transforms/extraction/objectExtraction.test.ts +0 -491
  232. package/test/transforms/flatten.test.ts +0 -721
  233. package/test/transforms/hexadecimalNumbers.test.ts +0 -62
  234. package/test/transforms/identifier/globalConcealing.test.ts +0 -72
  235. package/test/transforms/identifier/movedDeclarations.test.ts +0 -275
  236. package/test/transforms/identifier/renameVariables.test.ts +0 -621
  237. package/test/transforms/lock/antiDebug.test.ts +0 -66
  238. package/test/transforms/lock/browserLock.test.ts +0 -129
  239. package/test/transforms/lock/countermeasures.test.ts +0 -100
  240. package/test/transforms/lock/integrity.test.ts +0 -161
  241. package/test/transforms/lock/lock.test.ts +0 -204
  242. package/test/transforms/lock/osLock.test.ts +0 -312
  243. package/test/transforms/lock/selfDefending.test.ts +0 -68
  244. package/test/transforms/minify.test.ts +0 -575
  245. package/test/transforms/opaquePredicates.test.ts +0 -43
  246. package/test/transforms/preparation.test.ts +0 -157
  247. package/test/transforms/renameLabels.test.ts +0 -95
  248. package/test/transforms/rgf.test.ts +0 -378
  249. package/test/transforms/shuffle.test.ts +0 -135
  250. package/test/transforms/stack.test.ts +0 -573
  251. package/test/transforms/string/stringCompression.test.ts +0 -120
  252. package/test/transforms/string/stringConcealing.test.ts +0 -299
  253. package/test/transforms/string/stringEncoding.test.ts +0 -95
  254. package/test/transforms/string/stringSplitting.test.ts +0 -135
  255. package/test/transforms/transform.test.ts +0 -66
  256. package/test/traverse.test.ts +0 -139
  257. package/test/util/compare.test.ts +0 -34
  258. package/test/util/gen.test.ts +0 -121
  259. package/test/util/identifiers.test.ts +0 -253
  260. package/test/util/insert.test.ts +0 -142
  261. package/test/util/math.test.ts +0 -5
  262. package/test/util/random.test.ts +0 -71
  263. /package/dist/{types.js → obfuscationResult.js} +0 -0
@@ -1,557 +1,439 @@
1
- import { ok } from "assert";
2
- import {
3
- noRenameVariablePrefix,
4
- predictableFunctionTag,
5
- reservedIdentifiers,
6
- } from "../constants";
7
- import { ObfuscateOrder } from "../order";
8
- import { walk } from "../traverse";
9
- import {
10
- Identifier,
11
- ReturnStatement,
12
- VariableDeclaration,
13
- VariableDeclarator,
14
- CallExpression,
15
- MemberExpression,
16
- ExpressionStatement,
17
- AssignmentExpression,
18
- Node,
19
- BlockStatement,
20
- ArrayPattern,
21
- FunctionExpression,
22
- ObjectExpression,
23
- Property,
24
- Literal,
25
- AwaitExpression,
26
- FunctionDeclaration,
27
- SpreadElement,
28
- UnaryExpression,
29
- RestElement,
30
- } from "../util/gen";
31
- import { getIdentifierInfo } from "../util/identifiers";
32
- import {
33
- getBlockBody,
34
- prepend,
35
- clone,
36
- getDefiningContext,
37
- computeFunctionLength,
38
- } from "../util/insert";
39
- import { shuffle } from "../util/random";
40
- import Transform from "./transform";
41
- import { FunctionLengthTemplate } from "../templates/functionLength";
42
- import { ObjectDefineProperty } from "../templates/globals";
43
-
44
- /**
45
- * Flatten takes functions and isolates them from their original scope, and brings it to the top level of the program.
46
- *
47
- * An additional `flatObject` parameter is passed in, giving access to the original scoped variables.
48
- *
49
- * The `flatObject` uses `get` and `set` properties to allow easy an AST transformation:
50
- *
51
- * ```js
52
- * // Input
53
- * function myFunction(myParam){
54
- * modified = true;
55
- * if(reference) {
56
- *
57
- * }
58
- * ...
59
- * console.log(myParam);
60
- * }
61
- *
62
- * // Output
63
- * function myFunction_flat([myParam], flatObject){
64
- * flatObject["set_modified"] = true;
65
- * if(flatObject["get_reference"]) {
66
- *
67
- * }
68
- * ...
69
- * console.log(myParam)
70
- * }
71
- *
72
- * function myFunction(){
73
- * var flatObject = {
74
- * set set_modified(v) { modified = v }
75
- * get get_reference() { return reference }
76
- * }
77
- * return myFunction_flat([...arguments], flatObject)
78
- * }
79
- * ```
80
- *
81
- * Flatten is used to make functions eligible for the RGF transformation.
82
- *
83
- * - `myFunction_flat` is now eligible because it does not rely on outside scoped variables
84
- */
85
- export default class Flatten extends Transform {
86
- isDebug = false;
87
-
88
- definedNames: Map<Node, Set<string>>;
89
-
90
- // Array of FunctionDeclaration nodes
91
- flattenedFns: Node[];
92
- gen: ReturnType<Transform["getGenerator"]>;
93
-
94
- functionLengthName: string;
95
-
96
- constructor(o) {
97
- super(o, ObfuscateOrder.Flatten);
98
-
99
- this.definedNames = new Map();
100
- this.flattenedFns = [];
101
- this.gen = this.getGenerator("mangled");
102
-
103
- if (this.isDebug) {
104
- console.warn("Flatten debug mode");
105
- }
106
- }
107
-
108
- apply(tree) {
109
- super.apply(tree);
110
-
111
- if (this.flattenedFns.length) {
112
- prepend(tree, ...this.flattenedFns);
113
- }
114
- }
115
-
116
- match(object: Node, parents: Node[]) {
117
- return (
118
- (object.type == "FunctionDeclaration" ||
119
- object.type === "FunctionExpression") &&
120
- object.body.type == "BlockStatement" &&
121
- !object.$requiresEval &&
122
- !object.generator &&
123
- !object.params.find((x) => x.type !== "Identifier")
124
- );
125
- }
126
-
127
- transform(object: Node, parents: Node[]) {
128
- return () => {
129
- if (parents[0]) {
130
- // Don't change class methods
131
- if (
132
- parents[0].type === "MethodDefinition" &&
133
- parents[0].value === object
134
- ) {
135
- return;
136
- }
137
-
138
- // Don't change getter/setter methods
139
- if (
140
- parents[0].type === "Property" &&
141
- parents[0].value === object &&
142
- (parents[0].kind !== "init" || parents[0].method)
143
- ) {
144
- return;
145
- }
146
- }
147
-
148
- ok(
149
- object.type === "FunctionDeclaration" ||
150
- object.type === "FunctionExpression"
151
- );
152
-
153
- // The name is purely for debugging purposes
154
- var currentFnName =
155
- object.type === "FunctionDeclaration"
156
- ? object.id?.name
157
- : parents[0]?.type === "VariableDeclarator" &&
158
- parents[0].id?.type === "Identifier" &&
159
- parents[0].id?.name;
160
-
161
- if (parents[0]?.type === "Property" && parents[0]?.key) {
162
- currentFnName = currentFnName || String(parents[0]?.key?.name);
163
- }
164
-
165
- if (!currentFnName) currentFnName = "unnamed";
166
-
167
- var definedMap = new Map<Node, Set<string>>();
168
-
169
- var illegal = new Set<string>();
170
- var isIllegal = false;
171
-
172
- var identifierNodes: [
173
- Node,
174
- Node[],
175
- ReturnType<typeof getIdentifierInfo>
176
- ][] = [];
177
-
178
- walk(object, parents, (o, p) => {
179
- if (
180
- (o.type === "Identifier" && o.name === "arguments") ||
181
- (o.type === "UnaryExpression" && o.operator === "delete") ||
182
- o.type == "ThisExpression" ||
183
- o.type == "Super" ||
184
- o.type == "MetaProperty"
185
- ) {
186
- isIllegal = true;
187
- return "EXIT";
188
- }
189
-
190
- if (
191
- o.type == "Identifier" &&
192
- o !== object.id &&
193
- !this.options.globalVariables.has(o.name) &&
194
- !reservedIdentifiers.has(o.name)
195
- ) {
196
- var info = getIdentifierInfo(o, p);
197
- if (!info.spec.isReferenced) {
198
- return;
199
- }
200
-
201
- if (
202
- info.spec.isExported ||
203
- o.name.startsWith(noRenameVariablePrefix)
204
- ) {
205
- illegal.add(o.name);
206
-
207
- return;
208
- }
209
-
210
- if (info.spec.isDefined) {
211
- var definingContext = getDefiningContext(o, p);
212
-
213
- if (!definedMap.has(definingContext)) {
214
- definedMap.set(definingContext, new Set([o.name]));
215
- } else {
216
- definedMap.get(definingContext).add(o.name);
217
- }
218
- return;
219
- }
220
-
221
- var isDefined = p.find(
222
- (x) => definedMap.has(x) && definedMap.get(x).has(o.name)
223
- );
224
-
225
- if (!isDefined) {
226
- identifierNodes.push([o, p, info]);
227
- }
228
- }
229
-
230
- if (o.type == "TryStatement") {
231
- isIllegal = true;
232
- return "EXIT";
233
- }
234
- });
235
-
236
- if (isIllegal) {
237
- return;
238
- }
239
- if (illegal.size) {
240
- return;
241
- }
242
-
243
- var newFnName =
244
- this.getPlaceholder() +
245
- "_flat_" +
246
- currentFnName +
247
- predictableFunctionTag;
248
- var flatObjectName = this.getPlaceholder() + "_flat_object";
249
-
250
- const getFlatObjectMember = (propertyName: string) => {
251
- return MemberExpression(
252
- Identifier(flatObjectName),
253
- Literal(propertyName),
254
- true
255
- );
256
- };
257
-
258
- var getterPropNames: { [identifierName: string]: string } =
259
- Object.create(null);
260
- var setterPropNames: { [identifierName: string]: string } =
261
- Object.create(null);
262
- var typeofPropNames: { [identifierName: string]: string } =
263
- Object.create(null);
264
- var callPropNames: { [identifierName: string]: string } =
265
- Object.create(null);
266
-
267
- for (var [o, p, info] of identifierNodes) {
268
- var identifierName: string = o.name;
269
- if (
270
- p.find(
271
- (x) => definedMap.has(x) && definedMap.get(x).has(identifierName)
272
- )
273
- )
274
- continue;
275
-
276
- ok(!info.spec.isDefined);
277
-
278
- var type = info.spec.isModified ? "setter" : "getter";
279
-
280
- switch (type) {
281
- case "setter":
282
- var setterPropName = setterPropNames[identifierName];
283
- if (typeof setterPropName === "undefined") {
284
- // No getter function made yet, make it (Try to re-use getter name if available)
285
- setterPropName =
286
- getterPropNames[identifierName] ||
287
- (this.isDebug ? "set_" + identifierName : this.gen.generate());
288
- setterPropNames[identifierName] = setterPropName;
289
- }
290
-
291
- // If an update expression, ensure a getter function is also available. Ex: a++
292
- if (p[0].type === "UpdateExpression") {
293
- getterPropNames[identifierName] = setterPropName;
294
- } else {
295
- // If assignment on member expression, ensure a getter function is also available: Ex. myObject.property = ...
296
- var assignmentIndex = p.findIndex(
297
- (x) => x.type === "AssignmentExpression"
298
- );
299
- if (
300
- assignmentIndex !== -1 &&
301
- p[assignmentIndex].left.type !== "Identifier"
302
- ) {
303
- getterPropNames[identifierName] = setterPropName;
304
- }
305
- }
306
-
307
- // calls flatObject.set_identifier_value(newValue)
308
- this.replace(o, getFlatObjectMember(setterPropName));
309
- break;
310
-
311
- case "getter":
312
- var getterPropName = getterPropNames[identifierName];
313
- if (typeof getterPropName === "undefined") {
314
- // No getter function made yet, make it (Try to re-use setter name if available)
315
- getterPropName =
316
- setterPropNames[identifierName] ||
317
- (this.isDebug ? "get_" + identifierName : this.gen.generate());
318
- getterPropNames[identifierName] = getterPropName;
319
- }
320
-
321
- // Typeof expression check
322
- if (
323
- p[0].type === "UnaryExpression" &&
324
- p[0].operator === "typeof" &&
325
- p[0].argument === o
326
- ) {
327
- var typeofPropName = typeofPropNames[identifierName];
328
- if (typeof typeofPropName === "undefined") {
329
- // No typeof getter function made yet, make it (Don't re-use getter/setter names)
330
- typeofPropName = this.isDebug
331
- ? "get_typeof_" + identifierName
332
- : this.gen.generate();
333
- typeofPropNames[identifierName] = typeofPropName;
334
- }
335
-
336
- // Replace the entire unary expression not just the identifier node
337
- // calls flatObject.get_typeof_identifier()
338
- this.replace(p[0], getFlatObjectMember(typeofPropName));
339
- break;
340
- }
341
-
342
- // Bound call-expression check
343
- if (p[0].type === "CallExpression" && p[0].callee === o) {
344
- var callPropName = callPropNames[identifierName];
345
- if (typeof callPropName === "undefined") {
346
- callPropName = this.isDebug
347
- ? "call_" + identifierName
348
- : this.gen.generate();
349
- callPropNames[identifierName] = callPropName;
350
- }
351
-
352
- // Replace the entire call expression not just the identifier node
353
- // calls flatObject.call_identifier(...arguments)
354
- this.replace(
355
- p[0],
356
- CallExpression(
357
- getFlatObjectMember(callPropName),
358
- p[0].arguments
359
- )
360
- );
361
- break;
362
- }
363
-
364
- // calls flatObject.get_identifier_value()
365
- this.replace(o, getFlatObjectMember(getterPropName));
366
- break;
367
- }
368
- }
369
-
370
- // Create the getter and setter functions
371
- var flatObjectProperties: Node[] = [];
372
-
373
- // Getter functions
374
- for (var identifierName in getterPropNames) {
375
- var getterPropName = getterPropNames[identifierName];
376
-
377
- flatObjectProperties.push(
378
- Property(
379
- Literal(getterPropName),
380
- FunctionExpression(
381
- [],
382
- [ReturnStatement(Identifier(identifierName))]
383
- ),
384
- true,
385
- "get"
386
- )
387
- );
388
- }
389
-
390
- // Get typeof functions
391
- for (var identifierName in typeofPropNames) {
392
- var typeofPropName = typeofPropNames[identifierName];
393
-
394
- flatObjectProperties.push(
395
- Property(
396
- Literal(typeofPropName),
397
- FunctionExpression(
398
- [],
399
- [
400
- ReturnStatement(
401
- UnaryExpression("typeof", Identifier(identifierName))
402
- ),
403
- ]
404
- ),
405
- true,
406
- "get"
407
- )
408
- );
409
- }
410
-
411
- // Call functions
412
- for (var identifierName in callPropNames) {
413
- var callPropName = callPropNames[identifierName];
414
- var argumentsName = this.getPlaceholder();
415
- flatObjectProperties.push(
416
- Property(
417
- Literal(callPropName),
418
- FunctionExpression(
419
- [RestElement(Identifier(argumentsName))],
420
- [
421
- ReturnStatement(
422
- CallExpression(Identifier(identifierName), [
423
- SpreadElement(Identifier(argumentsName)),
424
- ])
425
- ),
426
- ]
427
- ),
428
- true
429
- )
430
- );
431
- }
432
-
433
- // Setter functions
434
- for (var identifierName in setterPropNames) {
435
- var setterPropName = setterPropNames[identifierName];
436
- var newValueParameterName = this.getPlaceholder();
437
-
438
- flatObjectProperties.push(
439
- Property(
440
- Literal(setterPropName),
441
- FunctionExpression(
442
- [Identifier(newValueParameterName)],
443
- [
444
- ExpressionStatement(
445
- AssignmentExpression(
446
- "=",
447
- Identifier(identifierName),
448
- Identifier(newValueParameterName)
449
- )
450
- ),
451
- ]
452
- ),
453
- true,
454
- "set"
455
- )
456
- );
457
- }
458
-
459
- if (!this.isDebug) {
460
- shuffle(flatObjectProperties);
461
- }
462
-
463
- var newBody = getBlockBody(object.body);
464
-
465
- // Remove 'use strict' directive
466
- if (newBody.length > 0 && newBody[0].directive) {
467
- newBody.shift();
468
- }
469
-
470
- var newFunctionDeclaration = FunctionDeclaration(
471
- newFnName,
472
- [ArrayPattern(clone(object.params)), Identifier(flatObjectName)],
473
- newBody
474
- );
475
-
476
- newFunctionDeclaration.async = !!object.async;
477
- newFunctionDeclaration.generator = false;
478
-
479
- this.flattenedFns.push(newFunctionDeclaration);
480
-
481
- var argumentsName = this.getPlaceholder();
482
-
483
- // newFn.call([...arguments], flatObject)
484
- var callExpression = CallExpression(Identifier(newFnName), [
485
- Identifier(argumentsName),
486
- Identifier(flatObjectName),
487
- ]);
488
-
489
- var newObjectBody: Node[] = [
490
- // var flatObject = { get(), set() };
491
- VariableDeclaration([
492
- VariableDeclarator(
493
- flatObjectName,
494
- ObjectExpression(flatObjectProperties)
495
- ),
496
- ]),
497
-
498
- ReturnStatement(
499
- newFunctionDeclaration.async
500
- ? AwaitExpression(callExpression)
501
- : callExpression
502
- ),
503
- ];
504
-
505
- object.body = BlockStatement(newObjectBody);
506
-
507
- // Preserve function.length property
508
- var originalFunctionLength = computeFunctionLength(object.params);
509
-
510
- object.params = [RestElement(Identifier(argumentsName))];
511
-
512
- if (this.options.preserveFunctionLength && originalFunctionLength !== 0) {
513
- if (!this.functionLengthName) {
514
- this.functionLengthName = this.getPlaceholder();
515
-
516
- prepend(
517
- parents[parents.length - 1] || object,
518
- FunctionLengthTemplate.single({
519
- name: this.functionLengthName,
520
- ObjectDefineProperty: this.createInitVariable(
521
- ObjectDefineProperty,
522
- parents
523
- ),
524
- })
525
- );
526
- }
527
-
528
- if (object.type === "FunctionDeclaration") {
529
- var body = parents[0];
530
- if (Array.isArray(body)) {
531
- var index = body.indexOf(object);
532
-
533
- body.splice(
534
- index + 1,
535
- 0,
536
- ExpressionStatement(
537
- CallExpression(Identifier(this.functionLengthName), [
538
- Identifier(object.id.name),
539
- Literal(originalFunctionLength),
540
- ])
541
- )
542
- );
543
- }
544
- } else {
545
- ok(object.type === "FunctionExpression");
546
- this.replace(
547
- object,
548
- CallExpression(Identifier(this.functionLengthName), [
549
- { ...object },
550
- Literal(originalFunctionLength),
551
- ])
552
- );
553
- }
554
- }
555
- };
556
- }
557
- }
1
+ import * as t from "@babel/types";
2
+ import { NodePath } from "@babel/core";
3
+ import {
4
+ ensureComputedExpression,
5
+ getFunctionName,
6
+ isDefiningIdentifier,
7
+ isModifiedIdentifier,
8
+ isStrictMode,
9
+ isVariableIdentifier,
10
+ prepend,
11
+ prependProgram,
12
+ } from "../utils/ast-utils";
13
+ import { PluginArg, PluginObject } from "./plugin";
14
+ import { computeProbabilityMap } from "../probability";
15
+ import { Order } from "../order";
16
+ import { NodeSymbol, PREDICTABLE, UNSAFE } from "../constants";
17
+ import {
18
+ computeFunctionLength,
19
+ isVariableFunctionIdentifier,
20
+ } from "../utils/function-utils";
21
+ import { ok } from "assert";
22
+ import { Scope } from "@babel/traverse";
23
+ import { NameGen } from "../utils/NameGen";
24
+
25
+ export default ({ Plugin }: PluginArg): PluginObject => {
26
+ const me = Plugin(Order.Flatten, {
27
+ changeData: {
28
+ functions: 0,
29
+ },
30
+ });
31
+ const isDebug = false;
32
+
33
+ function flattenFunction(fnPath: NodePath<t.Function>) {
34
+ // Skip if already processed
35
+ if (me.isSkipped(fnPath)) return;
36
+
37
+ // Don't apply to generator functions
38
+ if (fnPath.node.generator) return;
39
+
40
+ // Skip getter/setter methods
41
+ if (fnPath.isObjectMethod() || fnPath.isClassMethod()) {
42
+ if (fnPath.node.kind !== "method") return;
43
+ }
44
+
45
+ // Do not apply to arrow functions
46
+ if (t.isArrowFunctionExpression(fnPath.node)) return;
47
+ if (!t.isBlockStatement(fnPath.node.body)) return;
48
+
49
+ // Skip if marked as unsafe
50
+ if ((fnPath.node as NodeSymbol)[UNSAFE]) return;
51
+
52
+ var program = fnPath.findParent((p) =>
53
+ p.isProgram()
54
+ ) as NodePath<t.Program>;
55
+
56
+ let functionName = getFunctionName(fnPath);
57
+ if (!t.isValidIdentifier(functionName, true)) {
58
+ functionName = "anonymous";
59
+ }
60
+
61
+ if (!computeProbabilityMap(me.options.flatten, functionName)) {
62
+ return;
63
+ }
64
+
65
+ const strictMode = fnPath.find((path) => isStrictMode(path));
66
+ if (strictMode === fnPath) return;
67
+
68
+ me.log("Transforming", functionName);
69
+
70
+ const flatObjectName = `${me.getPlaceholder()}_flat_object`;
71
+ const newFnName = `${me.getPlaceholder()}_flat_${functionName}`;
72
+
73
+ const nameGen = new NameGen(me.options.identifierGenerator);
74
+
75
+ function generateProp(originalName: string, type: string) {
76
+ var newPropertyName: string;
77
+ do {
78
+ newPropertyName = isDebug
79
+ ? type + "_" + originalName
80
+ : nameGen.generate();
81
+ } while (allPropertyNames.has(newPropertyName));
82
+
83
+ allPropertyNames.add(newPropertyName);
84
+
85
+ return newPropertyName;
86
+ }
87
+
88
+ const standardProps = new Map<string, string>();
89
+ const setterPropsNeeded = new Set<string>();
90
+ const typeofProps = new Map<string, string>();
91
+ const functionCallProps = new Map<string, string>();
92
+ const allPropertyNames = new Set();
93
+
94
+ const identifierPaths: NodePath<t.Identifier>[] = [];
95
+
96
+ // Traverse function to identify variables to be replaced with flat object properties
97
+ fnPath.traverse({
98
+ Identifier: {
99
+ exit(identifierPath) {
100
+ if (!isVariableIdentifier(identifierPath)) return;
101
+
102
+ if (
103
+ identifierPath.isBindingIdentifier() &&
104
+ isDefiningIdentifier(identifierPath)
105
+ )
106
+ return;
107
+
108
+ if (isVariableFunctionIdentifier(identifierPath)) return;
109
+
110
+ if ((identifierPath.node as NodeSymbol)[UNSAFE]) return;
111
+ const identifierName = identifierPath.node.name;
112
+
113
+ if (identifierName === "arguments") return;
114
+
115
+ var binding = identifierPath.scope.getBinding(identifierName);
116
+ if (!binding) {
117
+ return;
118
+ }
119
+
120
+ var definedLocal = identifierPath.scope;
121
+ do {
122
+ if (definedLocal.hasOwnBinding(identifierName)) return;
123
+ if (definedLocal === fnPath.scope) break;
124
+
125
+ definedLocal = definedLocal.parent;
126
+ if (definedLocal === program.scope)
127
+ ok(functionName + ":" + identifierName);
128
+ } while (definedLocal);
129
+
130
+ var cursor: Scope = fnPath.scope.parent;
131
+ var isOutsideVariable = false;
132
+
133
+ do {
134
+ if (cursor.hasBinding(identifierName)) {
135
+ isOutsideVariable = true;
136
+ break;
137
+ }
138
+ cursor = cursor.parent;
139
+ } while (cursor);
140
+
141
+ if (!isOutsideVariable) {
142
+ return;
143
+ }
144
+
145
+ identifierPaths.push(identifierPath);
146
+ },
147
+ },
148
+ });
149
+
150
+ me.log(
151
+ `Function ${functionName}`,
152
+ "requires",
153
+ Array.from(new Set(identifierPaths.map((x) => x.node.name)))
154
+ );
155
+
156
+ for (var identifierPath of identifierPaths) {
157
+ const identifierName = identifierPath.node.name;
158
+ if (typeof identifierName !== "string") continue;
159
+
160
+ const isTypeof = identifierPath.parentPath.isUnaryExpression({
161
+ operator: "typeof",
162
+ });
163
+ const isFunctionCall =
164
+ identifierPath.parentPath.isCallExpression() &&
165
+ identifierPath.parentPath.node.callee === identifierPath.node;
166
+
167
+ if (isTypeof) {
168
+ var typeofProp = typeofProps.get(identifierName);
169
+ if (!typeofProp) {
170
+ typeofProp = generateProp(identifierName, "typeof");
171
+ typeofProps.set(identifierName, typeofProp);
172
+ }
173
+
174
+ ensureComputedExpression(identifierPath.parentPath);
175
+
176
+ identifierPath.parentPath
177
+ .replaceWith(
178
+ t.memberExpression(
179
+ t.identifier(flatObjectName),
180
+ t.stringLiteral(typeofProp),
181
+ true
182
+ )
183
+ )[0]
184
+ .skip();
185
+ } else if (isFunctionCall) {
186
+ let functionCallProp = functionCallProps.get(identifierName);
187
+ if (!functionCallProp) {
188
+ functionCallProp = generateProp(identifierName, "call");
189
+ functionCallProps.set(identifierName, functionCallProp);
190
+ }
191
+
192
+ ensureComputedExpression(identifierPath);
193
+
194
+ // Replace identifier with a reference to the flat object property
195
+ identifierPath
196
+ .replaceWith(
197
+ t.memberExpression(
198
+ t.identifier(flatObjectName),
199
+ t.stringLiteral(functionCallProp),
200
+ true
201
+ )
202
+ )[0]
203
+ .skip();
204
+ } else {
205
+ let standardProp = standardProps.get(identifierName);
206
+ if (!standardProp) {
207
+ standardProp = generateProp(identifierName, "standard");
208
+ standardProps.set(identifierName, standardProp);
209
+ }
210
+
211
+ if (!setterPropsNeeded.has(identifierName)) {
212
+ // Only provide 'set' method if the variable is modified
213
+ var isModification = isModifiedIdentifier(identifierPath);
214
+
215
+ if (isModification) {
216
+ setterPropsNeeded.add(identifierName);
217
+ }
218
+ }
219
+
220
+ ensureComputedExpression(identifierPath);
221
+
222
+ // Replace identifier with a reference to the flat object property
223
+ identifierPath
224
+ .replaceWith(
225
+ t.memberExpression(
226
+ t.identifier(flatObjectName),
227
+ t.stringLiteral(standardProp),
228
+ true
229
+ )
230
+ )[0]
231
+ .skip();
232
+ }
233
+ }
234
+
235
+ // for (const prop of [...typeofProps.keys(), ...functionCallProps.keys()]) {
236
+ // if (!standardProps.has(prop)) {
237
+ // standardProps.set(prop, generateProp());
238
+ // }
239
+ // }
240
+
241
+ const flatObjectProperties: t.ObjectMember[] = [];
242
+
243
+ for (var entry of standardProps) {
244
+ const [identifierName, objectProp] = entry;
245
+
246
+ flatObjectProperties.push(
247
+ me.skip(
248
+ t.objectMethod(
249
+ "get",
250
+ t.stringLiteral(objectProp),
251
+ [],
252
+ t.blockStatement([t.returnStatement(t.identifier(identifierName))]),
253
+ false,
254
+ false,
255
+ false
256
+ )
257
+ )
258
+ );
259
+
260
+ // Not all properties need a setter
261
+ if (setterPropsNeeded.has(identifierName)) {
262
+ var valueArgName = me.getPlaceholder() + "_value";
263
+ flatObjectProperties.push(
264
+ me.skip(
265
+ t.objectMethod(
266
+ "set",
267
+ t.stringLiteral(objectProp),
268
+ [t.identifier(valueArgName)],
269
+ t.blockStatement([
270
+ t.expressionStatement(
271
+ t.assignmentExpression(
272
+ "=",
273
+ t.identifier(identifierName),
274
+ t.identifier(valueArgName)
275
+ )
276
+ ),
277
+ ]),
278
+ false,
279
+ false,
280
+ false
281
+ )
282
+ )
283
+ );
284
+ }
285
+ }
286
+
287
+ for (const entry of typeofProps) {
288
+ const [identifierName, objectProp] = entry;
289
+
290
+ flatObjectProperties.push(
291
+ me.skip(
292
+ t.objectMethod(
293
+ "get",
294
+ t.stringLiteral(objectProp),
295
+ [],
296
+ t.blockStatement([
297
+ t.returnStatement(
298
+ t.unaryExpression("typeof", t.identifier(identifierName))
299
+ ),
300
+ ]),
301
+ false,
302
+ false,
303
+ false
304
+ )
305
+ )
306
+ );
307
+ }
308
+
309
+ for (const entry of functionCallProps) {
310
+ const [identifierName, objectProp] = entry;
311
+
312
+ flatObjectProperties.push(
313
+ me.skip(
314
+ t.objectMethod(
315
+ "method",
316
+ t.stringLiteral(objectProp),
317
+ [t.restElement(t.identifier("args"))],
318
+ t.blockStatement([
319
+ t.returnStatement(
320
+ t.callExpression(t.identifier(identifierName), [
321
+ t.spreadElement(t.identifier("args")),
322
+ ])
323
+ ),
324
+ ]),
325
+ false,
326
+ false,
327
+ false
328
+ )
329
+ )
330
+ );
331
+ }
332
+
333
+ // Create the new flattened function
334
+ const flattenedFunctionDeclaration = t.functionDeclaration(
335
+ t.identifier(newFnName),
336
+ [t.arrayPattern([...fnPath.node.params]), t.identifier(flatObjectName)],
337
+ t.blockStatement([...[...fnPath.node.body.body]]),
338
+ false,
339
+ fnPath.node.async
340
+ );
341
+
342
+ // Create the flat object variable declaration
343
+ const flatObjectDeclaration = t.variableDeclaration("var", [
344
+ t.variableDeclarator(
345
+ t.identifier(flatObjectName),
346
+ t.objectExpression(flatObjectProperties)
347
+ ),
348
+ ]);
349
+
350
+ var argName = me.getPlaceholder() + "_args";
351
+
352
+ // Replace original function body with a call to the flattened function
353
+ fnPath.node.body = t.blockStatement([
354
+ flatObjectDeclaration,
355
+ t.returnStatement(
356
+ t.callExpression(t.identifier(newFnName), [
357
+ t.identifier(argName),
358
+ t.identifier(flatObjectName),
359
+ ])
360
+ ),
361
+ ]);
362
+
363
+ const originalLength = computeFunctionLength(fnPath);
364
+ fnPath.node.params = [t.restElement(t.identifier(argName))];
365
+
366
+ // Ensure updated parameter gets registered in the function scope
367
+ fnPath.scope.crawl();
368
+ fnPath.skip();
369
+
370
+ // Add the new flattened function at the top level
371
+ var newPath = prependProgram(
372
+ program,
373
+ flattenedFunctionDeclaration
374
+ )[0] as NodePath<t.FunctionDeclaration>;
375
+
376
+ me.skip(newPath);
377
+
378
+ // Copy over all properties except the predictable flag
379
+ for (var symbol of Object.getOwnPropertySymbols(fnPath.node)) {
380
+ if (symbol !== PREDICTABLE) {
381
+ newPath.node[symbol] = fnPath.node[symbol];
382
+ }
383
+ }
384
+
385
+ // Old function is no longer predictable (rest element parameter)
386
+ (fnPath.node as NodeSymbol)[PREDICTABLE] = false;
387
+ // Old function is unsafe (uses arguments, this)
388
+ (fnPath.node as NodeSymbol)[UNSAFE] = true;
389
+
390
+ newPath.node[PREDICTABLE] = true;
391
+
392
+ // Carry over 'use strict' directive if not already present
393
+ if (strictMode) {
394
+ newPath.node.body.directives.push(
395
+ t.directive(t.directiveLiteral("use strict"))
396
+ );
397
+
398
+ // Non-simple parameter list conversion
399
+ prepend(
400
+ newPath,
401
+ t.variableDeclaration("var", [
402
+ t.variableDeclarator(
403
+ t.arrayPattern(newPath.node.params),
404
+ t.identifier("arguments")
405
+ ),
406
+ ])
407
+ );
408
+ newPath.node.params = [];
409
+ // Using 'arguments' is unsafe
410
+ (newPath.node as NodeSymbol)[UNSAFE] = true;
411
+ // Params changed and using 'arguments'
412
+ (newPath.node as NodeSymbol)[PREDICTABLE] = false;
413
+ }
414
+
415
+ // Ensure parameters are registered in the new function scope
416
+ newPath.scope.crawl();
417
+
418
+ newPath.skip();
419
+ me.skip(newPath);
420
+
421
+ // Set function length
422
+ me.setFunctionLength(fnPath, originalLength);
423
+
424
+ me.changeData.functions++;
425
+ }
426
+
427
+ return {
428
+ visitor: {
429
+ Function: {
430
+ exit(path: NodePath<t.Function>) {
431
+ flattenFunction(path);
432
+ },
433
+ },
434
+ Program(path) {
435
+ path.scope.crawl();
436
+ },
437
+ },
438
+ };
439
+ };