js-confuser 1.7.3 → 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 (269) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +6 -4
  2. package/CHANGELOG.md +70 -0
  3. package/Migration.md +57 -0
  4. package/README.md +23 -929
  5. package/dist/constants.js +65 -14
  6. package/dist/index.js +108 -160
  7. package/dist/obfuscator.js +316 -118
  8. package/dist/options.js +1 -119
  9. package/dist/order.js +30 -30
  10. package/dist/presets.js +47 -45
  11. package/dist/probability.js +25 -32
  12. package/dist/templates/bufferToStringTemplate.js +9 -0
  13. package/dist/templates/deadCodeTemplates.js +9 -0
  14. package/dist/templates/getGlobalTemplate.js +19 -0
  15. package/dist/templates/integrityTemplate.js +30 -0
  16. package/dist/templates/setFunctionLengthTemplate.js +9 -0
  17. package/dist/templates/stringCompressionTemplate.js +10 -0
  18. package/dist/templates/tamperProtectionTemplates.js +21 -0
  19. package/dist/templates/template.js +199 -184
  20. package/dist/transforms/astScrambler.js +100 -0
  21. package/dist/transforms/calculator.js +70 -127
  22. package/dist/transforms/controlFlowFlattening.js +1182 -0
  23. package/dist/transforms/deadCode.js +62 -587
  24. package/dist/transforms/dispatcher.js +300 -313
  25. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +88 -189
  26. package/dist/transforms/extraction/objectExtraction.js +131 -215
  27. package/dist/transforms/finalizer.js +56 -59
  28. package/dist/transforms/flatten.js +275 -276
  29. package/dist/transforms/functionOutlining.js +230 -0
  30. package/dist/transforms/identifier/globalConcealing.js +214 -135
  31. package/dist/transforms/identifier/movedDeclarations.js +167 -91
  32. package/dist/transforms/identifier/renameVariables.js +239 -193
  33. package/dist/transforms/lock/integrity.js +61 -184
  34. package/dist/transforms/lock/lock.js +261 -387
  35. package/dist/transforms/minify.js +431 -436
  36. package/dist/transforms/opaquePredicates.js +65 -118
  37. package/dist/transforms/pack.js +160 -0
  38. package/dist/transforms/plugin.js +179 -0
  39. package/dist/transforms/preparation.js +261 -173
  40. package/dist/transforms/renameLabels.js +132 -56
  41. package/dist/transforms/rgf.js +140 -267
  42. package/dist/transforms/shuffle.js +52 -145
  43. package/dist/transforms/string/encoding.js +44 -175
  44. package/dist/transforms/string/stringCompression.js +79 -155
  45. package/dist/transforms/string/stringConcealing.js +189 -225
  46. package/dist/transforms/string/stringEncoding.js +32 -40
  47. package/dist/transforms/string/stringSplitting.js +54 -55
  48. package/dist/transforms/variableMasking.js +232 -0
  49. package/dist/utils/ControlObject.js +125 -0
  50. package/dist/utils/IntGen.js +46 -0
  51. package/dist/utils/NameGen.js +106 -0
  52. package/dist/utils/ast-utils.js +560 -0
  53. package/dist/utils/function-utils.js +56 -0
  54. package/dist/utils/gen-utils.js +48 -0
  55. package/dist/utils/node.js +77 -0
  56. package/dist/utils/object-utils.js +21 -0
  57. package/dist/utils/random-utils.js +91 -0
  58. package/dist/utils/static-utils.js +64 -0
  59. package/dist/validateOptions.js +122 -0
  60. package/index.d.ts +1 -17
  61. package/package.json +27 -22
  62. package/src/constants.ts +139 -82
  63. package/src/index.ts +70 -165
  64. package/src/obfuscationResult.ts +43 -0
  65. package/src/obfuscator.ts +328 -135
  66. package/src/options.ts +149 -658
  67. package/src/order.ts +14 -14
  68. package/src/presets.ts +39 -34
  69. package/src/probability.ts +21 -36
  70. package/src/templates/bufferToStringTemplate.ts +57 -0
  71. package/src/templates/deadCodeTemplates.ts +1185 -0
  72. package/src/templates/getGlobalTemplate.ts +72 -0
  73. package/src/templates/integrityTemplate.ts +69 -0
  74. package/src/templates/setFunctionLengthTemplate.ts +11 -0
  75. package/src/templates/stringCompressionTemplate.ts +42 -0
  76. package/src/templates/tamperProtectionTemplates.ts +116 -0
  77. package/src/templates/template.ts +149 -157
  78. package/src/transforms/astScrambler.ts +99 -0
  79. package/src/transforms/calculator.ts +96 -226
  80. package/src/transforms/controlFlowFlattening.ts +1594 -0
  81. package/src/transforms/deadCode.ts +85 -676
  82. package/src/transforms/dispatcher.ts +431 -640
  83. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +147 -295
  84. package/src/transforms/extraction/objectExtraction.ts +160 -333
  85. package/src/transforms/finalizer.ts +63 -64
  86. package/src/transforms/flatten.ts +439 -557
  87. package/src/transforms/functionOutlining.ts +225 -0
  88. package/src/transforms/identifier/globalConcealing.ts +255 -266
  89. package/src/transforms/identifier/movedDeclarations.ts +228 -142
  90. package/src/transforms/identifier/renameVariables.ts +250 -271
  91. package/src/transforms/lock/integrity.ts +85 -263
  92. package/src/transforms/lock/lock.ts +338 -579
  93. package/src/transforms/minify.ts +523 -663
  94. package/src/transforms/opaquePredicates.ts +90 -229
  95. package/src/transforms/pack.ts +195 -0
  96. package/src/transforms/plugin.ts +185 -0
  97. package/src/transforms/preparation.ts +337 -231
  98. package/src/transforms/renameLabels.ts +176 -77
  99. package/src/transforms/rgf.ts +293 -424
  100. package/src/transforms/shuffle.ts +80 -254
  101. package/src/transforms/string/encoding.ts +20 -126
  102. package/src/transforms/string/stringCompression.ts +117 -307
  103. package/src/transforms/string/stringConcealing.ts +254 -342
  104. package/src/transforms/string/stringEncoding.ts +28 -47
  105. package/src/transforms/string/stringSplitting.ts +61 -75
  106. package/src/transforms/variableMasking.ts +257 -0
  107. package/src/utils/ControlObject.ts +141 -0
  108. package/src/utils/IntGen.ts +33 -0
  109. package/src/utils/NameGen.ts +106 -0
  110. package/src/utils/ast-utils.ts +667 -0
  111. package/src/utils/function-utils.ts +50 -0
  112. package/src/utils/gen-utils.ts +48 -0
  113. package/src/utils/node.ts +78 -0
  114. package/src/utils/object-utils.ts +21 -0
  115. package/src/utils/random-utils.ts +79 -0
  116. package/src/utils/static-utils.ts +66 -0
  117. package/src/validateOptions.ts +256 -0
  118. package/tsconfig.json +13 -8
  119. package/babel.config.js +0 -12
  120. package/dev.js +0 -8
  121. package/dist/compiler.js +0 -34
  122. package/dist/parser.js +0 -59
  123. package/dist/precedence.js +0 -66
  124. package/dist/templates/bufferToString.js +0 -129
  125. package/dist/templates/core.js +0 -35
  126. package/dist/templates/crash.js +0 -28
  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 -1287
  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 -83
  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 -349
  144. package/dist/transforms/transform.js +0 -372
  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 -14
  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 -156
  154. package/dist/util/scope.js +0 -20
  155. package/docs/ControlFlowFlattening.md +0 -595
  156. package/docs/Countermeasures.md +0 -70
  157. package/docs/ES5.md +0 -197
  158. package/docs/Integrity.md +0 -82
  159. package/docs/RGF.md +0 -424
  160. package/docs/RenameVariables.md +0 -116
  161. package/docs/TamperProtection.md +0 -100
  162. package/docs/Template.md +0 -117
  163. package/samples/example.js +0 -15
  164. package/samples/high.js +0 -1
  165. package/samples/input.js +0 -3
  166. package/samples/javascriptobfuscator.com.js +0 -8
  167. package/samples/jscrambler_advanced.js +0 -1894
  168. package/samples/jscrambler_light.js +0 -1134
  169. package/samples/low.js +0 -1
  170. package/samples/medium.js +0 -1
  171. package/samples/obfuscator.io.js +0 -1686
  172. package/samples/preemptive.com.js +0 -16
  173. package/src/compiler.ts +0 -35
  174. package/src/parser.ts +0 -49
  175. package/src/precedence.ts +0 -61
  176. package/src/templates/bufferToString.ts +0 -136
  177. package/src/templates/core.ts +0 -29
  178. package/src/templates/crash.ts +0 -23
  179. package/src/templates/es5.ts +0 -131
  180. package/src/templates/functionLength.ts +0 -32
  181. package/src/templates/globals.ts +0 -3
  182. package/src/transforms/antiTooling.ts +0 -102
  183. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +0 -2153
  184. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +0 -179
  185. package/src/transforms/es5/antiClass.ts +0 -276
  186. package/src/transforms/es5/antiDestructuring.ts +0 -294
  187. package/src/transforms/es5/antiES6Object.ts +0 -267
  188. package/src/transforms/es5/antiSpreadOperator.ts +0 -56
  189. package/src/transforms/es5/antiTemplate.ts +0 -98
  190. package/src/transforms/es5/es5.ts +0 -149
  191. package/src/transforms/extraction/classExtraction.ts +0 -168
  192. package/src/transforms/identifier/globalAnalysis.ts +0 -102
  193. package/src/transforms/identifier/variableAnalysis.ts +0 -118
  194. package/src/transforms/lock/antiDebug.ts +0 -112
  195. package/src/transforms/stack.ts +0 -557
  196. package/src/transforms/transform.ts +0 -441
  197. package/src/traverse.ts +0 -120
  198. package/src/types.ts +0 -133
  199. package/src/util/compare.ts +0 -181
  200. package/src/util/gen.ts +0 -651
  201. package/src/util/guard.ts +0 -17
  202. package/src/util/identifiers.ts +0 -494
  203. package/src/util/insert.ts +0 -419
  204. package/src/util/math.ts +0 -15
  205. package/src/util/object.ts +0 -39
  206. package/src/util/random.ts +0 -221
  207. package/src/util/scope.ts +0 -21
  208. package/test/code/Cash.src.js +0 -1011
  209. package/test/code/Cash.test.ts +0 -132
  210. package/test/code/Dynamic.src.js +0 -118
  211. package/test/code/Dynamic.test.ts +0 -49
  212. package/test/code/ES6.src.js +0 -235
  213. package/test/code/ES6.test.ts +0 -42
  214. package/test/code/NewFeatures.test.ts +0 -19
  215. package/test/code/StrictMode.src.js +0 -65
  216. package/test/code/StrictMode.test.js +0 -37
  217. package/test/compare.test.ts +0 -104
  218. package/test/index.test.ts +0 -249
  219. package/test/options.test.ts +0 -150
  220. package/test/presets.test.ts +0 -22
  221. package/test/probability.test.ts +0 -44
  222. package/test/templates/template.test.ts +0 -224
  223. package/test/transforms/antiTooling.test.ts +0 -52
  224. package/test/transforms/calculator.test.ts +0 -78
  225. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +0 -1274
  226. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +0 -192
  227. package/test/transforms/deadCode.test.ts +0 -85
  228. package/test/transforms/dispatcher.test.ts +0 -457
  229. package/test/transforms/es5/antiClass.test.ts +0 -427
  230. package/test/transforms/es5/antiDestructuring.test.ts +0 -157
  231. package/test/transforms/es5/antiES6Object.test.ts +0 -245
  232. package/test/transforms/es5/antiTemplate.test.ts +0 -116
  233. package/test/transforms/es5/es5.test.ts +0 -110
  234. package/test/transforms/extraction/classExtraction.test.ts +0 -86
  235. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +0 -200
  236. package/test/transforms/extraction/objectExtraction.test.ts +0 -491
  237. package/test/transforms/flatten.test.ts +0 -721
  238. package/test/transforms/hexadecimalNumbers.test.ts +0 -62
  239. package/test/transforms/identifier/globalConcealing.test.ts +0 -142
  240. package/test/transforms/identifier/movedDeclarations.test.ts +0 -275
  241. package/test/transforms/identifier/renameVariables.test.ts +0 -695
  242. package/test/transforms/lock/antiDebug.test.ts +0 -66
  243. package/test/transforms/lock/browserLock.test.ts +0 -129
  244. package/test/transforms/lock/countermeasures.test.ts +0 -100
  245. package/test/transforms/lock/integrity.test.ts +0 -161
  246. package/test/transforms/lock/lock.test.ts +0 -204
  247. package/test/transforms/lock/osLock.test.ts +0 -312
  248. package/test/transforms/lock/selfDefending.test.ts +0 -68
  249. package/test/transforms/lock/tamperProtection.test.ts +0 -336
  250. package/test/transforms/minify.test.ts +0 -575
  251. package/test/transforms/opaquePredicates.test.ts +0 -43
  252. package/test/transforms/preparation.test.ts +0 -157
  253. package/test/transforms/renameLabels.test.ts +0 -95
  254. package/test/transforms/rgf.test.ts +0 -378
  255. package/test/transforms/shuffle.test.ts +0 -135
  256. package/test/transforms/stack.test.ts +0 -573
  257. package/test/transforms/string/stringCompression.test.ts +0 -120
  258. package/test/transforms/string/stringConcealing.test.ts +0 -299
  259. package/test/transforms/string/stringEncoding.test.ts +0 -95
  260. package/test/transforms/string/stringSplitting.test.ts +0 -135
  261. package/test/transforms/transform.test.ts +0 -66
  262. package/test/traverse.test.ts +0 -139
  263. package/test/util/compare.test.ts +0 -34
  264. package/test/util/gen.test.ts +0 -121
  265. package/test/util/identifiers.test.ts +0 -253
  266. package/test/util/insert.test.ts +0 -142
  267. package/test/util/math.test.ts +0 -5
  268. package/test/util/random.test.ts +0 -71
  269. /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
+ };