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,722 +1,582 @@
1
- import Transform from "./transform";
2
- import { ObfuscateOrder } from "../order";
1
+ import { NodePath } from "@babel/core";
2
+ import { PluginArg, PluginObject } from "./plugin";
3
+ import * as t from "@babel/types";
4
+ import { Order } from "../order";
3
5
  import {
4
- Node,
5
- VariableDeclaration,
6
- BinaryExpression,
7
- ExpressionStatement,
8
- SequenceExpression,
9
- Literal,
10
- UnaryExpression,
11
- ConditionalExpression,
12
- BlockStatement,
13
- ReturnStatement,
14
- AssignmentExpression,
15
- VariableDeclarator,
16
- Identifier,
17
- CallExpression,
18
- } from "../util/gen";
19
- import {
20
- getBlockBody,
21
- clone,
22
- isForInitialize,
23
- append,
24
- isVarContext,
25
- computeFunctionLength,
26
- } from "../util/insert";
27
- import { isValidIdentifier, isEquivalent } from "../util/compare";
28
- import { walk, isBlock } from "../traverse";
29
- import { ok } from "assert";
30
- import { isLexicalScope } from "../util/scope";
31
- import Template from "../templates/template";
32
- import { ObjectDefineProperty } from "../templates/globals";
33
- import { getIdentifierInfo } from "../util/identifiers";
6
+ ensureComputedExpression,
7
+ getParentFunctionOrProgram,
8
+ isUndefined,
9
+ } from "../utils/ast-utils";
10
+ import { Binding, Scope } from "@babel/traverse";
11
+ import { NodeSymbol, placeholderVariablePrefix, UNSAFE } from "../constants";
12
+
13
+ const identifierMap = new Map<string, () => t.Expression>();
14
+ identifierMap.set("undefined", () =>
15
+ t.unaryExpression("void", t.numericLiteral(0))
16
+ );
17
+ identifierMap.set("Infinity", () =>
18
+ t.binaryExpression("/", t.numericLiteral(1), t.numericLiteral(0))
19
+ );
34
20
 
35
21
  /**
36
- * Basic transformations to reduce code size.
22
+ * Minify removes unnecessary code and shortens the length for file size.
37
23
  *
38
- * Examples:
39
- * - `if(a) { b() }` **->** `a && b()`
40
- * - `if(a){b()}else{c()}` **->** `a?b():c()`
41
- * - `x['y']` **->** `x.y`
24
+ * - Dead code elimination
25
+ * - Variable grouping
26
+ * - Constant folding
27
+ * - Shorten literals: True to !0, False to !1, Infinity to 1/0, Undefined to void 0
28
+ * - Remove unused variables, functions
42
29
  */
