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.
- package/DISCLOSURE +9 -0
- package/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/analysis/constant.d.ts +11 -0
- package/dist/analysis/constant.d.ts.map +1 -0
- package/dist/analysis/constant.js +261 -0
- package/dist/analysis/constant.js.map +1 -0
- package/dist/analysis/document-order.d.ts +5 -0
- package/dist/analysis/document-order.d.ts.map +1 -0
- package/dist/analysis/document-order.js +124 -0
- package/dist/analysis/document-order.js.map +1 -0
- package/dist/analysis/inlineability.d.ts +13 -0
- package/dist/analysis/inlineability.d.ts.map +1 -0
- package/dist/analysis/inlineability.js +270 -0
- package/dist/analysis/inlineability.js.map +1 -0
- package/dist/analysis/purity.d.ts +5 -0
- package/dist/analysis/purity.d.ts.map +1 -0
- package/dist/analysis/purity.js +281 -0
- package/dist/analysis/purity.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/obfuscator.d.ts +4 -0
- package/dist/obfuscator.d.ts.map +1 -0
- package/dist/obfuscator.js +222 -0
- package/dist/obfuscator.js.map +1 -0
- package/dist/options.d.ts +73 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +2 -0
- package/dist/options.js.map +1 -0
- package/dist/preparation/dot-to-bracket.d.ts +16 -0
- package/dist/preparation/dot-to-bracket.d.ts.map +1 -0
- package/dist/preparation/dot-to-bracket.js +36 -0
- package/dist/preparation/dot-to-bracket.js.map +1 -0
- package/dist/preparation/expand-destructuring.d.ts +26 -0
- package/dist/preparation/expand-destructuring.d.ts.map +1 -0
- package/dist/preparation/expand-destructuring.js +154 -0
- package/dist/preparation/expand-destructuring.js.map +1 -0
- package/dist/preparation/expand-template-literals.d.ts +23 -0
- package/dist/preparation/expand-template-literals.d.ts.map +1 -0
- package/dist/preparation/expand-template-literals.js +78 -0
- package/dist/preparation/expand-template-literals.js.map +1 -0
- package/dist/preparation/rewrite-class-fields.d.ts +43 -0
- package/dist/preparation/rewrite-class-fields.d.ts.map +1 -0
- package/dist/preparation/rewrite-class-fields.js +270 -0
- package/dist/preparation/rewrite-class-fields.js.map +1 -0
- package/dist/preparation/split-sequence-expressions.d.ts +15 -0
- package/dist/preparation/split-sequence-expressions.d.ts.map +1 -0
- package/dist/preparation/split-sequence-expressions.js +36 -0
- package/dist/preparation/split-sequence-expressions.js.map +1 -0
- package/dist/preparation/split-variable-declarations.d.ts +20 -0
- package/dist/preparation/split-variable-declarations.d.ts.map +1 -0
- package/dist/preparation/split-variable-declarations.js +36 -0
- package/dist/preparation/split-variable-declarations.js.map +1 -0
- package/dist/preparation/stringify-object-keys.d.ts +21 -0
- package/dist/preparation/stringify-object-keys.d.ts.map +1 -0
- package/dist/preparation/stringify-object-keys.js +96 -0
- package/dist/preparation/stringify-object-keys.js.map +1 -0
- package/dist/preparation/wrap-single-statements.d.ts +24 -0
- package/dist/preparation/wrap-single-statements.d.ts.map +1 -0
- package/dist/preparation/wrap-single-statements.js +53 -0
- package/dist/preparation/wrap-single-statements.js.map +1 -0
- package/dist/transforms/arguments-to-parameters.d.ts +20 -0
- package/dist/transforms/arguments-to-parameters.d.ts.map +1 -0
- package/dist/transforms/arguments-to-parameters.js +162 -0
- package/dist/transforms/arguments-to-parameters.js.map +1 -0
- package/dist/transforms/arrow-to-function.d.ts +46 -0
- package/dist/transforms/arrow-to-function.d.ts.map +1 -0
- package/dist/transforms/arrow-to-function.js +203 -0
- package/dist/transforms/arrow-to-function.js.map +1 -0
- package/dist/transforms/collapse-single-use-temps.d.ts +16 -0
- package/dist/transforms/collapse-single-use-temps.d.ts.map +1 -0
- package/dist/transforms/collapse-single-use-temps.js +805 -0
- package/dist/transforms/collapse-single-use-temps.js.map +1 -0
- package/dist/transforms/const-let-to-var.d.ts +21 -0
- package/dist/transforms/const-let-to-var.d.ts.map +1 -0
- package/dist/transforms/const-let-to-var.js +217 -0
- package/dist/transforms/const-let-to-var.js.map +1 -0
- package/dist/transforms/const-to-let.d.ts +17 -0
- package/dist/transforms/const-to-let.d.ts.map +1 -0
- package/dist/transforms/const-to-let.js +33 -0
- package/dist/transforms/const-to-let.js.map +1 -0
- package/dist/transforms/do-while-to-while.d.ts +26 -0
- package/dist/transforms/do-while-to-while.d.ts.map +1 -0
- package/dist/transforms/do-while-to-while.js +54 -0
- package/dist/transforms/do-while-to-while.js.map +1 -0
- package/dist/transforms/drop-console.d.ts +27 -0
- package/dist/transforms/drop-console.d.ts.map +1 -0
- package/dist/transforms/drop-console.js +107 -0
- package/dist/transforms/drop-console.js.map +1 -0
- package/dist/transforms/drop-debugger.d.ts +18 -0
- package/dist/transforms/drop-debugger.d.ts.map +1 -0
- package/dist/transforms/drop-debugger.js +34 -0
- package/dist/transforms/drop-debugger.js.map +1 -0
- package/dist/transforms/expand-binary-assignment.d.ts +26 -0
- package/dist/transforms/expand-binary-assignment.d.ts.map +1 -0
- package/dist/transforms/expand-binary-assignment.js +78 -0
- package/dist/transforms/expand-binary-assignment.js.map +1 -0
- package/dist/transforms/expand-logical-assignment.d.ts +22 -0
- package/dist/transforms/expand-logical-assignment.d.ts.map +1 -0
- package/dist/transforms/expand-logical-assignment.js +73 -0
- package/dist/transforms/expand-logical-assignment.js.map +1 -0
- package/dist/transforms/extract-object-properties.d.ts +21 -0
- package/dist/transforms/extract-object-properties.d.ts.map +1 -0
- package/dist/transforms/extract-object-properties.js +340 -0
- package/dist/transforms/extract-object-properties.js.map +1 -0
- package/dist/transforms/fold-builtin-methods.d.ts +23 -0
- package/dist/transforms/fold-builtin-methods.d.ts.map +1 -0
- package/dist/transforms/fold-builtin-methods.js +839 -0
- package/dist/transforms/fold-builtin-methods.js.map +1 -0
- package/dist/transforms/fold-constants.d.ts +16 -0
- package/dist/transforms/fold-constants.d.ts.map +1 -0
- package/dist/transforms/fold-constants.js +421 -0
- package/dist/transforms/fold-constants.js.map +1 -0
- package/dist/transforms/for-to-while.d.ts +29 -0
- package/dist/transforms/for-to-while.d.ts.map +1 -0
- package/dist/transforms/for-to-while.js +91 -0
- package/dist/transforms/for-to-while.js.map +1 -0
- package/dist/transforms/function-declaration-to-expression.d.ts +28 -0
- package/dist/transforms/function-declaration-to-expression.d.ts.map +1 -0
- package/dist/transforms/function-declaration-to-expression.js +95 -0
- package/dist/transforms/function-declaration-to-expression.js.map +1 -0
- package/dist/transforms/inline-functions.d.ts +16 -0
- package/dist/transforms/inline-functions.d.ts.map +1 -0
- package/dist/transforms/inline-functions.js +896 -0
- package/dist/transforms/inline-functions.js.map +1 -0
- package/dist/transforms/logical-to-ternary.d.ts +20 -0
- package/dist/transforms/logical-to-ternary.d.ts.map +1 -0
- package/dist/transforms/logical-to-ternary.js +78 -0
- package/dist/transforms/logical-to-ternary.js.map +1 -0
- package/dist/transforms/mangle-properties.d.ts +27 -0
- package/dist/transforms/mangle-properties.d.ts.map +1 -0
- package/dist/transforms/mangle-properties.js +703 -0
- package/dist/transforms/mangle-properties.js.map +1 -0
- package/dist/transforms/nullish-coalescing-to-ternary.d.ts +18 -0
- package/dist/transforms/nullish-coalescing-to-ternary.d.ts.map +1 -0
- package/dist/transforms/nullish-coalescing-to-ternary.js +61 -0
- package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -0
- package/dist/transforms/numbers-to-strings.d.ts +19 -0
- package/dist/transforms/numbers-to-strings.d.ts.map +1 -0
- package/dist/transforms/numbers-to-strings.js +51 -0
- package/dist/transforms/numbers-to-strings.js.map +1 -0
- package/dist/transforms/object-method-to-property.d.ts +34 -0
- package/dist/transforms/object-method-to-property.d.ts.map +1 -0
- package/dist/transforms/object-method-to-property.js +78 -0
- package/dist/transforms/object-method-to-property.js.map +1 -0
- package/dist/transforms/optional-chaining-to-ternary.d.ts +19 -0
- package/dist/transforms/optional-chaining-to-ternary.d.ts.map +1 -0
- package/dist/transforms/optional-chaining-to-ternary.js +206 -0
- package/dist/transforms/optional-chaining-to-ternary.js.map +1 -0
- package/dist/transforms/pack-declarations-into-parameters.d.ts +22 -0
- package/dist/transforms/pack-declarations-into-parameters.d.ts.map +1 -0
- package/dist/transforms/pack-declarations-into-parameters.js +208 -0
- package/dist/transforms/pack-declarations-into-parameters.js.map +1 -0
- package/dist/transforms/remove-anonymous-function-names.d.ts +17 -0
- package/dist/transforms/remove-anonymous-function-names.d.ts.map +1 -0
- package/dist/transforms/remove-anonymous-function-names.js +72 -0
- package/dist/transforms/remove-anonymous-function-names.js.map +1 -0
- package/dist/transforms/remove-unreachable-code.d.ts +21 -0
- package/dist/transforms/remove-unreachable-code.d.ts.map +1 -0
- package/dist/transforms/remove-unreachable-code.js +103 -0
- package/dist/transforms/remove-unreachable-code.js.map +1 -0
- package/dist/transforms/remove-unused-code.d.ts +18 -0
- package/dist/transforms/remove-unused-code.d.ts.map +1 -0
- package/dist/transforms/remove-unused-code.js +249 -0
- package/dist/transforms/remove-unused-code.js.map +1 -0
- package/dist/transforms/rename-identifiers.d.ts +24 -0
- package/dist/transforms/rename-identifiers.d.ts.map +1 -0
- package/dist/transforms/rename-identifiers.js +264 -0
- package/dist/transforms/rename-identifiers.js.map +1 -0
- package/dist/transforms/specials-to-strings.d.ts +30 -0
- package/dist/transforms/specials-to-strings.d.ts.map +1 -0
- package/dist/transforms/specials-to-strings.js +101 -0
- package/dist/transforms/specials-to-strings.js.map +1 -0
- package/dist/transforms/switch-to-if.d.ts +28 -0
- package/dist/transforms/switch-to-if.d.ts.map +1 -0
- package/dist/transforms/switch-to-if.js +279 -0
- package/dist/transforms/switch-to-if.js.map +1 -0
- package/dist/transforms/ternary-to-if.d.ts +20 -0
- package/dist/transforms/ternary-to-if.d.ts.map +1 -0
- package/dist/transforms/ternary-to-if.js +315 -0
- package/dist/transforms/ternary-to-if.js.map +1 -0
- package/dist/transforms/update-to-assignment.d.ts +26 -0
- package/dist/transforms/update-to-assignment.d.ts.map +1 -0
- package/dist/transforms/update-to-assignment.js +120 -0
- package/dist/transforms/update-to-assignment.js.map +1 -0
- package/dist/transforms/yodify-conditions.d.ts +22 -0
- package/dist/transforms/yodify-conditions.d.ts.map +1 -0
- package/dist/transforms/yodify-conditions.js +91 -0
- package/dist/transforms/yodify-conditions.js.map +1 -0
- package/dist/utils/ast.d.ts +10 -0
- package/dist/utils/ast.d.ts.map +1 -0
- package/dist/utils/ast.js +112 -0
- package/dist/utils/ast.js.map +1 -0
- package/dist/utils/change-tracking.d.ts +8 -0
- package/dist/utils/change-tracking.d.ts.map +1 -0
- package/dist/utils/change-tracking.js +8 -0
- package/dist/utils/change-tracking.js.map +1 -0
- package/dist/utils/dom-properties.d.ts +2 -0
- package/dist/utils/dom-properties.d.ts.map +1 -0
- package/dist/utils/dom-properties.js +9252 -0
- package/dist/utils/dom-properties.js.map +1 -0
- package/dist/utils/evaluation-order.d.ts +4 -0
- package/dist/utils/evaluation-order.d.ts.map +1 -0
- package/dist/utils/evaluation-order.js +152 -0
- package/dist/utils/evaluation-order.js.map +1 -0
- package/dist/utils/literal.d.ts +5 -0
- package/dist/utils/literal.d.ts.map +1 -0
- package/dist/utils/literal.js +90 -0
- package/dist/utils/literal.js.map +1 -0
- package/dist/utils/logical-lowering.d.ts +4 -0
- package/dist/utils/logical-lowering.d.ts.map +1 -0
- package/dist/utils/logical-lowering.js +104 -0
- package/dist/utils/logical-lowering.js.map +1 -0
- package/dist/utils/loop-lowering.d.ts +23 -0
- package/dist/utils/loop-lowering.d.ts.map +1 -0
- package/dist/utils/loop-lowering.js +88 -0
- package/dist/utils/loop-lowering.js.map +1 -0
- package/dist/utils/paths.d.ts +9 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +90 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/random.d.ts +3 -0
- package/dist/utils/random.d.ts.map +1 -0
- package/dist/utils/random.js +22 -0
- package/dist/utils/random.js.map +1 -0
- package/dist/utils/string-generator.d.ts +37 -0
- package/dist/utils/string-generator.d.ts.map +1 -0
- package/dist/utils/string-generator.js +175 -0
- package/dist/utils/string-generator.js.map +1 -0
- package/package.json +76 -0
- package/src/analysis/constant.ts +310 -0
- package/src/analysis/document-order.ts +141 -0
- package/src/analysis/inlineability.ts +336 -0
- package/src/analysis/purity.ts +315 -0
- package/src/index.ts +2 -0
- package/src/obfuscator.ts +271 -0
- package/src/options.ts +114 -0
- package/src/preparation/dot-to-bracket.ts +41 -0
- package/src/preparation/expand-destructuring.ts +211 -0
- package/src/preparation/expand-template-literals.ts +92 -0
- package/src/preparation/rewrite-class-fields.ts +320 -0
- package/src/preparation/split-sequence-expressions.ts +40 -0
- package/src/preparation/split-variable-declarations.ts +39 -0
- package/src/preparation/stringify-object-keys.ts +107 -0
- package/src/preparation/wrap-single-statements.ts +63 -0
- package/src/transforms/arguments-to-parameters.ts +187 -0
- package/src/transforms/arrow-to-function.ts +225 -0
- package/src/transforms/collapse-single-use-temps.ts +957 -0
- package/src/transforms/const-let-to-var.ts +246 -0
- package/src/transforms/const-to-let.ts +38 -0
- package/src/transforms/do-while-to-while.ts +69 -0
- package/src/transforms/drop-console.ts +119 -0
- package/src/transforms/drop-debugger.ts +38 -0
- package/src/transforms/expand-binary-assignment.ts +94 -0
- package/src/transforms/expand-logical-assignment.ts +84 -0
- package/src/transforms/extract-object-properties.ts +404 -0
- package/src/transforms/fold-builtin-methods.ts +949 -0
- package/src/transforms/fold-constants.ts +462 -0
- package/src/transforms/for-to-while.ts +109 -0
- package/src/transforms/function-declaration-to-expression.ts +108 -0
- package/src/transforms/inline-functions.ts +1074 -0
- package/src/transforms/logical-to-ternary.ts +92 -0
- package/src/transforms/mangle-properties.ts +819 -0
- package/src/transforms/nullish-coalescing-to-ternary.ts +77 -0
- package/src/transforms/numbers-to-strings.ts +61 -0
- package/src/transforms/object-method-to-property.ts +86 -0
- package/src/transforms/optional-chaining-to-ternary.ts +225 -0
- package/src/transforms/pack-declarations-into-parameters.ts +233 -0
- package/src/transforms/remove-anonymous-function-names.ts +84 -0
- package/src/transforms/remove-unreachable-code.ts +115 -0
- package/src/transforms/remove-unused-code.ts +296 -0
- package/src/transforms/rename-identifiers.ts +289 -0
- package/src/transforms/specials-to-strings.ts +116 -0
- package/src/transforms/switch-to-if.ts +339 -0
- package/src/transforms/ternary-to-if.ts +387 -0
- package/src/transforms/update-to-assignment.ts +132 -0
- package/src/transforms/yodify-conditions.ts +103 -0
- package/src/utils/ast.ts +129 -0
- package/src/utils/change-tracking.ts +15 -0
- package/src/utils/dom-properties.ts +9252 -0
- package/src/utils/evaluation-order.ts +178 -0
- package/src/utils/literal.ts +96 -0
- package/src/utils/logical-lowering.ts +132 -0
- package/src/utils/loop-lowering.ts +109 -0
- package/src/utils/paths.ts +103 -0
- package/src/utils/random.ts +22 -0
- 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
|
+
}
|