webfuscator 0.9.4

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 (289) hide show
  1. package/DISCLOSURE +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +83 -0
  4. package/dist/analysis/constant.d.ts +11 -0
  5. package/dist/analysis/constant.d.ts.map +1 -0
  6. package/dist/analysis/constant.js +261 -0
  7. package/dist/analysis/constant.js.map +1 -0
  8. package/dist/analysis/document-order.d.ts +5 -0
  9. package/dist/analysis/document-order.d.ts.map +1 -0
  10. package/dist/analysis/document-order.js +124 -0
  11. package/dist/analysis/document-order.js.map +1 -0
  12. package/dist/analysis/inlineability.d.ts +13 -0
  13. package/dist/analysis/inlineability.d.ts.map +1 -0
  14. package/dist/analysis/inlineability.js +270 -0
  15. package/dist/analysis/inlineability.js.map +1 -0
  16. package/dist/analysis/purity.d.ts +5 -0
  17. package/dist/analysis/purity.d.ts.map +1 -0
  18. package/dist/analysis/purity.js +281 -0
  19. package/dist/analysis/purity.js.map +1 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +2 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/obfuscator.d.ts +4 -0
  25. package/dist/obfuscator.d.ts.map +1 -0
  26. package/dist/obfuscator.js +222 -0
  27. package/dist/obfuscator.js.map +1 -0
  28. package/dist/options.d.ts +73 -0
  29. package/dist/options.d.ts.map +1 -0
  30. package/dist/options.js +2 -0
  31. package/dist/options.js.map +1 -0
  32. package/dist/preparation/dot-to-bracket.d.ts +16 -0
  33. package/dist/preparation/dot-to-bracket.d.ts.map +1 -0
  34. package/dist/preparation/dot-to-bracket.js +36 -0
  35. package/dist/preparation/dot-to-bracket.js.map +1 -0
  36. package/dist/preparation/expand-destructuring.d.ts +26 -0
  37. package/dist/preparation/expand-destructuring.d.ts.map +1 -0
  38. package/dist/preparation/expand-destructuring.js +154 -0
  39. package/dist/preparation/expand-destructuring.js.map +1 -0
  40. package/dist/preparation/expand-template-literals.d.ts +23 -0
  41. package/dist/preparation/expand-template-literals.d.ts.map +1 -0
  42. package/dist/preparation/expand-template-literals.js +78 -0
  43. package/dist/preparation/expand-template-literals.js.map +1 -0
  44. package/dist/preparation/rewrite-class-fields.d.ts +43 -0
  45. package/dist/preparation/rewrite-class-fields.d.ts.map +1 -0
  46. package/dist/preparation/rewrite-class-fields.js +270 -0
  47. package/dist/preparation/rewrite-class-fields.js.map +1 -0
  48. package/dist/preparation/split-sequence-expressions.d.ts +15 -0
  49. package/dist/preparation/split-sequence-expressions.d.ts.map +1 -0
  50. package/dist/preparation/split-sequence-expressions.js +36 -0
  51. package/dist/preparation/split-sequence-expressions.js.map +1 -0
  52. package/dist/preparation/split-variable-declarations.d.ts +20 -0
  53. package/dist/preparation/split-variable-declarations.d.ts.map +1 -0
  54. package/dist/preparation/split-variable-declarations.js +36 -0
  55. package/dist/preparation/split-variable-declarations.js.map +1 -0
  56. package/dist/preparation/stringify-object-keys.d.ts +21 -0
  57. package/dist/preparation/stringify-object-keys.d.ts.map +1 -0
  58. package/dist/preparation/stringify-object-keys.js +96 -0
  59. package/dist/preparation/stringify-object-keys.js.map +1 -0
  60. package/dist/preparation/wrap-single-statements.d.ts +24 -0
  61. package/dist/preparation/wrap-single-statements.d.ts.map +1 -0
  62. package/dist/preparation/wrap-single-statements.js +53 -0
  63. package/dist/preparation/wrap-single-statements.js.map +1 -0
  64. package/dist/transforms/arguments-to-parameters.d.ts +20 -0
  65. package/dist/transforms/arguments-to-parameters.d.ts.map +1 -0
  66. package/dist/transforms/arguments-to-parameters.js +162 -0
  67. package/dist/transforms/arguments-to-parameters.js.map +1 -0
  68. package/dist/transforms/arrow-to-function.d.ts +46 -0
  69. package/dist/transforms/arrow-to-function.d.ts.map +1 -0
  70. package/dist/transforms/arrow-to-function.js +203 -0
  71. package/dist/transforms/arrow-to-function.js.map +1 -0
  72. package/dist/transforms/collapse-single-use-temps.d.ts +16 -0
  73. package/dist/transforms/collapse-single-use-temps.d.ts.map +1 -0
  74. package/dist/transforms/collapse-single-use-temps.js +805 -0
  75. package/dist/transforms/collapse-single-use-temps.js.map +1 -0
  76. package/dist/transforms/const-let-to-var.d.ts +21 -0
  77. package/dist/transforms/const-let-to-var.d.ts.map +1 -0
  78. package/dist/transforms/const-let-to-var.js +217 -0
  79. package/dist/transforms/const-let-to-var.js.map +1 -0
  80. package/dist/transforms/const-to-let.d.ts +17 -0
  81. package/dist/transforms/const-to-let.d.ts.map +1 -0
  82. package/dist/transforms/const-to-let.js +33 -0
  83. package/dist/transforms/const-to-let.js.map +1 -0
  84. package/dist/transforms/do-while-to-while.d.ts +26 -0
  85. package/dist/transforms/do-while-to-while.d.ts.map +1 -0
  86. package/dist/transforms/do-while-to-while.js +54 -0
  87. package/dist/transforms/do-while-to-while.js.map +1 -0
  88. package/dist/transforms/drop-console.d.ts +27 -0
  89. package/dist/transforms/drop-console.d.ts.map +1 -0
  90. package/dist/transforms/drop-console.js +107 -0
  91. package/dist/transforms/drop-console.js.map +1 -0
  92. package/dist/transforms/drop-debugger.d.ts +18 -0
  93. package/dist/transforms/drop-debugger.d.ts.map +1 -0
  94. package/dist/transforms/drop-debugger.js +34 -0
  95. package/dist/transforms/drop-debugger.js.map +1 -0
  96. package/dist/transforms/expand-binary-assignment.d.ts +26 -0
  97. package/dist/transforms/expand-binary-assignment.d.ts.map +1 -0
  98. package/dist/transforms/expand-binary-assignment.js +78 -0
  99. package/dist/transforms/expand-binary-assignment.js.map +1 -0
  100. package/dist/transforms/expand-logical-assignment.d.ts +22 -0
  101. package/dist/transforms/expand-logical-assignment.d.ts.map +1 -0
  102. package/dist/transforms/expand-logical-assignment.js +73 -0
  103. package/dist/transforms/expand-logical-assignment.js.map +1 -0
  104. package/dist/transforms/extract-object-properties.d.ts +21 -0
  105. package/dist/transforms/extract-object-properties.d.ts.map +1 -0
  106. package/dist/transforms/extract-object-properties.js +340 -0
  107. package/dist/transforms/extract-object-properties.js.map +1 -0
  108. package/dist/transforms/fold-builtin-methods.d.ts +23 -0
  109. package/dist/transforms/fold-builtin-methods.d.ts.map +1 -0
  110. package/dist/transforms/fold-builtin-methods.js +839 -0
  111. package/dist/transforms/fold-builtin-methods.js.map +1 -0
  112. package/dist/transforms/fold-constants.d.ts +16 -0
  113. package/dist/transforms/fold-constants.d.ts.map +1 -0
  114. package/dist/transforms/fold-constants.js +421 -0
  115. package/dist/transforms/fold-constants.js.map +1 -0
  116. package/dist/transforms/for-to-while.d.ts +29 -0
  117. package/dist/transforms/for-to-while.d.ts.map +1 -0
  118. package/dist/transforms/for-to-while.js +91 -0
  119. package/dist/transforms/for-to-while.js.map +1 -0
  120. package/dist/transforms/function-declaration-to-expression.d.ts +28 -0
  121. package/dist/transforms/function-declaration-to-expression.d.ts.map +1 -0
  122. package/dist/transforms/function-declaration-to-expression.js +95 -0
  123. package/dist/transforms/function-declaration-to-expression.js.map +1 -0
  124. package/dist/transforms/inline-functions.d.ts +16 -0
  125. package/dist/transforms/inline-functions.d.ts.map +1 -0
  126. package/dist/transforms/inline-functions.js +896 -0
  127. package/dist/transforms/inline-functions.js.map +1 -0
  128. package/dist/transforms/logical-to-ternary.d.ts +20 -0
  129. package/dist/transforms/logical-to-ternary.d.ts.map +1 -0
  130. package/dist/transforms/logical-to-ternary.js +78 -0
  131. package/dist/transforms/logical-to-ternary.js.map +1 -0
  132. package/dist/transforms/mangle-properties.d.ts +27 -0
  133. package/dist/transforms/mangle-properties.d.ts.map +1 -0
  134. package/dist/transforms/mangle-properties.js +703 -0
  135. package/dist/transforms/mangle-properties.js.map +1 -0
  136. package/dist/transforms/nullish-coalescing-to-ternary.d.ts +18 -0
  137. package/dist/transforms/nullish-coalescing-to-ternary.d.ts.map +1 -0
  138. package/dist/transforms/nullish-coalescing-to-ternary.js +61 -0
  139. package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -0
  140. package/dist/transforms/numbers-to-strings.d.ts +19 -0
  141. package/dist/transforms/numbers-to-strings.d.ts.map +1 -0
  142. package/dist/transforms/numbers-to-strings.js +51 -0
  143. package/dist/transforms/numbers-to-strings.js.map +1 -0
  144. package/dist/transforms/object-method-to-property.d.ts +34 -0
  145. package/dist/transforms/object-method-to-property.d.ts.map +1 -0
  146. package/dist/transforms/object-method-to-property.js +78 -0
  147. package/dist/transforms/object-method-to-property.js.map +1 -0
  148. package/dist/transforms/optional-chaining-to-ternary.d.ts +19 -0
  149. package/dist/transforms/optional-chaining-to-ternary.d.ts.map +1 -0
  150. package/dist/transforms/optional-chaining-to-ternary.js +206 -0
  151. package/dist/transforms/optional-chaining-to-ternary.js.map +1 -0
  152. package/dist/transforms/pack-declarations-into-parameters.d.ts +22 -0
  153. package/dist/transforms/pack-declarations-into-parameters.d.ts.map +1 -0
  154. package/dist/transforms/pack-declarations-into-parameters.js +208 -0
  155. package/dist/transforms/pack-declarations-into-parameters.js.map +1 -0
  156. package/dist/transforms/remove-anonymous-function-names.d.ts +17 -0
  157. package/dist/transforms/remove-anonymous-function-names.d.ts.map +1 -0
  158. package/dist/transforms/remove-anonymous-function-names.js +72 -0
  159. package/dist/transforms/remove-anonymous-function-names.js.map +1 -0
  160. package/dist/transforms/remove-unreachable-code.d.ts +21 -0
  161. package/dist/transforms/remove-unreachable-code.d.ts.map +1 -0
  162. package/dist/transforms/remove-unreachable-code.js +103 -0
  163. package/dist/transforms/remove-unreachable-code.js.map +1 -0
  164. package/dist/transforms/remove-unused-code.d.ts +18 -0
  165. package/dist/transforms/remove-unused-code.d.ts.map +1 -0
  166. package/dist/transforms/remove-unused-code.js +249 -0
  167. package/dist/transforms/remove-unused-code.js.map +1 -0
  168. package/dist/transforms/rename-identifiers.d.ts +24 -0
  169. package/dist/transforms/rename-identifiers.d.ts.map +1 -0
  170. package/dist/transforms/rename-identifiers.js +264 -0
  171. package/dist/transforms/rename-identifiers.js.map +1 -0
  172. package/dist/transforms/specials-to-strings.d.ts +30 -0
  173. package/dist/transforms/specials-to-strings.d.ts.map +1 -0
  174. package/dist/transforms/specials-to-strings.js +101 -0
  175. package/dist/transforms/specials-to-strings.js.map +1 -0
  176. package/dist/transforms/switch-to-if.d.ts +28 -0
  177. package/dist/transforms/switch-to-if.d.ts.map +1 -0
  178. package/dist/transforms/switch-to-if.js +279 -0
  179. package/dist/transforms/switch-to-if.js.map +1 -0
  180. package/dist/transforms/ternary-to-if.d.ts +20 -0
  181. package/dist/transforms/ternary-to-if.d.ts.map +1 -0
  182. package/dist/transforms/ternary-to-if.js +315 -0
  183. package/dist/transforms/ternary-to-if.js.map +1 -0
  184. package/dist/transforms/update-to-assignment.d.ts +26 -0
  185. package/dist/transforms/update-to-assignment.d.ts.map +1 -0
  186. package/dist/transforms/update-to-assignment.js +120 -0
  187. package/dist/transforms/update-to-assignment.js.map +1 -0
  188. package/dist/transforms/yodify-conditions.d.ts +22 -0
  189. package/dist/transforms/yodify-conditions.d.ts.map +1 -0
  190. package/dist/transforms/yodify-conditions.js +91 -0
  191. package/dist/transforms/yodify-conditions.js.map +1 -0
  192. package/dist/utils/ast.d.ts +10 -0
  193. package/dist/utils/ast.d.ts.map +1 -0
  194. package/dist/utils/ast.js +112 -0
  195. package/dist/utils/ast.js.map +1 -0
  196. package/dist/utils/change-tracking.d.ts +8 -0
  197. package/dist/utils/change-tracking.d.ts.map +1 -0
  198. package/dist/utils/change-tracking.js +8 -0
  199. package/dist/utils/change-tracking.js.map +1 -0
  200. package/dist/utils/dom-properties.d.ts +2 -0
  201. package/dist/utils/dom-properties.d.ts.map +1 -0
  202. package/dist/utils/dom-properties.js +9252 -0
  203. package/dist/utils/dom-properties.js.map +1 -0
  204. package/dist/utils/evaluation-order.d.ts +4 -0
  205. package/dist/utils/evaluation-order.d.ts.map +1 -0
  206. package/dist/utils/evaluation-order.js +152 -0
  207. package/dist/utils/evaluation-order.js.map +1 -0
  208. package/dist/utils/literal.d.ts +5 -0
  209. package/dist/utils/literal.d.ts.map +1 -0
  210. package/dist/utils/literal.js +90 -0
  211. package/dist/utils/literal.js.map +1 -0
  212. package/dist/utils/logical-lowering.d.ts +4 -0
  213. package/dist/utils/logical-lowering.d.ts.map +1 -0
  214. package/dist/utils/logical-lowering.js +104 -0
  215. package/dist/utils/logical-lowering.js.map +1 -0
  216. package/dist/utils/loop-lowering.d.ts +23 -0
  217. package/dist/utils/loop-lowering.d.ts.map +1 -0
  218. package/dist/utils/loop-lowering.js +88 -0
  219. package/dist/utils/loop-lowering.js.map +1 -0
  220. package/dist/utils/paths.d.ts +9 -0
  221. package/dist/utils/paths.d.ts.map +1 -0
  222. package/dist/utils/paths.js +90 -0
  223. package/dist/utils/paths.js.map +1 -0
  224. package/dist/utils/random.d.ts +3 -0
  225. package/dist/utils/random.d.ts.map +1 -0
  226. package/dist/utils/random.js +22 -0
  227. package/dist/utils/random.js.map +1 -0
  228. package/dist/utils/string-generator.d.ts +37 -0
  229. package/dist/utils/string-generator.d.ts.map +1 -0
  230. package/dist/utils/string-generator.js +175 -0
  231. package/dist/utils/string-generator.js.map +1 -0
  232. package/package.json +76 -0
  233. package/src/analysis/constant.ts +310 -0
  234. package/src/analysis/document-order.ts +141 -0
  235. package/src/analysis/inlineability.ts +336 -0
  236. package/src/analysis/purity.ts +315 -0
  237. package/src/index.ts +2 -0
  238. package/src/obfuscator.ts +271 -0
  239. package/src/options.ts +114 -0
  240. package/src/preparation/dot-to-bracket.ts +41 -0
  241. package/src/preparation/expand-destructuring.ts +211 -0
  242. package/src/preparation/expand-template-literals.ts +92 -0
  243. package/src/preparation/rewrite-class-fields.ts +320 -0
  244. package/src/preparation/split-sequence-expressions.ts +40 -0
  245. package/src/preparation/split-variable-declarations.ts +39 -0
  246. package/src/preparation/stringify-object-keys.ts +107 -0
  247. package/src/preparation/wrap-single-statements.ts +63 -0
  248. package/src/transforms/arguments-to-parameters.ts +187 -0
  249. package/src/transforms/arrow-to-function.ts +225 -0
  250. package/src/transforms/collapse-single-use-temps.ts +957 -0
  251. package/src/transforms/const-let-to-var.ts +246 -0
  252. package/src/transforms/const-to-let.ts +38 -0
  253. package/src/transforms/do-while-to-while.ts +69 -0
  254. package/src/transforms/drop-console.ts +119 -0
  255. package/src/transforms/drop-debugger.ts +38 -0
  256. package/src/transforms/expand-binary-assignment.ts +94 -0
  257. package/src/transforms/expand-logical-assignment.ts +84 -0
  258. package/src/transforms/extract-object-properties.ts +404 -0
  259. package/src/transforms/fold-builtin-methods.ts +949 -0
  260. package/src/transforms/fold-constants.ts +462 -0
  261. package/src/transforms/for-to-while.ts +109 -0
  262. package/src/transforms/function-declaration-to-expression.ts +108 -0
  263. package/src/transforms/inline-functions.ts +1074 -0
  264. package/src/transforms/logical-to-ternary.ts +92 -0
  265. package/src/transforms/mangle-properties.ts +819 -0
  266. package/src/transforms/nullish-coalescing-to-ternary.ts +77 -0
  267. package/src/transforms/numbers-to-strings.ts +61 -0
  268. package/src/transforms/object-method-to-property.ts +86 -0
  269. package/src/transforms/optional-chaining-to-ternary.ts +225 -0
  270. package/src/transforms/pack-declarations-into-parameters.ts +233 -0
  271. package/src/transforms/remove-anonymous-function-names.ts +84 -0
  272. package/src/transforms/remove-unreachable-code.ts +115 -0
  273. package/src/transforms/remove-unused-code.ts +296 -0
  274. package/src/transforms/rename-identifiers.ts +289 -0
  275. package/src/transforms/specials-to-strings.ts +116 -0
  276. package/src/transforms/switch-to-if.ts +339 -0
  277. package/src/transforms/ternary-to-if.ts +387 -0
  278. package/src/transforms/update-to-assignment.ts +132 -0
  279. package/src/transforms/yodify-conditions.ts +103 -0
  280. package/src/utils/ast.ts +129 -0
  281. package/src/utils/change-tracking.ts +15 -0
  282. package/src/utils/dom-properties.ts +9252 -0
  283. package/src/utils/evaluation-order.ts +178 -0
  284. package/src/utils/literal.ts +96 -0
  285. package/src/utils/logical-lowering.ts +132 -0
  286. package/src/utils/loop-lowering.ts +109 -0
  287. package/src/utils/paths.ts +103 -0
  288. package/src/utils/random.ts +22 -0
  289. package/src/utils/string-generator.ts +206 -0
