webfuscator 1.3.0 → 1.3.1
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/README.md +67 -0
- package/dist/analysis/constant.d.ts.map +1 -1
- package/dist/analysis/constant.js +10 -26
- package/dist/analysis/constant.js.map +1 -1
- package/dist/analysis/inlineability.d.ts.map +1 -1
- package/dist/analysis/inlineability.js +4 -12
- package/dist/analysis/inlineability.js.map +1 -1
- package/dist/analysis/purity.d.ts +3 -3
- package/dist/analysis/purity.d.ts.map +1 -1
- package/dist/analysis/purity.js +28 -75
- package/dist/analysis/purity.js.map +1 -1
- package/dist/preparation/expand-destructuring.d.ts +2 -3
- package/dist/preparation/expand-destructuring.d.ts.map +1 -1
- package/dist/preparation/expand-destructuring.js +2 -8
- package/dist/preparation/expand-destructuring.js.map +1 -1
- package/dist/transforms/arguments-to-parameters.d.ts +5 -4
- package/dist/transforms/arguments-to-parameters.d.ts.map +1 -1
- package/dist/transforms/arguments-to-parameters.js +5 -4
- package/dist/transforms/arguments-to-parameters.js.map +1 -1
- package/dist/transforms/arrow-to-function.d.ts +4 -3
- package/dist/transforms/arrow-to-function.d.ts.map +1 -1
- package/dist/transforms/arrow-to-function.js +6 -8
- package/dist/transforms/arrow-to-function.js.map +1 -1
- package/dist/transforms/collapse-single-use-temps.d.ts +2 -1
- package/dist/transforms/collapse-single-use-temps.d.ts.map +1 -1
- package/dist/transforms/collapse-single-use-temps.js +12 -20
- package/dist/transforms/collapse-single-use-temps.js.map +1 -1
- package/dist/transforms/conceal-strings.d.ts +1 -2
- package/dist/transforms/conceal-strings.d.ts.map +1 -1
- package/dist/transforms/conceal-strings.js +2 -7
- package/dist/transforms/conceal-strings.js.map +1 -1
- package/dist/transforms/const-let-to-var.d.ts +4 -3
- package/dist/transforms/const-let-to-var.d.ts.map +1 -1
- package/dist/transforms/const-let-to-var.js +8 -94
- package/dist/transforms/const-let-to-var.js.map +1 -1
- package/dist/transforms/const-to-let.d.ts +1 -2
- package/dist/transforms/const-to-let.d.ts.map +1 -1
- package/dist/transforms/const-to-let.js +1 -6
- package/dist/transforms/const-to-let.js.map +1 -1
- package/dist/transforms/drop-console.d.ts.map +1 -1
- package/dist/transforms/drop-console.js +3 -5
- package/dist/transforms/drop-console.js.map +1 -1
- package/dist/transforms/expand-binary-assignment.d.ts.map +1 -1
- package/dist/transforms/expand-binary-assignment.js +0 -5
- package/dist/transforms/expand-binary-assignment.js.map +1 -1
- package/dist/transforms/expand-logical-assignment.d.ts.map +1 -1
- package/dist/transforms/expand-logical-assignment.js +0 -5
- package/dist/transforms/expand-logical-assignment.js.map +1 -1
- package/dist/transforms/extract-object-properties.js +5 -12
- package/dist/transforms/extract-object-properties.js.map +1 -1
- package/dist/transforms/fold-builtin-methods.js +3 -11
- package/dist/transforms/fold-builtin-methods.js.map +1 -1
- package/dist/transforms/fold-constants.js +2 -2
- package/dist/transforms/fold-constants.js.map +1 -1
- package/dist/transforms/function-declaration-to-expression.d.ts +5 -6
- package/dist/transforms/function-declaration-to-expression.d.ts.map +1 -1
- package/dist/transforms/function-declaration-to-expression.js +5 -6
- package/dist/transforms/function-declaration-to-expression.js.map +1 -1
- package/dist/transforms/hoist-variables.d.ts +1 -2
- package/dist/transforms/hoist-variables.d.ts.map +1 -1
- package/dist/transforms/hoist-variables.js +2 -11
- package/dist/transforms/hoist-variables.js.map +1 -1
- package/dist/transforms/inline-functions.js +11 -39
- package/dist/transforms/inline-functions.js.map +1 -1
- package/dist/transforms/logical-to-ternary.d.ts +1 -1
- package/dist/transforms/logical-to-ternary.js +5 -9
- package/dist/transforms/logical-to-ternary.js.map +1 -1
- package/dist/transforms/mangle-properties.d.ts +2 -5
- package/dist/transforms/mangle-properties.d.ts.map +1 -1
- package/dist/transforms/mangle-properties.js +2 -5
- package/dist/transforms/mangle-properties.js.map +1 -1
- package/dist/transforms/nullish-coalescing-to-ternary.d.ts +4 -5
- package/dist/transforms/nullish-coalescing-to-ternary.d.ts.map +1 -1
- package/dist/transforms/nullish-coalescing-to-ternary.js +7 -12
- package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -1
- package/dist/transforms/object-method-to-property.d.ts +1 -1
- package/dist/transforms/object-method-to-property.js +1 -1
- package/dist/transforms/optional-chaining-to-ternary.d.ts +4 -3
- package/dist/transforms/optional-chaining-to-ternary.d.ts.map +1 -1
- package/dist/transforms/optional-chaining-to-ternary.js +9 -9
- package/dist/transforms/optional-chaining-to-ternary.js.map +1 -1
- package/dist/transforms/pack-declarations-into-parameters.d.ts.map +1 -1
- package/dist/transforms/pack-declarations-into-parameters.js +4 -16
- package/dist/transforms/pack-declarations-into-parameters.js.map +1 -1
- package/dist/transforms/pack.d.ts +4 -11
- package/dist/transforms/pack.d.ts.map +1 -1
- package/dist/transforms/pack.js +4 -17
- package/dist/transforms/pack.js.map +1 -1
- package/dist/transforms/remove-anonymous-function-names.d.ts +2 -4
- package/dist/transforms/remove-anonymous-function-names.d.ts.map +1 -1
- package/dist/transforms/remove-anonymous-function-names.js +2 -40
- package/dist/transforms/remove-anonymous-function-names.js.map +1 -1
- package/dist/transforms/remove-unreachable-code.js +2 -21
- package/dist/transforms/remove-unreachable-code.js.map +1 -1
- package/dist/transforms/remove-unused-code.d.ts.map +1 -1
- package/dist/transforms/remove-unused-code.js +5 -10
- package/dist/transforms/remove-unused-code.js.map +1 -1
- package/dist/transforms/rename-identifiers.d.ts +3 -3
- package/dist/transforms/rename-identifiers.d.ts.map +1 -1
- package/dist/transforms/rename-identifiers.js +4 -20
- package/dist/transforms/rename-identifiers.js.map +1 -1
- package/dist/transforms/reorder-function-declarations.d.ts +1 -2
- package/dist/transforms/reorder-function-declarations.d.ts.map +1 -1
- package/dist/transforms/reorder-function-declarations.js +2 -4
- package/dist/transforms/reorder-function-declarations.js.map +1 -1
- package/dist/transforms/specials-to-strings.d.ts.map +1 -1
- package/dist/transforms/specials-to-strings.js +2 -4
- package/dist/transforms/specials-to-strings.js.map +1 -1
- package/dist/transforms/switch-to-if.js +5 -15
- package/dist/transforms/switch-to-if.js.map +1 -1
- package/dist/transforms/ternary-to-if.d.ts.map +1 -1
- package/dist/transforms/ternary-to-if.js +5 -9
- package/dist/transforms/ternary-to-if.js.map +1 -1
- package/dist/transforms/update-to-assignment.d.ts.map +1 -1
- package/dist/transforms/update-to-assignment.js +6 -8
- package/dist/transforms/update-to-assignment.js.map +1 -1
- package/dist/transforms/yodify-conditions.d.ts +3 -4
- package/dist/transforms/yodify-conditions.d.ts.map +1 -1
- package/dist/transforms/yodify-conditions.js +3 -4
- package/dist/transforms/yodify-conditions.js.map +1 -1
- package/dist/utils/ast.d.ts +0 -1
- package/dist/utils/ast.d.ts.map +1 -1
- package/dist/utils/ast.js +0 -26
- package/dist/utils/ast.js.map +1 -1
- package/dist/utils/evaluation-order.js +1 -1
- package/dist/utils/evaluation-order.js.map +1 -1
- package/dist/utils/logical-lowering.d.ts.map +1 -1
- package/dist/utils/logical-lowering.js +7 -9
- package/dist/utils/logical-lowering.js.map +1 -1
- package/dist/utils/nullish.d.ts +4 -0
- package/dist/utils/nullish.d.ts.map +1 -0
- package/dist/utils/nullish.js +8 -0
- package/dist/utils/nullish.js.map +1 -0
- package/dist/utils/paths.d.ts +0 -2
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +0 -28
- package/dist/utils/paths.js.map +1 -1
- package/package.json +16 -10
- package/src/analysis/constant.ts +12 -30
- package/src/analysis/inlineability.ts +4 -12
- package/src/analysis/purity.ts +29 -92
- package/src/preparation/expand-destructuring.ts +2 -10
- package/src/transforms/arguments-to-parameters.ts +5 -4
- package/src/transforms/arrow-to-function.ts +6 -8
- package/src/transforms/collapse-single-use-temps.ts +12 -23
- package/src/transforms/conceal-strings.ts +3 -8
- package/src/transforms/const-let-to-var.ts +9 -100
- package/src/transforms/const-to-let.ts +1 -6
- package/src/transforms/drop-console.ts +3 -5
- package/src/transforms/expand-binary-assignment.ts +0 -5
- package/src/transforms/expand-logical-assignment.ts +0 -5
- package/src/transforms/extract-object-properties.ts +5 -14
- package/src/transforms/fold-builtin-methods.ts +3 -12
- package/src/transforms/fold-constants.ts +2 -2
- package/src/transforms/function-declaration-to-expression.ts +5 -6
- package/src/transforms/hoist-variables.ts +2 -12
- package/src/transforms/inline-functions.ts +16 -46
- package/src/transforms/logical-to-ternary.ts +5 -10
- package/src/transforms/mangle-properties.ts +2 -5
- package/src/transforms/nullish-coalescing-to-ternary.ts +7 -13
- package/src/transforms/object-method-to-property.ts +1 -1
- package/src/transforms/optional-chaining-to-ternary.ts +9 -9
- package/src/transforms/pack-declarations-into-parameters.ts +10 -16
- package/src/transforms/pack.ts +4 -17
- package/src/transforms/remove-anonymous-function-names.ts +3 -46
- package/src/transforms/remove-unreachable-code.ts +2 -22
- package/src/transforms/remove-unused-code.ts +5 -10
- package/src/transforms/rename-identifiers.ts +4 -20
- package/src/transforms/reorder-function-declarations.ts +2 -4
- package/src/transforms/specials-to-strings.ts +2 -4
- package/src/transforms/switch-to-if.ts +5 -22
- package/src/transforms/ternary-to-if.ts +5 -9
- package/src/transforms/update-to-assignment.ts +6 -8
- package/src/transforms/yodify-conditions.ts +3 -4
- package/src/utils/ast.ts +0 -29
- package/src/utils/evaluation-order.ts +1 -1
- package/src/utils/logical-lowering.ts +7 -11
- package/src/utils/nullish.ts +23 -0
- package/src/utils/paths.ts +0 -32
|
@@ -3,7 +3,7 @@ import type { Binding, NodePath, Visitor } from '@babel/traverse'
|
|
|
3
3
|
import * as t from '@babel/types'
|
|
4
4
|
import type { File } from '@babel/types'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { referencesOrWritesVariable } from '../utils/paths'
|
|
7
7
|
|
|
8
8
|
export interface InlineCandidate {
|
|
9
9
|
name: string
|
|
@@ -30,10 +30,6 @@ export function analyzeInlineability(ast: File): Map<string, InlineCandidate> {
|
|
|
30
30
|
if (!path.node.id) {
|
|
31
31
|
return
|
|
32
32
|
}
|
|
33
|
-
// Babel misses the sloppy Annex B `var` alias of a block-level function.
|
|
34
|
-
if (hasAnnexBFunctionAlias(path)) {
|
|
35
|
-
return
|
|
36
|
-
}
|
|
37
33
|
const binding = path.scope.getBinding(path.node.id.name)
|
|
38
34
|
if (!binding) {
|
|
39
35
|
return
|
|
@@ -131,9 +127,9 @@ function checkCandidate(
|
|
|
131
127
|
if (!t.isBlockStatement(fnNode.body)) {
|
|
132
128
|
return null
|
|
133
129
|
}
|
|
134
|
-
// Writes live in `constantViolations`, not
|
|
135
|
-
// make a call resolve to a different function.
|
|
136
|
-
if (binding.constantViolations.length > 0) {
|
|
130
|
+
// Writes to mutable bindings live in `constantViolations`, not
|
|
131
|
+
// `referencePaths`, and can make a call resolve to a different function.
|
|
132
|
+
if (binding.kind !== 'const' && binding.constantViolations.length > 0) {
|
|
137
133
|
return null
|
|
138
134
|
}
|
|
139
135
|
// Splicing loses a named function expression's inner binding.
|
|
@@ -273,10 +269,6 @@ const analyzeBodyVisitor: Visitor<AnalyzeBodyState> = {
|
|
|
273
269
|
path.stop()
|
|
274
270
|
}
|
|
275
271
|
},
|
|
276
|
-
WithStatement(path, state) {
|
|
277
|
-
state.unsafe = true
|
|
278
|
-
path.stop()
|
|
279
|
-
},
|
|
280
272
|
Identifier(path, state) {
|
|
281
273
|
const name = path.node.name
|
|
282
274
|
if (state.innerFnDepth === 0 && name === 'arguments') {
|
package/src/analysis/purity.ts
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Scope } from '@babel/traverse'
|
|
2
2
|
import * as t from '@babel/types'
|
|
3
3
|
|
|
4
|
-
import { isInsideWith } from '../utils/ast'
|
|
5
4
|
import { isPrimitiveLiteral } from '../utils/literal'
|
|
6
|
-
import {
|
|
7
|
-
declarationReaches,
|
|
8
|
-
initializerReaches,
|
|
9
|
-
readCrossesFunctionBoundary,
|
|
10
|
-
} from './document-order'
|
|
11
5
|
|
|
12
6
|
// Purity also requires deterministic repetition and no throw. Identifier reads
|
|
13
|
-
// need scope
|
|
14
|
-
|
|
15
|
-
export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): boolean {
|
|
7
|
+
// need scope to prove resolution.
|
|
8
|
+
export function isPure(node: t.Node, scope?: Scope): boolean {
|
|
16
9
|
if (
|
|
17
10
|
t.isNumericLiteral(node) ||
|
|
18
11
|
t.isStringLiteral(node) ||
|
|
@@ -25,7 +18,7 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
|
|
|
25
18
|
return true
|
|
26
19
|
}
|
|
27
20
|
if (t.isIdentifier(node)) {
|
|
28
|
-
return identifierReadIsSafe(node, scope
|
|
21
|
+
return identifierReadIsSafe(node, scope)
|
|
29
22
|
}
|
|
30
23
|
if (t.isTemplateLiteral(node)) {
|
|
31
24
|
// ToString can call object code or throw on Symbol.
|
|
@@ -39,7 +32,7 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
|
|
|
39
32
|
if (t.isUnaryExpression(node)) {
|
|
40
33
|
// Numeric unary operators invoke ToPrimitive. `delete` and `throw` act.
|
|
41
34
|
if (node.operator === '!' || node.operator === 'typeof' || node.operator === 'void') {
|
|
42
|
-
return isPure(node.argument, scope
|
|
35
|
+
return isPure(node.argument, scope)
|
|
43
36
|
}
|
|
44
37
|
return false
|
|
45
38
|
}
|
|
@@ -47,7 +40,7 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
|
|
|
47
40
|
if (!t.isExpression(node.left)) {
|
|
48
41
|
return false
|
|
49
42
|
}
|
|
50
|
-
if (!isPure(node.left, scope
|
|
43
|
+
if (!isPure(node.left, scope) || !isPure(node.right, scope)) {
|
|
51
44
|
return false
|
|
52
45
|
}
|
|
53
46
|
// Only strict equality avoids coercion. Other operators need safe literals.
|
|
@@ -57,18 +50,16 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
|
|
|
57
50
|
return coercesWithoutEffect(node.left, node.right, node.operator)
|
|
58
51
|
}
|
|
59
52
|
if (t.isLogicalExpression(node)) {
|
|
60
|
-
return isPure(node.left, scope
|
|
53
|
+
return isPure(node.left, scope) && isPure(node.right, scope)
|
|
61
54
|
}
|
|
62
55
|
if (t.isConditionalExpression(node)) {
|
|
63
56
|
return (
|
|
64
|
-
isPure(node.test, scope,
|
|
65
|
-
isPure(node.consequent, scope, referencePath) &&
|
|
66
|
-
isPure(node.alternate, scope, referencePath)
|
|
57
|
+
isPure(node.test, scope) && isPure(node.consequent, scope) && isPure(node.alternate, scope)
|
|
67
58
|
)
|
|
68
59
|
}
|
|
69
60
|
if (t.isSequenceExpression(node)) {
|
|
70
61
|
for (const expr of node.expressions) {
|
|
71
|
-
if (!isPure(expr, scope
|
|
62
|
+
if (!isPure(expr, scope)) {
|
|
72
63
|
return false
|
|
73
64
|
}
|
|
74
65
|
}
|
|
@@ -77,8 +68,9 @@ export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): b
|
|
|
77
68
|
return false
|
|
78
69
|
}
|
|
79
70
|
|
|
80
|
-
// A droppable identifier read must resolve
|
|
81
|
-
|
|
71
|
+
// A droppable identifier read must resolve to a local binding or immutable
|
|
72
|
+
// global `undefined`.
|
|
73
|
+
export function isSideEffectFree(node: t.Node, scope?: Scope): boolean {
|
|
82
74
|
if (
|
|
83
75
|
t.isNumericLiteral(node) ||
|
|
84
76
|
t.isStringLiteral(node) ||
|
|
@@ -99,7 +91,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
99
91
|
return true
|
|
100
92
|
}
|
|
101
93
|
if (t.isIdentifier(node)) {
|
|
102
|
-
return identifierReadIsSafe(node, scope
|
|
94
|
+
return identifierReadIsSafe(node, scope)
|
|
103
95
|
}
|
|
104
96
|
if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
|
|
105
97
|
return true
|
|
@@ -107,7 +99,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
107
99
|
if (t.isClassExpression(node)) {
|
|
108
100
|
// `extends` and computed keys run during class definition.
|
|
109
101
|
if (node.superClass) {
|
|
110
|
-
if (!isSideEffectFree(node.superClass, scope
|
|
102
|
+
if (!isSideEffectFree(node.superClass, scope)) {
|
|
111
103
|
return false
|
|
112
104
|
}
|
|
113
105
|
// ClassDefinitionEvaluation accepts only `null` or a constructor.
|
|
@@ -127,7 +119,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
127
119
|
'computed' in member &&
|
|
128
120
|
member.computed &&
|
|
129
121
|
'key' in member &&
|
|
130
|
-
!isSideEffectFree(member.key, scope
|
|
122
|
+
!isSideEffectFree(member.key, scope)
|
|
131
123
|
) {
|
|
132
124
|
return false
|
|
133
125
|
}
|
|
@@ -142,7 +134,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
142
134
|
if (t.isSpreadElement(element)) {
|
|
143
135
|
return false
|
|
144
136
|
}
|
|
145
|
-
if (!isSideEffectFree(element, scope
|
|
137
|
+
if (!isSideEffectFree(element, scope)) {
|
|
146
138
|
return false
|
|
147
139
|
}
|
|
148
140
|
}
|
|
@@ -154,7 +146,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
154
146
|
return false
|
|
155
147
|
}
|
|
156
148
|
if (t.isObjectMethod(prop)) {
|
|
157
|
-
if (prop.computed && !isSideEffectFree(prop.key, scope
|
|
149
|
+
if (prop.computed && !isSideEffectFree(prop.key, scope)) {
|
|
158
150
|
return false
|
|
159
151
|
}
|
|
160
152
|
continue
|
|
@@ -162,13 +154,13 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
162
154
|
if (!t.isObjectProperty(prop)) {
|
|
163
155
|
return false
|
|
164
156
|
}
|
|
165
|
-
if (prop.computed && !isSideEffectFree(prop.key, scope
|
|
157
|
+
if (prop.computed && !isSideEffectFree(prop.key, scope)) {
|
|
166
158
|
return false
|
|
167
159
|
}
|
|
168
160
|
if (!t.isExpression(prop.value)) {
|
|
169
161
|
return false
|
|
170
162
|
}
|
|
171
|
-
if (!isSideEffectFree(prop.value, scope
|
|
163
|
+
if (!isSideEffectFree(prop.value, scope)) {
|
|
172
164
|
return false
|
|
173
165
|
}
|
|
174
166
|
}
|
|
@@ -179,7 +171,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
179
171
|
return false
|
|
180
172
|
}
|
|
181
173
|
if (node.operator === '!' || node.operator === 'typeof' || node.operator === 'void') {
|
|
182
|
-
return isSideEffectFree(node.argument, scope
|
|
174
|
+
return isSideEffectFree(node.argument, scope)
|
|
183
175
|
}
|
|
184
176
|
return false
|
|
185
177
|
}
|
|
@@ -187,10 +179,7 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
187
179
|
if (!t.isExpression(node.left)) {
|
|
188
180
|
return false
|
|
189
181
|
}
|
|
190
|
-
if (
|
|
191
|
-
!isSideEffectFree(node.left, scope, referencePath) ||
|
|
192
|
-
!isSideEffectFree(node.right, scope, referencePath)
|
|
193
|
-
) {
|
|
182
|
+
if (!isSideEffectFree(node.left, scope) || !isSideEffectFree(node.right, scope)) {
|
|
194
183
|
return false
|
|
195
184
|
}
|
|
196
185
|
// Coercing operators need compatible literals that cannot throw.
|
|
@@ -200,21 +189,18 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
200
189
|
return coercesWithoutEffect(node.left, node.right, node.operator)
|
|
201
190
|
}
|
|
202
191
|
if (t.isLogicalExpression(node)) {
|
|
203
|
-
return (
|
|
204
|
-
isSideEffectFree(node.left, scope, referencePath) &&
|
|
205
|
-
isSideEffectFree(node.right, scope, referencePath)
|
|
206
|
-
)
|
|
192
|
+
return isSideEffectFree(node.left, scope) && isSideEffectFree(node.right, scope)
|
|
207
193
|
}
|
|
208
194
|
if (t.isConditionalExpression(node)) {
|
|
209
195
|
return (
|
|
210
|
-
isSideEffectFree(node.test, scope
|
|
211
|
-
isSideEffectFree(node.consequent, scope
|
|
212
|
-
isSideEffectFree(node.alternate, scope
|
|
196
|
+
isSideEffectFree(node.test, scope) &&
|
|
197
|
+
isSideEffectFree(node.consequent, scope) &&
|
|
198
|
+
isSideEffectFree(node.alternate, scope)
|
|
213
199
|
)
|
|
214
200
|
}
|
|
215
201
|
if (t.isSequenceExpression(node)) {
|
|
216
202
|
for (const expr of node.expressions) {
|
|
217
|
-
if (!isSideEffectFree(expr, scope
|
|
203
|
+
if (!isSideEffectFree(expr, scope)) {
|
|
218
204
|
return false
|
|
219
205
|
}
|
|
220
206
|
}
|
|
@@ -223,43 +209,13 @@ export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: No
|
|
|
223
209
|
return false
|
|
224
210
|
}
|
|
225
211
|
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
function identifierReadIsSafe(
|
|
229
|
-
node: t.Identifier,
|
|
230
|
-
scope: Scope | undefined,
|
|
231
|
-
referencePath: NodePath | undefined,
|
|
232
|
-
): boolean {
|
|
233
|
-
// A `with` property can shadow any name, including `undefined`.
|
|
234
|
-
if (referencePath && isInsideWith(referencePath)) {
|
|
235
|
-
return false
|
|
236
|
-
}
|
|
212
|
+
// Unbound `undefined` is safe because the global property is immutable.
|
|
213
|
+
function identifierReadIsSafe(node: t.Identifier, scope: Scope | undefined): boolean {
|
|
237
214
|
const isUndefinedName = node.name === 'undefined'
|
|
238
215
|
if (!scope) {
|
|
239
216
|
return isUndefinedName
|
|
240
217
|
}
|
|
241
|
-
|
|
242
|
-
if (!binding) {
|
|
243
|
-
return isUndefinedName
|
|
244
|
-
}
|
|
245
|
-
if (t.isFunctionDeclaration(binding.path.node) || binding.kind === 'module') {
|
|
246
|
-
return true
|
|
247
|
-
}
|
|
248
|
-
if (binding.kind === 'param') {
|
|
249
|
-
// Earlier parameter defaults can read later parameters in TDZ.
|
|
250
|
-
if (!referencePath || !readIsInOwnParamList(binding, referencePath)) {
|
|
251
|
-
return true
|
|
252
|
-
}
|
|
253
|
-
return declarationReaches(binding.path, referencePath)
|
|
254
|
-
}
|
|
255
|
-
// Lexical reads need an unconditionally reached declaration in the same call.
|
|
256
|
-
if (isLexicalBinding(binding)) {
|
|
257
|
-
if (referencePath && readCrossesFunctionBoundary(binding, referencePath)) {
|
|
258
|
-
return false
|
|
259
|
-
}
|
|
260
|
-
return initializerReaches(binding.path, referencePath)
|
|
261
|
-
}
|
|
262
|
-
return declarationReaches(binding.path, referencePath)
|
|
218
|
+
return scope.getBinding(node.name) !== undefined || isUndefinedName
|
|
263
219
|
}
|
|
264
220
|
|
|
265
221
|
// Only `null` and plain function expressions are known-valid superclass values.
|
|
@@ -294,22 +250,3 @@ function coercesWithoutEffect(left: t.Node, right: t.Node, operator: string): bo
|
|
|
294
250
|
}
|
|
295
251
|
return leftKind !== 'bigint' || !THROWING_BIGINT_OPERATORS.has(operator)
|
|
296
252
|
}
|
|
297
|
-
|
|
298
|
-
function isLexicalBinding(binding: Binding): boolean {
|
|
299
|
-
return (
|
|
300
|
-
binding.kind === 'let' || binding.kind === 'const' || t.isClassDeclaration(binding.path.node)
|
|
301
|
-
)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function readIsInOwnParamList(binding: Binding, referencePath: NodePath): boolean {
|
|
305
|
-
const owner = binding.scope.block
|
|
306
|
-
let current: NodePath | null = referencePath
|
|
307
|
-
while (current) {
|
|
308
|
-
const parent: NodePath | null = current.parentPath
|
|
309
|
-
if (parent && parent.node === owner && current.listKey === 'params') {
|
|
310
|
-
return true
|
|
311
|
-
}
|
|
312
|
-
current = parent
|
|
313
|
-
}
|
|
314
|
-
return false
|
|
315
|
-
}
|
|
@@ -3,8 +3,6 @@ import type { NodePath, Scope } from '@babel/traverse'
|
|
|
3
3
|
import * as t from '@babel/types'
|
|
4
4
|
import type { File } from '@babel/types'
|
|
5
5
|
|
|
6
|
-
import { isInsideWith } from '../utils/ast'
|
|
7
|
-
|
|
8
6
|
type Kind = t.VariableDeclaration['kind']
|
|
9
7
|
|
|
10
8
|
// Babel 8 omits `AssignmentPattern` from `LVal`, so list these targets directly.
|
|
@@ -18,9 +16,8 @@ type BindingTarget = t.Identifier | t.ObjectPattern | t.ArrayPattern | t.Assignm
|
|
|
18
16
|
* The source, computed keys, and getters keep their original evaluation counts.
|
|
19
17
|
* Defaults compare against `void 0` because `undefined` can be shadowed.
|
|
20
18
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* keys to preserve ObjectBindingPattern evaluation order.
|
|
19
|
+
* The `RequireObjectCoercible` guard runs before computed keys to preserve
|
|
20
|
+
* ObjectBindingPattern evaluation order.
|
|
24
21
|
*
|
|
25
22
|
* @example
|
|
26
23
|
* // ◀️ before
|
|
@@ -42,11 +39,6 @@ const visitor = {
|
|
|
42
39
|
if (typeof path.listKey !== 'string') {
|
|
43
40
|
return
|
|
44
41
|
}
|
|
45
|
-
// A `with` property can capture the generated temporary.
|
|
46
|
-
if (isInsideWith(path)) {
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
|
|
50
42
|
const kind = path.node.kind
|
|
51
43
|
const replacement: t.Statement[] = []
|
|
52
44
|
let changed = false
|
|
@@ -8,10 +8,11 @@ import type { ChangeState } from '../utils/change-tracking'
|
|
|
8
8
|
import { isInStrictContext } from '../utils/paths'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Replaces `arguments[i]` reads with
|
|
12
|
-
* with simple parameter lists.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Replaces numeric `arguments[i]` reads with the corresponding parameter in
|
|
12
|
+
* sloppy functions with simple parameter lists. It leaves a read unchanged when
|
|
13
|
+
* that index is written or its parameter is reassigned. Reassigning, deleting,
|
|
14
|
+
* or passing around `arguments` disables the rewrite. Missing parameters are
|
|
15
|
+
* never added because that would change `fn.length`.
|
|
15
16
|
*
|
|
16
17
|
* @example
|
|
17
18
|
* // ◀️ before
|
|
@@ -2,14 +2,14 @@ import type { NodePath, Visitor } from '@babel/traverse'
|
|
|
2
2
|
import * as t from '@babel/types'
|
|
3
3
|
import type { File } from '@babel/types'
|
|
4
4
|
|
|
5
|
-
import { isInsideWith } from '../utils/ast'
|
|
6
5
|
import { traverseForChanges } from '../utils/change-tracking'
|
|
7
6
|
import type { ChangeState } from '../utils/change-tracking'
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
11
|
-
* that
|
|
12
|
-
*
|
|
9
|
+
* Converts arrows with Babel's capture-aware `arrowFunctionToExpression`.
|
|
10
|
+
* Arrows that use `super` stay unchanged. An arrow also stays unchanged unless
|
|
11
|
+
* every use is an immediate call or a discarded expression, because a normal
|
|
12
|
+
* function can be constructed with `new` and has a `prototype`.
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* // ◀️ before
|
|
@@ -64,9 +64,7 @@ const visitor: Visitor<ChangeState> = {
|
|
|
64
64
|
!valueStaysInView(path) ||
|
|
65
65
|
arrowReferencesSuper(path) ||
|
|
66
66
|
inUnsafeCaptureContext(path) ||
|
|
67
|
-
usesReassignedArguments(path)
|
|
68
|
-
// `with` can capture Babel's `this` or `arguments` alias.
|
|
69
|
-
isInsideWith(path)
|
|
67
|
+
usesReassignedArguments(path)
|
|
70
68
|
) {
|
|
71
69
|
return
|
|
72
70
|
}
|
|
@@ -140,7 +138,7 @@ function valueStaysInView(valuePath: NodePath): boolean {
|
|
|
140
138
|
return false
|
|
141
139
|
}
|
|
142
140
|
const binding = valuePath.scope.getBinding(parent.node.id.name)
|
|
143
|
-
if (!binding || !binding.constant) {
|
|
141
|
+
if (!binding || (binding.kind !== 'const' && !binding.constant)) {
|
|
144
142
|
return false
|
|
145
143
|
}
|
|
146
144
|
return binding.referencePaths.every(
|
|
@@ -10,14 +10,14 @@ import {
|
|
|
10
10
|
readCrossesFunctionBoundary,
|
|
11
11
|
} from '../analysis/document-order'
|
|
12
12
|
import { isPure } from '../analysis/purity'
|
|
13
|
-
import { isInsideWith } from '../utils/ast'
|
|
14
13
|
import { reorderableToStatement } from '../utils/evaluation-order'
|
|
15
14
|
import { valueToLiteral } from '../utils/literal'
|
|
16
15
|
import { isCalleeOrTagOf } from '../utils/paths'
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Drops single-use temporaries through substitution, direct branch writes, or
|
|
20
|
-
* constant replacement.
|
|
19
|
+
* constant replacement. It repeats these rewrites so one removal can expose
|
|
20
|
+
* another.
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* // ◀️ before
|
|
@@ -94,21 +94,9 @@ function resolveSingleUseBinding(path: NodePath<t.VariableDeclarator>): Binding
|
|
|
94
94
|
if (binding.references === 0) {
|
|
95
95
|
return null
|
|
96
96
|
}
|
|
97
|
-
// `with` can redirect the temporary or its consumer outside Babel's scope table.
|
|
98
|
-
if (bindingTouchesWith(binding)) {
|
|
99
|
-
return null
|
|
100
|
-
}
|
|
101
97
|
return binding
|
|
102
98
|
}
|
|
103
99
|
|
|
104
|
-
function bindingTouchesWith(binding: Binding): boolean {
|
|
105
|
-
return (
|
|
106
|
-
isInsideWith(binding.path) ||
|
|
107
|
-
binding.referencePaths.some((ref) => isInsideWith(ref)) ||
|
|
108
|
-
binding.constantViolations.some((violation) => isInsideWith(violation))
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
100
|
function findProgramScope(ast: File): Scope | undefined {
|
|
113
101
|
let result: Scope | undefined
|
|
114
102
|
traverse(ast, {
|
|
@@ -145,7 +133,7 @@ function trySubstituteSingleUse(
|
|
|
145
133
|
let removeWriteStmt: boolean
|
|
146
134
|
|
|
147
135
|
if (init) {
|
|
148
|
-
if (binding.constantViolations.length > 0) {
|
|
136
|
+
if (binding.kind !== 'const' && binding.constantViolations.length > 0) {
|
|
149
137
|
return false
|
|
150
138
|
}
|
|
151
139
|
writeStmt = declStmt
|
|
@@ -333,8 +321,9 @@ function tryRenameWritesToReader(
|
|
|
333
321
|
return false
|
|
334
322
|
}
|
|
335
323
|
|
|
336
|
-
//
|
|
337
|
-
|
|
324
|
+
// A later `var` initializer can overwrite a redirected write. Writes before
|
|
325
|
+
// lexical initialization are intentionally ignored.
|
|
326
|
+
if (yBinding.kind === 'var' && !declarationReaches(yBinding.path, violation)) {
|
|
338
327
|
return false
|
|
339
328
|
}
|
|
340
329
|
}
|
|
@@ -662,7 +651,7 @@ function spanStatementIsInert(path: NodePath, tempBinding: Binding): boolean {
|
|
|
662
651
|
}
|
|
663
652
|
if (t.isIfStatement(node)) {
|
|
664
653
|
const test = path.get('test') as NodePath
|
|
665
|
-
if (!isPure(test.node, test.scope
|
|
654
|
+
if (!isPure(test.node, test.scope)) {
|
|
666
655
|
return false
|
|
667
656
|
}
|
|
668
657
|
if (!spanStatementIsInert(path.get('consequent') as NodePath, tempBinding)) {
|
|
@@ -675,9 +664,9 @@ function spanStatementIsInert(path: NodePath, tempBinding: Binding): boolean {
|
|
|
675
664
|
const expr = path.get('expression') as NodePath
|
|
676
665
|
if (isTempWrite(expr.node, tempBinding)) {
|
|
677
666
|
const rhs = expr.get('right') as NodePath
|
|
678
|
-
return isPure(rhs.node, rhs.scope
|
|
667
|
+
return isPure(rhs.node, rhs.scope)
|
|
679
668
|
}
|
|
680
|
-
return isPure(expr.node, expr.scope
|
|
669
|
+
return isPure(expr.node, expr.scope)
|
|
681
670
|
}
|
|
682
671
|
return false
|
|
683
672
|
}
|
|
@@ -723,7 +712,7 @@ function tryPropagateConstant(
|
|
|
723
712
|
let writeSourcePath: NodePath
|
|
724
713
|
|
|
725
714
|
if (init) {
|
|
726
|
-
if (binding.constantViolations.length > 0) {
|
|
715
|
+
if (binding.kind !== 'const' && binding.constantViolations.length > 0) {
|
|
727
716
|
return false
|
|
728
717
|
}
|
|
729
718
|
if (!isUnconditionallyExecuted(declStmt, declaratorPath.scope)) {
|
|
@@ -890,10 +879,10 @@ function isStatementMovementSafe(path: NodePath): boolean {
|
|
|
890
879
|
if (t.isEmptyStatement(node)) {
|
|
891
880
|
return true
|
|
892
881
|
}
|
|
893
|
-
//
|
|
882
|
+
// Only inert expression statements are safe to cross.
|
|
894
883
|
if (t.isExpressionStatement(node)) {
|
|
895
884
|
const expr = path.get('expression') as NodePath
|
|
896
|
-
return isPure(expr.node, expr.scope
|
|
885
|
+
return isPure(expr.node, expr.scope)
|
|
897
886
|
}
|
|
898
887
|
return false
|
|
899
888
|
}
|
|
@@ -20,8 +20,7 @@ const RECORD_WIDTH = 4
|
|
|
20
20
|
* `scope: 'program'` creates one vault. `scope: 'function'` creates a vault per
|
|
21
21
|
* function but repeats the program-wide pool in each one. Parameter defaults and
|
|
22
22
|
* computed keys use the enclosing vault because they run before the function
|
|
23
|
-
* body.
|
|
24
|
-
* decoder references.
|
|
23
|
+
* body.
|
|
25
24
|
*
|
|
26
25
|
* Directives, module specifiers, import attributes, and non-computed keys are not
|
|
27
26
|
* runtime expressions, so they stay unchanged.
|
|
@@ -91,7 +90,7 @@ interface ResolvedOptions {
|
|
|
91
90
|
scope: 'function' | 'program'
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
type VaultOwner = t.Program | t.Function
|
|
93
|
+
type VaultOwner = t.Program | t.Function
|
|
95
94
|
type VaultOwnerPath = NodePath<VaultOwner>
|
|
96
95
|
|
|
97
96
|
interface Occurrence {
|
|
@@ -208,10 +207,6 @@ function findVaultOwner(
|
|
|
208
207
|
if (current.isFunction() && child.key === 'body' && scope === 'function') {
|
|
209
208
|
return current as VaultOwnerPath
|
|
210
209
|
}
|
|
211
|
-
if (current.isWithStatement() && child.key === 'body') {
|
|
212
|
-
const body = current.get('body')
|
|
213
|
-
return body.isBlockStatement() ? (body as VaultOwnerPath) : null
|
|
214
|
-
}
|
|
215
210
|
if (current.isProgram()) {
|
|
216
211
|
return current
|
|
217
212
|
}
|
|
@@ -375,7 +370,7 @@ function maskPosition(position: number, key: number, step: number, indexMask: nu
|
|
|
375
370
|
}
|
|
376
371
|
|
|
377
372
|
function statementBody(owner: VaultOwnerPath): NodePath<t.Program | t.BlockStatement> {
|
|
378
|
-
if (owner.isProgram()
|
|
373
|
+
if (owner.isProgram()) {
|
|
379
374
|
return owner
|
|
380
375
|
}
|
|
381
376
|
const body = owner.get('body') as NodePath
|