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,187 @@
1
+ import type { NodePath, Visitor } from '@babel/traverse'
2
+ import * as t from '@babel/types'
3
+ import type { File } from '@babel/types'
4
+
5
+ import { hasDuplicateParamNames } from 'src/utils/ast'
6
+ import { traverseForChanges } from 'src/utils/change-tracking'
7
+ import type { ChangeState } from 'src/utils/change-tracking'
8
+ import { isInStrictContext } from 'src/utils/paths'
9
+
10
+ /**
11
+ * Replaces `arguments[i]` reads with their mapped parameter in sloppy functions
12
+ * with simple parameter lists. Writes, deletion, reassigned parameters, and any
13
+ * escape of the arguments object make the mapping unsafe. Missing parameters
14
+ * are never added because that would change `fn.length`.
15
+ *
16
+ * @example
17
+ * // ◀️ before
18
+ * function foo(bar) {
19
+ * log(arguments[0]);
20
+ * }
21
+ *
22
+ * // ▶️ after
23
+ * function foo(bar) {
24
+ * log(bar);
25
+ * }
26
+ */
27
+ export function argumentsToParameters(ast: File): boolean {
28
+ return traverseForChanges(ast, visitor)
29
+ }
30
+
31
+ const visitor: Visitor<ChangeState> = {
32
+ Function(path, state) {
33
+ if (processFunction(path)) {
34
+ state.changed = true
35
+ }
36
+ },
37
+ }
38
+
39
+ interface CollectState {
40
+ reads: { memPath: NodePath<t.MemberExpression>; index: number }[]
41
+ writtenIndices: Set<number>
42
+ mappingBroken: boolean
43
+ }
44
+
45
+ const collectVisitor: Visitor<CollectState> = {
46
+ Function(innerPath) {
47
+ if (!innerPath.isArrowFunctionExpression()) {
48
+ innerPath.skip()
49
+ }
50
+ },
51
+ Identifier(path, state) {
52
+ if (path.node.name !== 'arguments' || !path.isReferencedIdentifier()) {
53
+ return
54
+ }
55
+ if (path.scope.getBinding('arguments')) {
56
+ return
57
+ }
58
+ const parent = path.parentPath
59
+ // Any other use can expose or mutate the arguments object.
60
+ if (
61
+ (parent.isMemberExpression() || parent.isOptionalMemberExpression()) &&
62
+ parent.node.object === path.node
63
+ ) {
64
+ return
65
+ }
66
+ state.mappingBroken = true
67
+ },
68
+ AssignmentExpression(assignPath, state) {
69
+ // Reassignment replaces the mapped object.
70
+ if (
71
+ t.isIdentifier(assignPath.node.left, { name: 'arguments' }) &&
72
+ !assignPath.scope.getBinding('arguments')
73
+ ) {
74
+ state.mappingBroken = true
75
+ }
76
+ },
77
+ MemberExpression(memPath, state) {
78
+ const obj = memPath.node.object
79
+ if (!t.isIdentifier(obj) || obj.name !== 'arguments') {
80
+ return
81
+ }
82
+ if (memPath.scope.getBinding('arguments')) {
83
+ return
84
+ }
85
+
86
+ // Deleting an index breaks its parameter mapping.
87
+ if (memPath.parentPath?.isUnaryExpression({ operator: 'delete' })) {
88
+ state.mappingBroken = true
89
+ return
90
+ }
91
+
92
+ if (!memPath.node.computed) {
93
+ return
94
+ }
95
+ const prop = memPath.node.property
96
+ if (!t.isNumericLiteral(prop)) {
97
+ return
98
+ }
99
+ const index = prop.value
100
+ if (!Number.isInteger(index) || index < 0) {
101
+ return
102
+ }
103
+
104
+ if (memberIsWriteTarget(memPath)) {
105
+ state.writtenIndices.add(index)
106
+ return
107
+ }
108
+ state.reads.push({ memPath, index })
109
+ },
110
+ }
111
+
112
+ function processFunction(path: NodePath<t.Function>): boolean {
113
+ if (path.isArrowFunctionExpression()) {
114
+ return false
115
+ }
116
+ const { params } = path.node
117
+ // Rest, defaults, and destructuring make `arguments` unmapped.
118
+ if (!params.every((param) => t.isIdentifier(param))) {
119
+ return false
120
+ }
121
+ // Duplicate names map only their last index.
122
+ if (hasDuplicateParamNames(params)) {
123
+ return false
124
+ }
125
+ // Strict-mode `arguments` is not mapped to parameters.
126
+ if (isInStrictContext(path)) {
127
+ return false
128
+ }
129
+
130
+ const state: CollectState = { reads: [], writtenIndices: new Set(), mappingBroken: false }
131
+ path.traverse(collectVisitor, state)
132
+ if (state.mappingBroken || state.reads.length === 0) {
133
+ return false
134
+ }
135
+
136
+ let changed = false
137
+ for (const { memPath, index } of state.reads) {
138
+ // A written index and its parameter diverge when the argument was omitted.
139
+ if (state.writtenIndices.has(index)) {
140
+ continue
141
+ }
142
+ // Adding a parameter would change `fn.length`.
143
+ const param = params[index]
144
+ if (!param || !t.isIdentifier(param)) {
145
+ continue
146
+ }
147
+ // Reassigned parameters diverge when the argument was omitted.
148
+ const binding = path.scope.getBinding(param.name)
149
+ if (binding && binding.constantViolations.length > 0) {
150
+ continue
151
+ }
152
+ memPath.replaceWith(t.identifier(param.name))
153
+ changed = true
154
+ }
155
+ return changed
156
+ }
157
+
158
+ // Treat any occurrence under a write target as written. The conservative member
159
+ // case only skips a safe substitution.
160
+ function memberIsWriteTarget(memPath: NodePath<t.MemberExpression>): boolean {
161
+ const directParent = memPath.parentPath?.node
162
+ if (
163
+ directParent &&
164
+ (t.isForInStatement(directParent) || t.isForOfStatement(directParent)) &&
165
+ directParent.left === memPath.node
166
+ ) {
167
+ return true
168
+ }
169
+ // Writes may reach the member through an enclosing destructuring pattern.
170
+ let currentNode: t.Node = memPath.node
171
+ let cursor: NodePath | null = memPath.parentPath
172
+ while (cursor && !cursor.isStatement()) {
173
+ const parent = cursor.node
174
+ if (t.isUpdateExpression(parent)) {
175
+ return true
176
+ }
177
+ if (t.isAssignmentExpression(parent)) {
178
+ return parent.left === currentNode
179
+ }
180
+ if (t.isArrayPattern(parent) || t.isObjectPattern(parent) || t.isRestElement(parent)) {
181
+ return true
182
+ }
183
+ currentNode = parent
184
+ cursor = cursor.parentPath
185
+ }
186
+ return false
187
+ }
@@ -0,0 +1,225 @@
1
+ import type { NodePath, Visitor } from '@babel/traverse'
2
+ import * as t from '@babel/types'
3
+ import type { File } from '@babel/types'
4
+
5
+ import { enclosingScopeHasDirectEval, isInsideWith } from 'src/utils/ast'
6
+ import { traverseForChanges } from 'src/utils/change-tracking'
7
+ import type { ChangeState } from 'src/utils/change-tracking'
8
+
9
+ /**
10
+ * Lowers arrows with Babel's capture-aware `arrowFunctionToExpression`. Arrows
11
+ * that reach `super` stay intact. Values that escape visible call positions also
12
+ * stay intact because only the arrow rejects construction with `new`.
13
+ *
14
+ * @example
15
+ * // ◀️ before
16
+ * var fn = x => {
17
+ * return x + 1;
18
+ * };
19
+ * var thisFn = function () {
20
+ * var read = () => {
21
+ * return this.x;
22
+ * };
23
+ * return read();
24
+ * };
25
+ * var escapes = [() => 1];
26
+ * class Sub extends Base {
27
+ * m() {
28
+ * var call = () => super.m();
29
+ * return call();
30
+ * }
31
+ * }
32
+ *
33
+ * // ▶️ after
34
+ * var fn = function (x) {
35
+ * return x + 1;
36
+ * };
37
+ * var thisFn = function () {
38
+ * var _this = this;
39
+ * var read = function () {
40
+ * return _this.x;
41
+ * };
42
+ * return read();
43
+ * };
44
+ * var escapes = [() => 1];
45
+ * class Sub extends Base {
46
+ * m() {
47
+ * var call = () => super.m();
48
+ * return call();
49
+ * }
50
+ * }
51
+ */
52
+ export function arrowToFunction(ast: File): boolean {
53
+ return traverseForChanges(ast, visitor)
54
+ }
55
+
56
+ const visitor: Visitor<ChangeState> = {
57
+ ArrowFunctionExpression(path, state) {
58
+ if (!path.isArrowFunctionExpression()) {
59
+ return
60
+ }
61
+ if (
62
+ // The lowered function gains construction and `prototype`. Every use must
63
+ // remain visible so those differences cannot be observed.
64
+ !valueStaysInView(path) ||
65
+ arrowReferencesSuper(path) ||
66
+ inUnsafeCaptureContext(path) ||
67
+ usesReassignedArguments(path) ||
68
+ // `with` can shadow capture aliases, and direct eval can inspect the changed
69
+ // runtime meaning of `this` or `arguments`.
70
+ isInsideWith(path) ||
71
+ enclosingScopeHasDirectEval(path)
72
+ ) {
73
+ return
74
+ }
75
+ path.arrowFunctionToExpression({
76
+ allowInsertArrow: false,
77
+ noNewArrows: true,
78
+ })
79
+ state.changed = true
80
+ },
81
+ }
82
+
83
+ // Babel places captures in the nearest function body. Fields, static blocks,
84
+ // parameter defaults, and computed method keys evaluate in a different scope or
85
+ // at a different time.
86
+ function inUnsafeCaptureContext(path: NodePath<t.ArrowFunctionExpression>): boolean {
87
+ for (let current: NodePath | null = path.parentPath; current; current = current.parentPath) {
88
+ if (current.isStaticBlock() || current.isClassProperty() || current.isClassPrivateProperty()) {
89
+ return true
90
+ }
91
+ if (current.isFunction()) {
92
+ if (current.isArrowFunctionExpression()) {
93
+ continue
94
+ }
95
+ return arrowIsOutsideBodyOf(path, current)
96
+ }
97
+ if (current.isProgram()) {
98
+ return false
99
+ }
100
+ }
101
+ return false
102
+ }
103
+
104
+ // Captures placed in the body cannot serve parameter defaults or computed keys.
105
+ function arrowIsOutsideBodyOf(path: NodePath, fnPath: NodePath): boolean {
106
+ let child: NodePath = path
107
+ for (;;) {
108
+ const next: NodePath | null = child.parentPath
109
+ if (!next || next === fnPath) {
110
+ break
111
+ }
112
+ child = next
113
+ }
114
+ return child.listKey === 'params' || child.key === 'key'
115
+ }
116
+
117
+ // A capture snapshots `arguments` before any later reassignment.
118
+ function usesReassignedArguments(path: NodePath<t.ArrowFunctionExpression>): boolean {
119
+ if (!referencesName(path, 'arguments')) {
120
+ return false
121
+ }
122
+ const fnPath = nearestNonArrowFunction(path)
123
+ return fnPath != null && reassignsName(fnPath, 'arguments')
124
+ }
125
+
126
+ // Immediate calls, discarded values, and bindings used only for immediate calls
127
+ // keep every use visible. Any other escape could expose construction, binding,
128
+ // or `prototype` behavior.
129
+ function valueStaysInView(valuePath: NodePath): boolean {
130
+ const parent = valuePath.parentPath
131
+ if (!parent) {
132
+ return false
133
+ }
134
+ if (isImmediateCallee(valuePath, parent) || parent.isExpressionStatement()) {
135
+ return true
136
+ }
137
+ if (
138
+ !parent.isVariableDeclarator() ||
139
+ parent.node.init !== valuePath.node ||
140
+ !t.isIdentifier(parent.node.id)
141
+ ) {
142
+ return false
143
+ }
144
+ const binding = valuePath.scope.getBinding(parent.node.id.name)
145
+ if (!binding || !binding.constant) {
146
+ return false
147
+ }
148
+ return binding.referencePaths.every(
149
+ (ref) => ref.parentPath != null && isImmediateCallee(ref, ref.parentPath),
150
+ )
151
+ }
152
+
153
+ function isImmediateCallee(valuePath: NodePath, parent: NodePath): boolean {
154
+ return (
155
+ (parent.isCallExpression() || parent.isOptionalCallExpression()) &&
156
+ parent.node.callee === valuePath.node
157
+ )
158
+ }
159
+
160
+ function referencesName(path: NodePath, name: string): boolean {
161
+ let found = false
162
+ path.traverse({
163
+ Function(inner) {
164
+ if (!inner.isArrowFunctionExpression()) {
165
+ inner.skip()
166
+ }
167
+ },
168
+ Identifier(inner) {
169
+ if (inner.node.name === name && inner.isReferencedIdentifier()) {
170
+ found = true
171
+ }
172
+ },
173
+ })
174
+ return found
175
+ }
176
+
177
+ function reassignsName(fnPath: NodePath, name: string): boolean {
178
+ let found = false
179
+ fnPath.traverse({
180
+ Function(inner) {
181
+ // Nested arrows share this function's `arguments` binding.
182
+ if (!inner.isArrowFunctionExpression()) {
183
+ inner.skip()
184
+ }
185
+ },
186
+ AssignmentExpression(inner) {
187
+ // Destructuring can rebind `arguments`; member writes only mutate it.
188
+ if (Object.hasOwn(t.getBindingIdentifiers(inner.node.left), name)) {
189
+ found = true
190
+ }
191
+ },
192
+ UpdateExpression(inner) {
193
+ if (t.isIdentifier(inner.node.argument, { name })) {
194
+ found = true
195
+ }
196
+ },
197
+ })
198
+ return found
199
+ }
200
+
201
+ function nearestNonArrowFunction(path: NodePath): NodePath | null {
202
+ for (let current: NodePath | null = path.parentPath; current; current = current.parentPath) {
203
+ if (current.isFunction() && !current.isArrowFunctionExpression()) {
204
+ return current
205
+ }
206
+ }
207
+ return null
208
+ }
209
+
210
+ function arrowReferencesSuper(path: NodePath<t.ArrowFunctionExpression>): boolean {
211
+ const state: { found: boolean } = { found: false }
212
+ path.traverse(superCheckVisitor, state)
213
+ return state.found
214
+ }
215
+
216
+ const superCheckVisitor: Visitor<{ found: boolean }> = {
217
+ Function(path) {
218
+ if (!path.isArrowFunctionExpression()) {
219
+ path.skip()
220
+ }
221
+ },
222
+ Super(_path, state) {
223
+ state.found = true
224
+ },
225
+ }