@@ -0,0 +1,289 @@
1
+ import traverse from '@babel/traverse'
2
+ import type { Binding, Scope } from '@babel/traverse'
3
+ import * as t from '@babel/types'
4
+ import type { File } from '@babel/types'
5
+
6
+ import type { TransformContext } from 'src/options'
7
+ import { isDirectEvalCall, isInsideWith } from 'src/utils/ast'
8
+ import { hasAnnexBFunctionAlias, referencesOrWritesVariable } from 'src/utils/paths'
9
+ import { mulberry32 } from 'src/utils/random'
10
+ import { StringGenerator } from 'src/utils/string-generator'
11
+
12
+ /**
13
+ * Renames bindings and labels with a fresh `StringGenerator`. Each scope avoids
14
+ * live ancestor names but otherwise restarts the cursor, allowing safe reuse in
15
+ * sibling scopes.
16
+ *
17
+ * @example
18
+ * // ◀️ before
19
+ * function compute(value, multiplier) {
20
+ * var scaled = value * multiplier;
21
+ * return scaled;
22
+ * }
23
+ * compute(3, 4);
24
+ *
25
+ * // ▶️ after
26
+ * function a(b, c) {
27
+ * var d = b * c;
28
+ * return d;
29
+ * }
30
+ * a(3, 4);
31
+ */
32
+ export function renameIdentifiers(ast: File, ctx: TransformContext): boolean {
33
+ const generator = new StringGenerator(ctx.stringGeneratorMode, mulberry32(ctx.seed))
34
+
35
+ let programScope: Scope | undefined
36
+ traverse(ast, {
37
+ Program(path) {
38
+ programScope = path.scope
39
+ path.stop()
40
+ },
41
+ })
42
+ if (!programScope) {
43
+ return false
44
+ }
45
+ programScope.crawl()
46
+
47
+ const liveAncestors = new Map<Scope, Set<Binding>>()
48
+ // Reserve unbound names so generated bindings cannot capture them.
49
+ const freeNames = new Set<string>()
50
+ // `with` and direct eval pin bindings to their source names.
51
+ const unrenamable = new Set<Binding>()
52
+ // Reserve pinned source names globally, including before their binding is seen.
53
+ const pinnedNames = new Set<string>()
54
+ const pin = (binding: Binding, name: string): void => {
55
+ unrenamable.add(binding)
56
+ pinnedNames.add(name)
57
+ }
58
+ traverse(ast, {
59
+ Identifier(path) {
60
+ if (isNonBindingPosition(path)) {
61
+ return
62
+ }
63
+ const binding = path.scope.getBinding(path.node.name)
64
+ if (!binding) {
65
+ // Assignment-only globals are free names even without a read reference.
66
+ if (referencesOrWritesVariable(path)) {
67
+ freeNames.add(path.node.name)
68
+ }
69
+ return
70
+ }
71
+ if (isInsideWith(path)) {
72
+ pin(binding, path.node.name)
73
+ }
74
+ let scope: Scope | undefined = path.scope
75
+ while (scope && scope !== binding.scope) {
76
+ let live = liveAncestors.get(scope)
77
+ if (!live) {
78
+ live = new Set()
79
+ liveAncestors.set(scope, live)
80
+ }
81
+ live.add(binding)
82
+ scope = scope.parent
83
+ }
84
+ },
85
+ CallExpression(path) {
86
+ // Direct eval can read every visible binding by its source name.
87
+ if (!isDirectEvalCall(path.node)) {
88
+ return
89
+ }
90
+ let scope: Scope | undefined = path.scope
91
+ while (scope) {
92
+ for (const name of Object.keys(scope.bindings)) {
93
+ pin(scope.bindings[name]!, name)
94
+ }
95
+ scope = scope.parent
96
+ }
97
+ },
98
+ FunctionDeclaration(path) {
99
+ // ECMA-262 B.3.3 gives sloppy block functions an alias Babel does not model.
100
+ if (!hasAnnexBFunctionAlias(path)) {
101
+ return
102
+ }
103
+ const name = path.node.id?.name
104
+ const binding = name === undefined ? undefined : path.scope.getBinding(name)
105
+ if (binding && name !== undefined) {
106
+ pin(binding, name)
107
+ }
108
+ },
109
+ CatchClause(path) {
110
+ // Babel splits a catch parameter and same-named `var` that share one runtime
111
+ // variable. They must keep the same name.
112
+ const fnScope = path.scope.getFunctionParent() ?? path.scope.getProgramParent()
113
+ for (const name of Object.keys(path.scope.bindings)) {
114
+ const varBinding = fnScope.getOwnBinding(name)
115
+ if (varBinding) {
116
+ pin(path.scope.bindings[name]!, name)
117
+ pin(varBinding, name)
118
+ }
119
+ }
120
+ },
121
+ })
122
+
123
+ const bindingToName = new Map<Binding, string>()
124
+ const idToName = new WeakMap<t.Identifier, string>()
125
+
126
+ traverse(ast, {
127
+ Scopable(path) {
128
+ const { scope } = path
129
+ // Test global reservations during allocation to avoid copying them per scope.
130
+ const offLimits = new Set<string>()
131
+ const live = liveAncestors.get(scope)
132
+ if (live) {
133
+ for (const ancestor of live) {
134
+ const name = bindingToName.get(ancestor)
135
+ if (name !== undefined) {
136
+ offLimits.add(name)
137
+ }
138
+ }
139
+ }
140
+
141
+ let cursor = 0
142
+ for (const oldName of Object.keys(scope.bindings)) {
143
+ const binding = scope.bindings[oldName]!
144
+ if (!t.isIdentifier(binding.identifier)) {
145
+ continue
146
+ }
147
+ // `pin` already reserved this binding's source name.
148
+ if (unrenamable.has(binding)) {
149
+ continue
150
+ }
151
+
152
+ const inherited = idToName.get(binding.identifier)
153
+ if (inherited !== undefined) {
154
+ bindingToName.set(binding, inherited)
155
+ offLimits.add(inherited)
156
+ continue
157
+ }
158
+
159
+ let newName: string
160
+ do {
161
+ newName = generator.at(cursor++)
162
+ } while (offLimits.has(newName) || freeNames.has(newName) || pinnedNames.has(newName))
163
+
164
+ offLimits.add(newName)
165
+ bindingToName.set(binding, newName)
166
+ idToName.set(binding.identifier, newName)
167
+ }
168
+ },
169
+ })
170
+
171
+ if (bindingToName.size === 0) {
172
+ return renameLabels(ast, generator)
173
+ }
174
+
175
+ traverse(ast, {
176
+ Identifier(path) {
177
+ const { node } = path
178
+ const directName = idToName.get(node)
179
+ if (directName !== undefined) {
180
+ node.name = directName
181
+ return
182
+ }
183
+ if (isNonBindingPosition(path)) {
184
+ return
185
+ }
186
+ const binding = path.scope.getBinding(node.name)
187
+ if (!binding) {
188
+ return
189
+ }
190
+ const newName = bindingToName.get(binding)
191
+ if (newName === undefined) {
192
+ return
193
+ }
194
+ node.name = newName
195
+ },
196
+ })
197
+
198
+ renameLabels(ast, generator)
199
+ return true
200
+ }
201
+
202
+ function isNonBindingPosition(path: { node: t.Identifier; parent: t.Node }): boolean {
203
+ const { node, parent } = path
204
+ // A PrivateName id is not a variable and must match its class declaration.
205
+ if (t.isPrivateName(parent)) {
206
+ return true
207
+ }
208
+ if (t.isMemberExpression(parent) && parent.property === node && !parent.computed) {
209
+ return true
210
+ }
211
+ if (t.isOptionalMemberExpression(parent) && parent.property === node && !parent.computed) {
212
+ return true
213
+ }
214
+ if (
215
+ (t.isObjectProperty(parent) || t.isObjectMethod(parent)) &&
216
+ parent.key === node &&
217
+ !parent.computed
218
+ ) {
219
+ return true
220
+ }
221
+ if (
222
+ (t.isClassMethod(parent) || t.isClassProperty(parent) || t.isClassPrivateProperty(parent)) &&
223
+ 'key' in parent &&
224
+ parent.key === node &&
225
+ !('computed' in parent && parent.computed)
226
+ ) {
227
+ return true
228
+ }
229
+ // External import and export names are part of the module contract.
230
+ if (t.isImportSpecifier(parent) && parent.imported === node) {
231
+ return true
232
+ }
233
+ if (t.isExportSpecifier(parent) && parent.exported === node) {
234
+ return true
235
+ }
236
+ if (t.isLabeledStatement(parent) && parent.label === node) {
237
+ return true
238
+ }
239
+ if (t.isBreakStatement(parent) && parent.label === node) {
240
+ return true
241
+ }
242
+ if (t.isContinueStatement(parent) && parent.label === node) {
243
+ return true
244
+ }
245
+ if (t.isMetaProperty(parent)) {
246
+ return true
247
+ }
248
+ return false
249
+ }
250
+
251
+ function renameLabels(ast: File, generator: StringGenerator): boolean {
252
+ const labelRenames: { node: t.LabeledStatement; oldName: string; newName: string }[] = []
253
+ traverse(ast, {
254
+ LabeledStatement(path) {
255
+ labelRenames.push({
256
+ node: path.node,
257
+ oldName: path.node.label.name,
258
+ newName: generator.next(),
259
+ })
260
+ },
261
+ })
262
+
263
+ if (labelRenames.length === 0) {
264
+ return false
265
+ }
266
+
267
+ for (const { node, oldName, newName } of labelRenames) {
268
+ node.label.name = newName
269
+ rewriteLabelReferences(node, oldName, newName)
270
+ }
271
+ return true
272
+ }
273
+
274
+ // A same-named inner label owns its references and is renamed separately.
275
+ function rewriteLabelReferences(node: t.LabeledStatement, oldName: string, newName: string): void {
276
+ t.traverseFast(node.body, (visited) => {
277
+ const shadowedByInnerLabel =
278
+ t.isLabeledStatement(visited) && visited !== node && visited.label.name === oldName
279
+ if (!shadowedByInnerLabel) {
280
+ if (t.isBreakStatement(visited) && visited.label?.name === oldName) {
281
+ visited.label.name = newName
282
+ }
283
+ if (t.isContinueStatement(visited) && visited.label?.name === oldName) {
284
+ visited.label.name = newName
285
+ }
286
+ }
287
+ return shadowedByInnerLabel ? t.traverseFast.skip : undefined
288
+ })
289
+ }
@@ -0,0 +1,116 @@
1
+ import traverse from '@babel/traverse'
2
+ import type { NodePath, Visitor } from '@babel/traverse'
3
+ import * as t from '@babel/types'
4
+ import type { File } from '@babel/types'
5
+
6
+ import type { TransformContext } from 'src/options'
7
+ import { enclosingScopeHasDirectEval, isInsideWith } from 'src/utils/ast'
8
+ import type { ChangeState } from 'src/utils/change-tracking'
9
+ import { mulberry32 } from 'src/utils/random'
10
+ import { StringGenerator } from 'src/utils/string-generator'
11
+
12
+ /**
13
+ * Rewrites five constant-like values through strings for later string mangling:
14
+ *
15
+ * - `true` -> `!!"<random>"`
16
+ * - `false` -> `!"<random>"`
17
+ * - `NaN` -> `+"<random>"`
18
+ * - `undefined`-> `void "<random>"`
19
+ * - `Infinity` -> `1/!"<random>"`
20
+ *
21
+ * Generated strings are nonempty and nonnumeric, fixing their truthiness and
22
+ * numeric coercion.
23
+ *
24
+ * `NaN`, `Infinity`, and `undefined` require genuine global reads. Delete and
25
+ * write positions stay unchanged because the replacement is not a reference.
26
+ *
27
+ * @example
28
+ * // ◀️ before
29
+ * if (x === undefined) return NaN;
30
+ * var lim = !done ? Infinity : 0;
31
+ * var flag = !x ? true : false;
32
+ *
33
+ * // ▶️ after
34
+ * if (x === void "ab") return +"cd";
35
+ * var lim = !done ? 1 / !"ef" : 0;
36
+ * var flag = !x ? !!"gh" : !"ij";
37
+ */
38
+ export function specialsToStrings(ast: File, ctx: TransformContext): boolean {
39
+ const state: State = {
40
+ changed: false,
41
+ generator: new StringGenerator(ctx.stringGeneratorMode, mulberry32(ctx.seed)),
42
+ }
43
+ traverse(ast, visitor, undefined, state)
44
+ return state.changed
45
+ }
46
+
47
+ interface State extends ChangeState {
48
+ generator: StringGenerator
49
+ }
50
+
51
+ const GLOBAL_SPECIALS = new Set(['NaN', 'Infinity', 'undefined'])
52
+
53
+ const visitor: Visitor<State> = {
54
+ BooleanLiteral(path, state) {
55
+ const str = t.stringLiteral(state.generator.next())
56
+ const negated = t.unaryExpression('!', str)
57
+ path.replaceWith(path.node.value ? t.unaryExpression('!', negated) : negated)
58
+ path.skip()
59
+ state.changed = true
60
+ },
61
+ Identifier(path, state) {
62
+ const { name } = path.node
63
+ if (!GLOBAL_SPECIALS.has(name)) {
64
+ return
65
+ }
66
+ if (!path.isReferencedIdentifier()) {
67
+ return
68
+ }
69
+ if (path.scope.getBinding(name)) {
70
+ return
71
+ }
72
+ // Replacements are not write targets. `with` and eval can also shadow globals.
73
+ if (isWriteTarget(path) || isInsideWith(path) || enclosingScopeHasDirectEval(path)) {
74
+ return
75
+ }
76
+ const str = t.stringLiteral(state.generator.next())
77
+ path.replaceWith(buildGlobalReplacement(name, str))
78
+ path.skip()
79
+ state.changed = true
80
+ },
81
+ }
82
+
83
+ function buildGlobalReplacement(name: string, str: t.StringLiteral): t.Expression {
84
+ if (name === 'undefined') {
85
+ return t.unaryExpression('void', str)
86
+ }
87
+ if (name === 'NaN') {
88
+ return t.unaryExpression('+', str)
89
+ }
90
+ return t.binaryExpression('/', t.numericLiteral(1), t.unaryExpression('!', str))
91
+ }
92
+
93
+ // Babel calls update and iterator targets references, but replacements there
94
+ // would not be assignable. Delete also distinguishes references from values.
95
+ function isWriteTarget(path: NodePath<t.Identifier>): boolean {
96
+ const { parentPath } = path
97
+ if (!parentPath) {
98
+ return false
99
+ }
100
+ if (parentPath.isUpdateExpression({ argument: path.node })) {
101
+ return true
102
+ }
103
+ if (parentPath.isAssignmentExpression({ left: path.node })) {
104
+ return true
105
+ }
106
+ if (parentPath.isUnaryExpression({ argument: path.node, operator: 'delete' })) {
107
+ return true
108
+ }
109
+ if (
110
+ (parentPath.isForInStatement() || parentPath.isForOfStatement()) &&
111
+ parentPath.node.left === path.node
112
+ ) {
113
+ return true
114
+ }
115
+ return false
116
+ }