43
- export default class Minify extends Transform {
44
- /**
45
- * A helper function that is introduced preserve function semantics
46
- */
47
- arrowFunctionName: string;
48
-
49
- constructor(o) {
50
- super(o, ObfuscateOrder.Minify);
51
- }
52
-
53
- match(object: Node, parents: Node[]) {
54
- return object.hasOwnProperty("type");
55
- }
56
-
57
- transform(object: Node, parents: Node[]) {
58
- if (isLexicalScope(object)) {
59
- return () => {
60
- var body =
61
- object.type == "SwitchCase"
62
- ? object.consequent
63
- : getBlockBody(object);
64
- var earlyReturn = body.length;
65
- var fnDecs: [Node, number][] = [];
66
-
67
- body.forEach((stmt, i) => {
30
+ export default ({ Plugin }: PluginArg): PluginObject => {
31
+ const me = Plugin(Order.Minify);
32
+ return {
33
+ visitor: {
34
+ Program(path) {
35
+ path.scope.crawl();
36
+ },
37
+ // var a; var b; -> var a,b;
38
+ VariableDeclaration: {
39
+ exit(path) {
40
+ if (typeof path.key !== "number") return;
41
+ const kind = path.node.kind;
42
+
43
+ // get declaration after this
44
+ const nextDeclaration = path.getSibling(path.key + 1);
68
45
  if (
69
- stmt.type === "ReturnStatement" ||
70
- stmt.type === "BreakStatement" ||
71
- stmt.type === "ContinueStatement" ||
72
- stmt.type === "ThrowStatement"
46
+ nextDeclaration.isVariableDeclaration({
47
+ kind: kind,
48
+ })
73
49
  ) {
74
- if (earlyReturn > i + 1) {
75
- earlyReturn = i + 1;
50
+ const declarations = path.get("declarations");
51
+
52
+ // Preserve bindings!
53
+ // This is important for dead code elimination
54
+ const bindings: { [name: string]: Binding } = Object.create(null);
55
+ for (var declaration of declarations) {
56
+ for (var idPath of Object.values(
57
+ declaration.getBindingIdentifierPaths()
58
+ )) {
59
+ bindings[idPath.node.name] = idPath.scope.getBinding(
60
+ idPath.node.name
61
+ );
62
+ }
76
63
  }
77
- }
78
-
79
- if (stmt.type == "FunctionDeclaration") {
80
- fnDecs.push([stmt, i]);
81
- }
82
- });
83
-
84
- if (earlyReturn < body.length) {
85
- body.length = earlyReturn;
86
- body.push(
87
- ...fnDecs.filter((x) => x[1] >= earlyReturn).map((x) => x[0])
88
- );
89
- }
90
-
91
- // Now combine ExpressionStatements
92
64
 
93
- if (body.length > 1) {
94
- var exprs = [];
95
- var startIndex = -1;
96
-
97
- var sequences: { index: number; exprs: Node[] }[] = [];
65
+ nextDeclaration.node.declarations.unshift(
66
+ ...declarations.map((x) => x.node)
67
+ );
98
68
 
99
- body.forEach((stmt, i) => {
100
- if (stmt.type == "ExpressionStatement" && !stmt.directive) {
101
- exprs.push(stmt.expression);
102
- if (startIndex == -1) {
103
- startIndex = i;
104
- }
105
- } else {
106
- if (exprs.length) {
107
- sequences.push({ exprs: exprs, index: startIndex });
69
+ const newBindingIdentifierPaths =
70
+ nextDeclaration.getBindingIdentifierPaths();
71
+
72
+ // path.remove() unfortunately removes the bindings
73
+ // We must perverse the entire binding object (referencePaths, constantViolations, etc)
74
+ // and re-add them to the new scope
75
+ path.remove();
76
+
77
+ // Add bindings back
78
+ function addBindingsToScope(scope: Scope) {
79
+ for (var name in bindings) {
80
+ const binding = bindings[name];
81
+ if (binding) {
82
+ binding.path = newBindingIdentifierPaths[name];
83
+ scope.bindings[name] = binding;
84
+ }
108
85
  }
109
- exprs = [];
110
- startIndex = -1;
111
86
  }
112
- });
113
87
 
114
- if (exprs.length) {
115
- sequences.push({ exprs: exprs, index: startIndex });
88
+ if (kind === "var") {
89
+ addBindingsToScope(getParentFunctionOrProgram(path).scope);
90
+ }
91
+ addBindingsToScope(path.scope);
116
92
  }
117
-
118
- sequences.reverse().forEach((seq) => {
119
- ok(seq.index != -1);
120
- body.splice(
121
- seq.index,
122
- seq.exprs.length,
123
- ExpressionStatement(
124
- seq.exprs.length == 1
125
- ? seq.exprs[0]
126
- : SequenceExpression(seq.exprs)
127
- )
93
+ },
94
+ },
95
+ // true -> !0, false -> !1
96
+ BooleanLiteral: {
97
+ exit(path) {
98
+ if (path.node.value) {
99
+ path.replaceWith(t.unaryExpression("!", t.numericLiteral(0)));
100
+ } else {
101
+ path.replaceWith(t.unaryExpression("!", t.numericLiteral(1)));
102
+ }
103
+ },
104
+ },
105
+ // !"" -> !1
106
+ UnaryExpression: {
107
+ exit(path) {
108
+ if (path.node.operator === "!") {
109
+ var argument = path.get("argument");
110
+ if (argument.isNumericLiteral()) return;
111
+ const value = argument.evaluateTruthy();
112
+ const parent = getParentFunctionOrProgram(path);
113
+ if (parent && (parent.node as NodeSymbol)[UNSAFE]) return;
114
+
115
+ if (value === undefined) return;
116
+
117
+ path.replaceWith(
118
+ t.unaryExpression("!", t.numericLiteral(value ? 1 : 0))
128
119
  );
129
- });
130
- }
131
-
132
- // Unnecessary return
133
- if (
134
- parents[0] &&
135
- isVarContext(parents[0]) &&
136
- body.length &&
137
- body[body.length - 1]
138
- ) {
139
- var last = body[body.length - 1];
140
- if (last.type == "ReturnStatement") {
141
- var isUndefined = last.argument == null;
142
-
143
- if (isUndefined) {
144
- body.pop();
120
+ }
121
+ },
122
+ },
123
+ // a["key"] -> a.key
124
+ MemberExpression: {
125
+ exit(path) {
126
+ if (!path.node.computed) return;
127
+
128
+ const property = path.get("property");
129
+ if (!property.isStringLiteral()) return;
130
+
131
+ const key = property.node.value;
132
+ if (!t.isValidIdentifier(key)) return;
133
+
134
+ path.node.computed = false;
135
+ path.node.property = t.identifier(key);
136
+ },
137
+ },
138
+ // {["key"]: 1} -> {key: 1}
139
+ // {"key": 1} -> {key: 1}
140
+ ObjectProperty: {
141
+ exit(path) {
142
+ var key = path.get("key");
143
+ if (path.node.computed && key.isStringLiteral()) {
144
+ path.node.computed = false;
145
+ }
146
+
147
+ if (
148
+ !path.node.computed &&
149
+ key.isStringLiteral() &&
150
+ t.isValidIdentifier(key.node.value)
151
+ ) {
152
+ if (identifierMap.has(key.node.value)) {
153
+ path.node.computed = true;
154
+ key.replaceWith(identifierMap.get(key.node.value)!());
155
+ } else {
156
+ key.replaceWith(t.identifier(key.node.value));
145
157
  }
146
158
  }
147
- }
148
-
149
- // Variable declaration grouping
150
- // var a = 1;
151
- // var b = 1;
152
- // var c = 1;
153
- //
154
- // var a=1,b=1,c=1;
155
- var lastDec = null;
156
-
157
- var remove = [];
158
- body.forEach((x, i) => {
159
- if (x.type === "VariableDeclaration") {
159
+ },
160
+ },
161
+ // (a); -> a;
162
+ SequenceExpression: {
163
+ exit(path) {
164
+ if (path.node.expressions.length === 1) {
165
+ path.replaceWith(path.node.expressions[0]);
166
+ }
167
+ },
168
+ },
169
+ // ; -> ()
170
+ EmptyStatement: {
171
+ exit(path) {
172
+ path.remove();
173
+ },
174
+ },
175
+ // console; -> ();
176
+ ExpressionStatement: {
177
+ exit(path) {
178
+ if (path.get("expression").isIdentifier()) {
179
+ // Preserve last expression of program for RGF
160
180
  if (
161
- !lastDec ||
162
- lastDec.kind !== x.kind ||
163
- !lastDec.declarations.length
164
- ) {
165
- lastDec = x;
166
- } else {
167
- lastDec.declarations.push(...clone(x.declarations));
168
- remove.unshift(i);
181
+ path.parentPath?.isProgram() &&
182
+ path.parentPath?.get("body").at(-1) === path
183
+ )
184
+ return;
185
+ path.remove();
186
+ }
187
+ },
188
+ },
189
+ // undefined -> void 0
190
+ // Infinity -> 1/0
191
+ Identifier: {
192
+ exit(path) {
193
+ if (path.isReferencedIdentifier()) {
194
+ if (identifierMap.has(path.node.name)) {
195
+ ensureComputedExpression(path);
196
+ path.replaceWith(identifierMap.get(path.node.name)!());
169
197
  }
170
- } else {
171
- lastDec = null;
172
198
  }
173
- });
174
-
175
- remove.forEach((x) => {
176
- body.splice(x, 1);
177
- });
178
- };
179
- }
180
-
181
- /**
182
- * ES6 and higher only
183
- * - `function(){}` -> `()=>{}`
184
- * - `function abc(){}` -> `var abc = ()=>{}`
185
- */
186
- if (
187
- !this.options.es5 &&
188
- (object.type == "FunctionExpression" ||
189
- object.type == "FunctionDeclaration")
190
- ) {
191
- return () => {
192
- // Don't touch `{get key(){...}}`
193
- var propIndex = parents.findIndex(
194
- (x) => x.type == "Property" || x.type == "MethodDefinition"
195
- );
196
- if (propIndex !== -1) {
197
- if (parents[propIndex].value === (parents[propIndex - 1] || object)) {
199
+ },
200
+ },
201
+ // true ? a : b -> a
202
+ ConditionalExpression: {
203
+ exit(path) {
204
+ const testValue = path.get("test").evaluateTruthy();
205
+ if (testValue === undefined) return;
206
+
207
+ path.replaceWith(
208
+ testValue ? path.node.consequent : path.node.alternate
209
+ );
210
+ },
211
+ },
212
+ // Remove unused functions
213
+ FunctionDeclaration: {
214
+ exit(path) {
215
+ const id = path.get("id");
216
+ if (
217
+ id.isIdentifier() &&
218
+ !id.node.name.startsWith(placeholderVariablePrefix)
219
+ ) {
220
+ const binding = path.scope.getBinding(id.node.name);
198
221
  if (
199
- parents[propIndex].kind !== "init" ||
200
- parents[propIndex].method
222
+ binding &&
223
+ binding.constantViolations.length === 0 &&
224
+ binding.referencePaths.length === 0 &&
225
+ !binding.referenced
201
226
  ) {
202
- return;
227
+ path.remove();
203
228
  }
204
229
  }
205
- }
230
+ },
231
+ },
232
+ // var x=undefined -> var x
233
+ // Remove unused variables
234
+ // Simple destructuring
235
+ VariableDeclarator: {
236
+ exit(path) {
237
+ if (isUndefined(path.get("init"))) {
238
+ path.node.init = null;
239
+ }
206
240
 
207
- if (object.type === "FunctionDeclaration") {
208
- var body = parents[0];
209
- if (!Array.isArray(body)) {
210
- return;
241
+ const id = path.get("id");
242
+ const init = path.get("init");
243
+
244
+ // Simple array/object destructuring
245
+ if (id.isArrayPattern() && init.isArrayExpression()) {
246
+ const elements = id.get("elements");
247
+ const initElements = init.get("elements");
248
+
249
+ if (elements.length === 1 && initElements.length === 1) {
250
+ id.replaceWith(elements[0]);
251
+ init.replaceWith(initElements[0]);
252
+ }
211
253
  }
212
254
 
213
- var index = body.indexOf(object);
214
- if (index == -1) {
215
- return;
255
+ if (id.isObjectPattern() && init.isObjectExpression()) {
256
+ const properties = id.get("properties");
257
+ const initProperties = init.get("properties");
258
+
259
+ if (properties.length === 1 && initProperties.length === 1) {
260
+ const firstProperty = properties[0];
261
+ const firstInitProperty = initProperties[0];
262
+
263
+ if (
264
+ firstProperty.isObjectProperty() &&
265
+ firstInitProperty.isObjectProperty()
266
+ ) {
267
+ const firstKey = firstProperty.get("key");
268
+ const firstInitKey = firstInitProperty.get("key");
269
+ if (
270
+ firstKey.isIdentifier() &&
271
+ firstInitKey.isIdentifier() &&
272
+ firstKey.node.name === firstInitKey.node.name
273
+ ) {
274
+ id.replaceWith(firstProperty.node.value);
275
+ init.replaceWith(firstInitProperty.node.value);
276
+ }
277
+ }
278
+ }
216
279
  }
217
280
 
218
- var before = body.slice(0, index);
219
- ok(!before.includes(object));
281
+ // Remove unused variables
282
+ // Can only remove if it's pure
283
+ if (id.isIdentifier()) {
284
+ // Do not remove variables in unsafe functions
285
+ const fn = getParentFunctionOrProgram(path);
286
+ if ((fn.node as NodeSymbol)[UNSAFE]) return;
220
287
 
221
- var beforeTypes = new Set(before.map((x) => x.type));
222
- beforeTypes.delete("FunctionDeclaration");
288
+ const binding = path.scope.getBinding(id.node.name);
223
289
 
224
- if (beforeTypes.size > 0) {
225
- return;
290
+ if (
291
+ binding &&
292
+ binding.constantViolations.length === 0 &&
293
+ binding.referencePaths.length === 0
294
+ ) {
295
+ if (!init.node || init.isPure()) {
296
+ path.remove();
297
+ } else if (
298
+ path.parentPath.isVariableDeclaration() &&
299
+ path.parentPath.node.declarations.length === 1
300
+ ) {
301
+ path.parentPath.replaceWith(t.expressionStatement(init.node));
302
+ }
303
+ }
304
+ }
305
+ },
306
+ },
307
+ // return undefined->return
308
+ ReturnStatement: {
309
+ exit(path) {
310
+ if (isUndefined(path.get("argument"))) {
311
+ path.node.argument = null;
312
+ }
313
+ },
314
+ },
315
+ // while(true) {a();} -> while(true) a();
316
+ // for(;;) {a();} -> for(;;) a();
317
+ // with(a) {a();} -> with(a) a();
318
+ "While|For|WithStatement": {
319
+ exit(_path) {
320
+ var path = _path as NodePath<t.While | t.For | t.WithStatement>;
321
+ var body = path.get("body");
322
+
323
+ if (body.isBlock() && body.node.body.length === 1) {
324
+ body.replaceWith(body.node.body[0]);
226
325
  }
326
+ },
327
+ },
328
+ // if(a) a(); -> a && a();
329
+ // if(a) { return b; } -> if(a) return b;
330
+ // if(a) { a(); } else { b(); } -> a ? a() : b();
331
+ // if(a) { return b; } else { return c; } -> return a ? b : c;
332
+ IfStatement: {
333
+ exit(path) {
334
+ // BlockStatement to single statement
335
+ const consequent = path.get("consequent");
336
+ const alternate = path.get("alternate");
337
+
338
+ const isMoveable = (node: t.Statement) => {
339
+ if (t.isDeclaration(node)) return false;
340
+
341
+ return true;
342
+ };
227
343
 
228
- // Test Variant #25: Don't break redefined function declaration
229
- if (
230
- object.id &&
231
- body.find(
232
- (x) =>
233
- x.type === "FunctionDeclaration" &&
234
- x !== object &&
235
- x.id &&
236
- x.id.name === object.id.name
237
- )
238
- ) {
239
- return;
344
+ let testValue = path.get("test").evaluateTruthy();
345
+
346
+ const parent = getParentFunctionOrProgram(path);
347
+ if (parent && (parent.node as NodeSymbol)[UNSAFE]) {
348
+ testValue = undefined;
240
349
  }
241
- }
242
-
243
- var canTransform = true;
244
- walk(object.body, [], ($object, $parents) => {
245
- if ($object.type == "ThisExpression") {
246
- canTransform = false;
247
- } else if ($object.type == "Identifier") {
248
- if ($object.name == "arguments") {
249
- canTransform = false;
350
+
351
+ if (typeof testValue !== "undefined") {
352
+ if (
353
+ !alternate.node &&
354
+ consequent.isBlock() &&
355
+ consequent.node.body.length === 1 &&
356
+ isMoveable(consequent.node.body[0])
357
+ ) {
358
+ consequent.replaceWith(consequent.node.body[0]);
250
359
  }
251
- if ($object.name == "this") {
252
- this.error(new Error("Use ThisExpression instead"));
360
+
361
+ if (
362
+ alternate.node &&
363
+ alternate.isBlock() &&
364
+ alternate.node.body.length === 1 &&
365
+ isMoveable(alternate.node.body[0])
366
+ ) {
367
+ alternate.replaceWith(alternate.node.body[0]);
253
368
  }
254
369
  }
255
- });
256
-
257
- if (canTransform) {
258
- if (!this.arrowFunctionName) {
259
- this.arrowFunctionName = this.getPlaceholder();
260
-
261
- append(
262
- parents[parents.length - 1] || object,
263
- new Template(`
264
- function ${this.arrowFunctionName}(arrowFn, functionLength = 0){
265
- var functionObject = function(){ return arrowFn(...arguments) };
266
-
267
- ${
268
- this.options.preserveFunctionLength
269
- ? `return {ObjectDefineProperty}(functionObject, "length", {
270
- "value": functionLength,
271
- "configurable": true
272
- });`
273
- : `return functionObject`
274
- }
275
-
370
+
371
+ if (testValue === false) {
372
+ // if(false){} -> ()
373
+ if (!alternate.node) {
374
+ path.remove();
375
+ return;
376
+
377
+ // if(false){a()}else{b()} -> b()
378
+ } else {
379
+ path.replaceWith(alternate.node);
380
+ return;
276
381
  }
277
- `).single({
278
- ObjectDefineProperty: this.options.preserveFunctionLength
279
- ? this.createInitVariable(ObjectDefineProperty, parents)
280
- : undefined,
281
- })
282
- );
382
+
383
+ // if(true){a()} -> {a()}
384
+ } else if (testValue === true) {
385
+ path.replaceWith(consequent.node);
386
+ return;
283
387
  }
284
388
 
285
- const wrap = (object: Node) => {
286
- var args: Node[] = [clone(object)];
287
- var fnLength = computeFunctionLength(object.params);
288
- if (this.options.preserveFunctionLength && fnLength != 0) {
289
- args.push(Literal(fnLength));
290
- }
291
- return CallExpression(Identifier(this.arrowFunctionName), args);
292
- };
389
+ function getResult(path: NodePath): {
390
+ returnPath: NodePath<t.ReturnStatement> | null;
391
+ expressions: t.Expression[];
392
+ } {
393
+ if (!path.node) return null;
293
394
 
294
- if (object.type == "FunctionExpression") {
295
- object.type = "ArrowFunctionExpression";
395
+ if (path.isReturnStatement()) {
396
+ return { returnPath: path, expressions: [] };
397
+ }
398
+ if (path.isExpressionStatement()) {
399
+ return {
400
+ returnPath: null,
401
+ expressions: [path.get("expression").node],
402
+ };
403
+ }
296
404
 
297
- this.replace(object, wrap(clone(object)));
298
- } else {
299
- var arrow = { ...clone(object), type: "ArrowFunctionExpression" };
300
- this.replace(
301
- object,
302
- VariableDeclaration(
303
- VariableDeclarator(object.id.name, wrap(arrow))
304
- )
305
- );
405
+ if (path.isBlockStatement()) {
406
+ var expressions = [];
407
+ for (var statement of path.get("body")) {
408
+ if (statement.isReturnStatement()) {
409
+ return { returnPath: statement, expressions: expressions };
410
+ } else if (statement.isExpressionStatement()) {
411
+ expressions.push(statement.get("expression").node);
412
+ } else {
413
+ return null;
414
+ }
415
+ }
306
416
 
307
- var x = this.transform(arrow, []);
308
- if (typeof x === "function") {
309
- x();
417
+ return { returnPath: null, expressions: expressions };
310
418
  }
419
+
420
+ return null;
311
421
  }
312
- }
313
- };
314
- }
315
-
316
- /**
317
- * ()=>{ expr } -> ()=>expr
318
- */
319
- if (
320
- object.type == "ArrowFunctionExpression" &&
321
- object.body.type == "BlockStatement"
322
- ) {
323
- return () => {
324
- var body = getBlockBody(object.body);
325
- var stmt1 = body[0];
326
-
327
- if (body.length == 1 && stmt1.type == "ReturnStatement") {
328
- // x=>{a: 1} // Invalid syntax
329
- if (stmt1.argument && stmt1.argument.type != "ObjectExpression") {
330
- object.body = stmt1.argument;
331
- object.expression = true;
332
- }
333
- } else {
334
- // ()=>{exprStmt;exprStmt;} -> ()=>(expr, expr, expr, undefined)
335
- var exprs = body.filter((x) => x.type == "ExpressionStatement");
336
- if (exprs.length == body.length) {
337
- var array: Node[] = [];
338
- function flatten(expr) {
339
- if (expr.type == "SequenceExpression") {
340
- expr.expressions.forEach(flatten);
341
- } else if (expr.type == "ExpressionStatement") {
342
- flatten(expr.expression);
343
- } else {
344
- array.push(expr);
422
+
423
+ var consequentReturn = getResult(consequent);
424
+ var alternateReturn = getResult(alternate);
425
+
426
+ if (consequentReturn && alternateReturn) {
427
+ if (consequentReturn.returnPath && alternateReturn.returnPath) {
428
+ function createReturnArgument(
429
+ resultInfo: ReturnType<typeof getResult>
430
+ ) {
431
+ return t.sequenceExpression([
432
+ ...resultInfo.expressions,
433
+ resultInfo.returnPath.node.argument ||
434
+ t.identifier("undefined"),
435
+ ]);
345
436
  }
346
- }
347
437
 
348
- body.forEach(flatten);
438
+ path.replaceWith(
439
+ t.returnStatement(
440
+ t.conditionalExpression(
441
+ path.node.test,
442
+ createReturnArgument(consequentReturn),
443
+ createReturnArgument(alternateReturn)
444
+ )
445
+ )
446
+ );
447
+ } else if (
448
+ !consequentReturn.returnPath &&
449
+ !alternateReturn.returnPath
450
+ ) {
451
+ function joinExpressions(expressions: t.Expression[]) {
452
+ // condition?():() is invalid syntax
453
+ // Just use 0 as a placeholder
454
+ if (expressions.length === 0) return t.numericLiteral(0);
349
455
 
350
- object.body = SequenceExpression([
351
- ...clone(array),
352
- UnaryExpression("void", Literal(0)),
353
- ]);
354
- }
355
- }
356
- };
357
- }
358
-
359
- // (a()) -> a()
360
- if (object.type == "SequenceExpression") {
361
- return () => {
362
- if (object.expressions.length == 1) {
363
- this.replace(object, clone(object.expressions[0]));
364
- }
365
- };
366
- }
367
-
368
- // a += -1 -> a -= 1
369
- if (object.type == "AssignmentExpression") {
370
- return () => {
371
- if (
372
- object.operator == "+=" &&
373
- object.right.type == "UnaryExpression" &&
374
- object.right.operator == "-"
375
- ) {
376
- object.operator = "-=";
377
- object.right = object.right.argument;
378
- } else if (
379
- // a -= -1 -> a += 1
380
- object.operator == "-=" &&
381
- object.right.type == "UnaryExpression" &&
382
- object.right.operator == "-"
383
- ) {
384
- object.operator = "+=";
385
- object.right = object.right.argument;
386
- }
387
- };
388
- }
389
-
390
- // a + -b -> a - b
391
- if (object.type == "BinaryExpression") {
392
- return () => {
393
- if (
394
- object.operator == "+" &&
395
- object.right.type == "UnaryExpression" &&
396
- object.right.operator == "-"
397
- ) {
398
- object.operator = "-";
399
- object.right = object.right.argument;
400
- } else if (
401
- // a - -1 -> a + 1
402
- object.operator == "-" &&
403
- object.right.type == "UnaryExpression" &&
404
- object.right.operator == "-"
405
- ) {
406
- object.operator = "+";
407
- object.right = object.right.argument;
408
- }
409
- };
410
- }
411
-
412
- if (
413
- object.type == "ForStatement" ||
414
- object.type == "ForInStatement" ||
415
- object.type == "ForOfStatement" ||
416
- object.type == "WhileStatement"
417
- ) {
418
- if (object.body.type == "BlockStatement") {
419
- return () => {
420
- if (object.body.body.length === 1) {
421
- object.body = object.body.body[0];
422
- }
423
- };
424
- }
425
- }
426
-
427
- // Last switch case does not need break
428
- if (object.type == "SwitchStatement") {
429
- var last = object.cases[object.cases.length - 1];
430
- if (last) {
431
- var lastStatement = last.consequent[last.consequent.length - 1];
432
- if (
433
- lastStatement &&
434
- lastStatement.type == "BreakStatement" &&
435
- lastStatement.label == null
436
- ) {
437
- last.consequent.pop();
438
- }
439
- } else {
440
- if (
441
- object.cases.length == 0 &&
442
- (object.discriminant.type == "Literal" ||
443
- object.discriminant.type == "Identifier")
444
- ) {
445
- if (
446
- parents[0].type == "LabeledStatement" &&
447
- Array.isArray(parents[1])
448
- ) {
449
- return () => {
450
- parents[1].splice(parents[1].indexOf(parents[0]), 1);
451
- };
452
- } else if (Array.isArray(parents[0])) {
453
- return () => {
454
- parents[0].splice(parents[0].indexOf(object), 1);
455
- };
456
- }
457
- }
458
- }
459
- }
460
-
461
- // if ( x ) { y() } -> x && y()
462
- // Todo Make this shit readable
463
- if (object.type == "IfStatement") {
464
- if (object.consequent.type != "BlockStatement") {
465
- this.replace(
466
- object.consequent,
467
- BlockStatement([clone(object.consequent)])
468
- );
469
- }
470
- if (object.alternate && object.alternate.type != "BlockStatement") {
471
- this.replace(
472
- object.alternate,
473
- BlockStatement([clone(object.alternate)])
474
- );
475
- }
476
- var body = getBlockBody(object.consequent);
477
-
478
- // Check for hard-coded if statements
479
- if (object.test.type == "Literal") {
480
- if (object.test.value || object.test.regex) {
481
- // Why would anyone test just a regex literal
482
- object.alternate = null;
483
- } else {
484
- object.consequent = BlockStatement([]);
485
- }
486
- }
487
-
488
- return () => {
489
- // if ( a ) { } else {b()} -> if ( !a ) b();
490
- if (body.length == 0 && object.alternate) {
491
- object.test = UnaryExpression("!", clone(object.test));
492
- if (
493
- object.alternate.type == "BlockStatement" &&
494
- object.alternate.body.length == 1
495
- ) {
496
- object.alternate = clone(object.alternate.body[0]);
497
- }
498
- object.consequent = object.alternate;
499
- object.alternate = null;
500
- }
501
-
502
- if (
503
- object.consequent &&
504
- object.consequent.body &&
505
- object.consequent.body.length == 1 &&
506
- object.alternate &&
507
- object.alternate.body.length == 1
508
- ) {
509
- var stmt1 = clone(object.consequent.body[0]);
510
- var stmt2 = clone(object.alternate.body[0]);
511
-
512
- // if (a) {return b;} else {return c;} -> return a ? b : c;
513
- if (
514
- stmt1.type == "ReturnStatement" &&
515
- stmt2.type == "ReturnStatement"
516
- ) {
517
- this.replace(
518
- object,
519
- ReturnStatement(
520
- ConditionalExpression(
521
- clone(object.test),
522
- stmt1.argument || Identifier("undefined"),
523
- stmt2.argument || Identifier("undefined")
456
+ // No need for sequence expression if there's only one expression
457
+ if (expressions.length === 1) return expressions[0];
458
+
459
+ return t.sequenceExpression(expressions);
460
+ }
461
+
462
+ path.replaceWith(
463
+ t.conditionalExpression(
464
+ path.node.test,
465
+ joinExpressions(consequentReturn.expressions),
466
+ joinExpressions(alternateReturn.expressions)
524
467
  )
525
- )
526
- );
468
+ );
469
+ }
527
470
  }
471
+ },
472
+ },
473
+ // Remove unreachable code
474
+ // Code after a return/throw/break/continue is unreachable
475
+ // Remove implied returns
476
+ // Remove code after if all branches are unreachable
477
+ "Block|SwitchCase": {
478
+ enter(path) {
479
+ if (path.isProgram()) {
480
+ path.scope.crawl();
481
+ }
482
+ },
483
+ exit(path) {
484
+ var statementList = path.isBlock()
485
+ ? (path.get("body") as NodePath<t.Statement>[])
486
+ : (path.get("consequent") as NodePath<t.Statement>[]);
528
487
 
529
- // if (a) {b = 0} else {b = 1} -> b = a ? 0 : 1;
530
- if (
531
- stmt1.type == "ExpressionStatement" &&
532
- stmt2.type == "ExpressionStatement"
488
+ var impliedReturn: NodePath<t.ReturnStatement>;
489
+
490
+ function isUnreachable(
491
+ statementList: NodePath<t.Statement>[],
492
+ topLevel = false
533
493
  ) {
534
- var e1 = stmt1.expression;
535
- var e2 = stmt2.expression;
494
+ var unreachableState = false;
495
+
496
+ for (var statement of statementList) {
497
+ if (unreachableState) {
498
+ statement.remove();
499
+ continue;
500
+ }
501
+
502
+ if (statement.isIfStatement()) {
503
+ const consequent = statement.get("consequent");
504
+ const alternate = statement.get("alternate");
505
+
506
+ if (
507
+ [consequent, alternate].every(
508
+ (x) =>
509
+ x.node &&
510
+ x.isBlockStatement() &&
511
+ isUnreachable(x.get("body"))
512
+ )
513
+ ) {
514
+ unreachableState = true;
515
+ if (!topLevel) {
516
+ return true;
517
+ } else {
518
+ continue;
519
+ }
520
+ }
521
+ }
522
+
523
+ if (statement.isSwitchStatement()) {
524
+ // Can only remove switch statements if all cases are unreachable
525
+ // And all paths are exhausted
526
+ const cases = statement.get("cases");
527
+ const hasDefaultCase = cases.some((x) => !x.node.test);
528
+ if (
529
+ hasDefaultCase &&
530
+ cases.every((x) => isUnreachable(x.get("consequent")))
531
+ ) {
532
+ unreachableState = true;
533
+ if (!topLevel) {
534
+ return true;
535
+ } else {
536
+ continue;
537
+ }
538
+ }
539
+ }
536
540
 
537
- if (
538
- e1.type == "AssignmentExpression" &&
539
- e2.type == "AssignmentExpression"
540
- ) {
541
541
  if (
542
- e1.operator === e2.operator &&
543
- isEquivalent(e1.left, e2.left)
542
+ statement.isReturnStatement() ||
543
+ statement.isThrowStatement() ||
544
+ statement.isBreakStatement() ||
545
+ statement.isContinueStatement()
544
546
  ) {
545
- this.replace(
546
- object,
547
- ExpressionStatement(
548
- AssignmentExpression(
549
- e1.operator,
550
- e1.left,
551
- ConditionalExpression(
552
- clone(object.test),
553
- e1.right,
554
- e2.right
555
- )
556
- )
557
- )
558
- );
547
+ unreachableState = true;
548
+ if (!topLevel) {
549
+ return true;
550
+ }
551
+ }
552
+
553
+ if (topLevel) {
554
+ if (
555
+ statement == statementList.at(-1) &&
556
+ statement.isReturnStatement() &&
557
+ !statement.node.argument
558
+ ) {
559
+ impliedReturn = statement;
560
+ }
559
561
  }
560
562
  }
563
+ return false;
561
564
  }
562
- }
563
- };
564
- }
565
-
566
- // x["abc"] -> x.abc
567
- if (object.type == "MemberExpression") {
568
- var { object: obj, property } = object;
569
-
570
- if (property.type == "Literal" && isValidIdentifier(property.value)) {
571
- object.computed = false;
572
- object.property.type = "Identifier";
573
- object.property.name = clone(object.property.value);
574
-
575
- // obj.name &&
576
- // this.log(
577
- // obj.name +
578
- // "['" +
579
- // object.property.name +
580
- // "'] -> " +
581
- // obj.name +
582
- // "." +
583
- // object.property.name
584
- // );
585
- }
586
- }
587
-
588
- if (object.type == "CallExpression") {
589
- if (object.callee.type == "MemberExpression") {
590
- var key = object.callee.computed
591
- ? object.callee.property.value
592
- : object.callee.property.name;
593
- if (key == "toString" && object.arguments.length == 0) {
594
- this.replace(
595
- object,
596
- BinaryExpression("+", Literal(""), clone(object.callee.object))
597
- );
598
- }
599
- }
600
- }
601
-
602
- // { "x": 1 } -> {x: 1}
603
- if (object.type === "Property" || object.type === "MethodDefinition") {
604
- if (
605
- object.key.type == "SequenceExpression" &&
606
- object.key.expressions.length == 1
607
- ) {
608
- object.key = object.key.expressions[0];
609
- object.computed = true;
610
- }
611
-
612
- if (
613
- object.key.type == "Literal" &&
614
- typeof object.key.value === "string" &&
615
- isValidIdentifier(object.key.value)
616
- ) {
617
- object.key.type = "Identifier";
618
- object.key.name = object.key.value;
619
- object.computed = false;
620
- }
621
- }
622
-
623
- if (object.type == "VariableDeclarator") {
624
- // undefined is not necessary
625
- if (object.init && object.init.type == "Identifier") {
626
- if (object.init.name == "undefined") {
627
- object.init = null;
628
- }
629
- }
630
-
631
- if (
632
- object.id.type == "ObjectPattern" &&
633
- object.init &&
634
- object.init.type == "ObjectExpression"
635
- ) {
636
- if (
637
- object.id.properties.length === 1 &&
638
- object.init.properties.length === 1
639
- ) {
640
- var key1 = object.id.properties[0].computed
641
- ? object.id.properties[0].key.value
642
- : object.id.properties[0].key.name;
643
- var key2 = object.init.properties[0].computed
644
- ? object.init.properties[0].key.value
645
- : object.init.properties[0].key.name;
646
-
647
- // console.log(key1, key2);
648
-
649
- if (key1 && key2 && key1 === key2) {
650
- object.id = object.id.properties[0].value;
651
- object.init = object.init.properties[0].value;
565
+
566
+ isUnreachable(statementList, true);
567
+
568
+ if (impliedReturn) {
569
+ var functionParent = path.getFunctionParent();
570
+ if (
571
+ functionParent &&
572
+ t.isBlockStatement(functionParent.node.body) &&
573
+ functionParent.node.body === path.node
574
+ ) {
575
+ impliedReturn.remove();
576
+ }
652
577
  }
653
- }
654
- }
655
-
656
- // check for redundant patterns
657
- if (
658
- object.id.type == "ArrayPattern" &&
659
- object.init &&
660
- typeof object.init === "object" &&
661
- object.init.type == "ArrayExpression"
662
- ) {
663
- if (
664
- object.id.elements.length == 1 &&
665
- object.init.elements.length == 1
666
- ) {
667
- object.id = object.id.elements[0];
668
- object.init = object.init.elements[0];
669
- }
670
- }
671
- }
672
-
673
- if (object.type == "Literal") {
674
- return () => {
675
- switch (typeof object.value) {
676
- case "boolean":
677
- this.replaceIdentifierOrLiteral(
678
- object,
679
- UnaryExpression("!", Literal(object.value ? 0 : 1)),
680
- parents
681
- );
682
- break;
683
- }
684
- };
685
- }
686
- if (object.type == "Identifier") {
687
- return () => {
688
- var info = getIdentifierInfo(object, parents);
689
- if (info.spec.isDefined || info.spec.isModified) return;
690
-
691
- if (object.name == "undefined" && !isForInitialize(object, parents)) {
692
- this.replaceIdentifierOrLiteral(
693
- object,
694
- UnaryExpression("void", Literal(0)),
695
- parents
696
- );
697
- } else if (object.name == "Infinity") {
698
- this.replaceIdentifierOrLiteral(
699
- object,
700
- BinaryExpression("/", Literal(1), Literal(0)),
701
- parents
702
- );
703
- }
704
- };
705
- }
706
-
707
- if (object.type == "UnaryExpression" && object.operator == "!") {
708
- if (object.argument.type == "Literal" && !object.argument.regex) {
709
- this.replace(object, Literal(!object.argument.value));
710
- }
711
- }
712
-
713
- if (object.type == "ConditionalExpression") {
714
- if (object.test.type == "Literal" && !object.test.regex) {
715
- this.replace(
716
- object,
717
- object.test.value ? object.consequent : object.alternate
718
- );
719
- }
720
- }
721
- }
722
- }
578
+ },
579
+ },
580
+ },
581
+ };
582
+ };