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,14 +3,11 @@ import type { NodePath, Scope, Visitor } from '@babel/traverse'
|
|
|
3
3
|
import * as t from '@babel/types'
|
|
4
4
|
import type { File } from '@babel/types'
|
|
5
5
|
|
|
6
|
-
import { initializerReaches } from '../analysis/document-order'
|
|
7
|
-
import { isInsideWith } from '../utils/ast'
|
|
8
|
-
import { hasConstantViolation } from '../utils/paths'
|
|
9
|
-
|
|
10
6
|
/**
|
|
11
|
-
* Converts
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* Converts `let` and `const` declarations to `var` and renames bindings that
|
|
8
|
+
* would conflict after hoisting. It leaves loop declarations unchanged when
|
|
9
|
+
* `var` would lose a captured per-iteration binding or reuse the previous
|
|
10
|
+
* iteration's value.
|
|
14
11
|
*
|
|
15
12
|
* @example
|
|
16
13
|
* // ◀️ before
|
|
@@ -33,36 +30,12 @@ export function constLetToVar(ast: File): boolean {
|
|
|
33
30
|
if (kind !== 'let' && kind !== 'const') {
|
|
34
31
|
return
|
|
35
32
|
}
|
|
36
|
-
// Babel's scope table cannot account for properties supplied by `with`.
|
|
37
|
-
if (isInsideWith(path)) {
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
// A `var` write would lose the `const` TypeError.
|
|
41
|
-
if (kind === 'const' && hasConstantViolation(path)) {
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
// Hoisted `var` reads `undefined` where a lexical binding throws in TDZ.
|
|
45
|
-
if (hasTdzRiskyReference(path)) {
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
33
|
// An uninitialized lexical loop head resets on each entry. `var` does not.
|
|
49
34
|
if (forHeadMissingInitializer(path)) {
|
|
50
35
|
return
|
|
51
36
|
}
|
|
52
37
|
const blockScope = path.scope
|
|
53
38
|
const varScope = blockScope.getFunctionParent() ?? blockScope.getProgramParent()
|
|
54
|
-
if (varScope.path.isProgram()) {
|
|
55
|
-
// Script `var` creates a global-object property; module `var` does not.
|
|
56
|
-
if (varScope.path.node.sourceType !== 'module') {
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
// A circular importer can read this binding before initialization. A
|
|
60
|
-
// lexical binding throws in TDZ then, while `var` reads `undefined`. That
|
|
61
|
-
// external read does not appear in this file's references.
|
|
62
|
-
if (bindsExportedName(path)) {
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
39
|
// One `var` cannot preserve per-iteration bindings captured by closures.
|
|
67
40
|
if (capturedInEnclosingLoop(path)) {
|
|
68
41
|
return
|
|
@@ -76,10 +49,6 @@ export function constLetToVar(ast: File): boolean {
|
|
|
76
49
|
if (!id) {
|
|
77
50
|
return
|
|
78
51
|
}
|
|
79
|
-
if (isInsideWith(path)) {
|
|
80
|
-
return
|
|
81
|
-
}
|
|
82
|
-
|
|
83
52
|
const scope = path.parentPath.scope
|
|
84
53
|
if (!isVarScope(scope) && scope.parent!.hasBinding(id.name, { noUids: true })) {
|
|
85
54
|
path.scope.rename(id.name)
|
|
@@ -107,35 +76,6 @@ const conflictingFunctionsVisitor: Visitor<{ names: string[] }> = {
|
|
|
107
76
|
},
|
|
108
77
|
}
|
|
109
78
|
|
|
110
|
-
// Same-scope uses need a reachable initializer. Nested uses also need their
|
|
111
|
-
// function value created after initialization.
|
|
112
|
-
function hasTdzRiskyReference(path: NodePath<t.VariableDeclaration>): boolean {
|
|
113
|
-
const declHome = nearestFunctionOrProgram(path)
|
|
114
|
-
for (const name of Object.keys(path.getBindingIdentifiers())) {
|
|
115
|
-
const binding = path.scope.getBinding(name)
|
|
116
|
-
if (!binding) {
|
|
117
|
-
continue
|
|
118
|
-
}
|
|
119
|
-
for (const usage of [...binding.referencePaths, ...binding.constantViolations]) {
|
|
120
|
-
if (usage.findParent((ancestor) => ancestor === path)) {
|
|
121
|
-
return true
|
|
122
|
-
}
|
|
123
|
-
const usageHome = nearestFunctionOrProgram(usage)
|
|
124
|
-
if (usageHome === declHome) {
|
|
125
|
-
if (!initializerReaches(path, usage)) {
|
|
126
|
-
return true
|
|
127
|
-
}
|
|
128
|
-
continue
|
|
129
|
-
}
|
|
130
|
-
const nested = outermostNestedFunction(usage, declHome)
|
|
131
|
-
if (!nested || nested.isFunctionDeclaration() || !initializerReaches(path, nested)) {
|
|
132
|
-
return true
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return false
|
|
137
|
-
}
|
|
138
|
-
|
|
139
79
|
function forHeadMissingInitializer(path: NodePath<t.VariableDeclaration>): boolean {
|
|
140
80
|
if (!t.isForStatement(path.parent) || path.key !== 'init') {
|
|
141
81
|
return false
|
|
@@ -143,26 +83,6 @@ function forHeadMissingInitializer(path: NodePath<t.VariableDeclaration>): boole
|
|
|
143
83
|
return path.node.declarations.some((decl) => decl.init == null)
|
|
144
84
|
}
|
|
145
85
|
|
|
146
|
-
// A source clause re-exports another module's binding, not this local binding.
|
|
147
|
-
function bindsExportedName(path: NodePath<t.VariableDeclaration>): boolean {
|
|
148
|
-
if (path.parentPath.isExportNamedDeclaration()) {
|
|
149
|
-
return true
|
|
150
|
-
}
|
|
151
|
-
const names = new Set(Object.keys(path.getBindingIdentifiers()))
|
|
152
|
-
const program = path.scope.getProgramParent().path.node as t.Program
|
|
153
|
-
for (const statement of program.body) {
|
|
154
|
-
if (!t.isExportNamedDeclaration(statement) || statement.source) {
|
|
155
|
-
continue
|
|
156
|
-
}
|
|
157
|
-
for (const specifier of statement.specifiers) {
|
|
158
|
-
if (t.isExportSpecifier(specifier) && names.has(specifier.local.name)) {
|
|
159
|
-
return true
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return false
|
|
164
|
-
}
|
|
165
|
-
|
|
166
86
|
// A nested function or class can observe the loop's per-iteration binding.
|
|
167
87
|
function capturedInEnclosingLoop(path: NodePath<t.VariableDeclaration>): boolean {
|
|
168
88
|
const loop = enclosingLoop(path)
|
|
@@ -174,7 +94,11 @@ function capturedInEnclosingLoop(path: NodePath<t.VariableDeclaration>): boolean
|
|
|
174
94
|
if (!binding) {
|
|
175
95
|
continue
|
|
176
96
|
}
|
|
177
|
-
|
|
97
|
+
const usages =
|
|
98
|
+
binding.kind === 'const'
|
|
99
|
+
? binding.referencePaths
|
|
100
|
+
: [...binding.referencePaths, ...binding.constantViolations]
|
|
101
|
+
for (const usage of usages) {
|
|
178
102
|
for (
|
|
179
103
|
let current = usage.parentPath;
|
|
180
104
|
current && current !== loop;
|
|
@@ -232,21 +156,6 @@ function transformBlockScopedVariable(
|
|
|
232
156
|
})
|
|
233
157
|
}
|
|
234
158
|
|
|
235
|
-
function nearestFunctionOrProgram(path: NodePath): NodePath {
|
|
236
|
-
return path.findParent((ancestor) => ancestor.isFunction() || ancestor.isProgram())!
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// The shallowest nested function decides when this use can run.
|
|
240
|
-
function outermostNestedFunction(usage: NodePath, home: NodePath): NodePath | null {
|
|
241
|
-
let result: NodePath | null = null
|
|
242
|
-
for (let current = usage.parentPath; current && current !== home; current = current.parentPath) {
|
|
243
|
-
if (current.isFunction()) {
|
|
244
|
-
result = current
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
return result
|
|
248
|
-
}
|
|
249
|
-
|
|
250
159
|
function enclosingLoop(path: NodePath): NodePath<t.Loop> | null {
|
|
251
160
|
for (let current = path.parentPath; current; current = current.parentPath) {
|
|
252
161
|
if (current.isFunction()) {
|
|
@@ -3,11 +3,9 @@ import type { File } from '@babel/types'
|
|
|
3
3
|
|
|
4
4
|
import { traverseForChanges } from '../utils/change-tracking'
|
|
5
5
|
import type { ChangeState } from '../utils/change-tracking'
|
|
6
|
-
import { hasConstantViolation } from '../utils/paths'
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
|
-
* Changes `const` to `let
|
|
10
|
-
* the same scope and TDZ behavior; the declarations differ only on writes.
|
|
8
|
+
* Changes `const` declarations to `let`.
|
|
11
9
|
*
|
|
12
10
|
* @example
|
|
13
11
|
* // ◀️ before
|
|
@@ -27,9 +25,6 @@ const visitor: Visitor<ChangeState> = {
|
|
|
27
25
|
if (path.node.kind !== 'const') {
|
|
28
26
|
return
|
|
29
27
|
}
|
|
30
|
-
if (hasConstantViolation(path)) {
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
28
|
path.node.kind = 'let'
|
|
34
29
|
state.changed = true
|
|
35
30
|
},
|
|
@@ -4,7 +4,6 @@ import * as t from '@babel/types'
|
|
|
4
4
|
import type { File } from '@babel/types'
|
|
5
5
|
|
|
6
6
|
import { isSideEffectFree } from '../analysis/purity'
|
|
7
|
-
import { isInsideWith } from '../utils/ast'
|
|
8
7
|
import { traverseForChanges } from '../utils/change-tracking'
|
|
9
8
|
import type { ChangeState } from '../utils/change-tracking'
|
|
10
9
|
|
|
@@ -90,8 +89,7 @@ const visitor: Visitor<ChangeState> = {
|
|
|
90
89
|
if (!t.isIdentifier(object) || object.name !== 'console') {
|
|
91
90
|
return
|
|
92
91
|
}
|
|
93
|
-
|
|
94
|
-
if (path.scope.getBinding('console') || isInsideWith(path)) {
|
|
92
|
+
if (path.scope.getBinding('console')) {
|
|
95
93
|
return
|
|
96
94
|
}
|
|
97
95
|
// Removing the call also removes argument and computed-key evaluation.
|
|
@@ -108,11 +106,11 @@ function callArgumentsAreInert(
|
|
|
108
106
|
callee: t.MemberExpression | t.OptionalMemberExpression,
|
|
109
107
|
path: NodePath<t.ExpressionStatement>,
|
|
110
108
|
): boolean {
|
|
111
|
-
if (callee.computed && !isSideEffectFree(callee.property, path.scope
|
|
109
|
+
if (callee.computed && !isSideEffectFree(callee.property, path.scope)) {
|
|
112
110
|
return false
|
|
113
111
|
}
|
|
114
112
|
for (const arg of expr.arguments) {
|
|
115
|
-
if (!t.isExpression(arg) || !isSideEffectFree(arg, path.scope
|
|
113
|
+
if (!t.isExpression(arg) || !isSideEffectFree(arg, path.scope)) {
|
|
116
114
|
return false
|
|
117
115
|
}
|
|
118
116
|
}
|
|
@@ -2,7 +2,6 @@ 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
|
|
|
@@ -58,10 +57,6 @@ function lowerBinaryAssignment(path: NodePath<t.AssignmentExpression>): boolean
|
|
|
58
57
|
const read = t.cloneNode(left, true)
|
|
59
58
|
|
|
60
59
|
if (t.isMemberExpression(left) && t.isMemberExpression(read)) {
|
|
61
|
-
// `with` can turn a bare object name into a getter or shadow a memo temp.
|
|
62
|
-
if (isInsideWith(path)) {
|
|
63
|
-
return false
|
|
64
|
-
}
|
|
65
60
|
const object = left.object
|
|
66
61
|
if (t.isExpression(object)) {
|
|
67
62
|
const memo = path.scope.maybeGenerateMemoised(object)
|
|
@@ -2,7 +2,6 @@ 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
|
|
|
@@ -52,10 +51,6 @@ function lowerLogicalAssignment(path: NodePath<t.AssignmentExpression>): boolean
|
|
|
52
51
|
const lhs = t.cloneNode(left, true)
|
|
53
52
|
|
|
54
53
|
if (t.isMemberExpression(left) && t.isMemberExpression(lhs)) {
|
|
55
|
-
// `with` can turn a bare object name into a getter or shadow a memo temp.
|
|
56
|
-
if (isInsideWith(path)) {
|
|
57
|
-
return false
|
|
58
|
-
}
|
|
59
54
|
const object = left.object
|
|
60
55
|
if (t.isExpression(object)) {
|
|
61
56
|
const memo = path.scope.maybeGenerateMemoised(object)
|
|
@@ -3,7 +3,7 @@ import * as t from '@babel/types'
|
|
|
3
3
|
import type { File } from '@babel/types'
|
|
4
4
|
|
|
5
5
|
import { initializerReaches } from '../analysis/document-order'
|
|
6
|
-
import {
|
|
6
|
+
import { isProtoKey } from '../utils/ast'
|
|
7
7
|
import { traverseForChanges } from '../utils/change-tracking'
|
|
8
8
|
import type { ChangeState } from '../utils/change-tracking'
|
|
9
9
|
import { isCalleeOrTagOf } from '../utils/paths'
|
|
@@ -172,12 +172,7 @@ function performExtraction(
|
|
|
172
172
|
if (!binding) {
|
|
173
173
|
return false
|
|
174
174
|
}
|
|
175
|
-
if (!binding.constant) {
|
|
176
|
-
return false
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// `with` can shadow a synthesized entry binding.
|
|
180
|
-
if (isInsideWith(path)) {
|
|
175
|
+
if (path.node.kind !== 'const' && !binding.constant) {
|
|
181
176
|
return false
|
|
182
177
|
}
|
|
183
178
|
|
|
@@ -196,15 +191,11 @@ function performExtraction(
|
|
|
196
191
|
const seenMembers = new Set<t.MemberExpression>()
|
|
197
192
|
|
|
198
193
|
for (const ref of binding.referencePaths) {
|
|
199
|
-
//
|
|
200
|
-
|
|
194
|
+
// A pre-initializer `var` wrapper read sees `undefined`, while an extracted
|
|
195
|
+
// binding read would not perform the member access.
|
|
196
|
+
if (path.node.kind === 'var' && !initializerReaches(path, ref)) {
|
|
201
197
|
return false
|
|
202
198
|
}
|
|
203
|
-
// `with` can redirect the rewritten reference.
|
|
204
|
-
if (isInsideWith(ref)) {
|
|
205
|
-
return false
|
|
206
|
-
}
|
|
207
|
-
|
|
208
199
|
const parent = ref.parentPath
|
|
209
200
|
if (!parent || !parent.isMemberExpression()) {
|
|
210
201
|
return false
|
|
@@ -5,7 +5,7 @@ import type { File } from '@babel/types'
|
|
|
5
5
|
|
|
6
6
|
import { evaluateConstant, isOpaque } from '../analysis/constant'
|
|
7
7
|
import { isSideEffectFree } from '../analysis/purity'
|
|
8
|
-
import { isInDirectivePrologue
|
|
8
|
+
import { isInDirectivePrologue } from '../utils/ast'
|
|
9
9
|
import type { ChangeState } from '../utils/change-tracking'
|
|
10
10
|
import { valueToLiteral } from '../utils/literal'
|
|
11
11
|
|
|
@@ -507,7 +507,7 @@ function replaceWithPreservedExpression(
|
|
|
507
507
|
expression: t.Expression,
|
|
508
508
|
result: t.Expression,
|
|
509
509
|
): void {
|
|
510
|
-
if (!isSideEffectFree(expression, path.scope
|
|
510
|
+
if (!isSideEffectFree(expression, path.scope)) {
|
|
511
511
|
path.replaceWith(t.sequenceExpression([expression, result]))
|
|
512
512
|
return
|
|
513
513
|
}
|
|
@@ -527,7 +527,7 @@ function foldArrayLiteralLength(
|
|
|
527
527
|
}
|
|
528
528
|
const effects = array.elements.filter(
|
|
529
529
|
(element): element is t.Expression =>
|
|
530
|
-
element !== null && t.isExpression(element) && !isSideEffectFree(element, path.scope
|
|
530
|
+
element !== null && t.isExpression(element) && !isSideEffectFree(element, path.scope),
|
|
531
531
|
)
|
|
532
532
|
const length = t.numericLiteral(array.elements.length)
|
|
533
533
|
path.replaceWith(effects.length === 0 ? length : t.sequenceExpression([...effects, length]))
|
|
@@ -555,10 +555,6 @@ function foldStringIndex(
|
|
|
555
555
|
}
|
|
556
556
|
|
|
557
557
|
function handleCall(path: NodePath<t.CallExpression>, patchedRoots: Set<string>): void {
|
|
558
|
-
// `with` can replace an intrinsic that Babel treats as an unbound global.
|
|
559
|
-
if (isInsideWith(path)) {
|
|
560
|
-
return
|
|
561
|
-
}
|
|
562
558
|
const callee = path.node.callee
|
|
563
559
|
|
|
564
560
|
if (t.isIdentifier(callee)) {
|
|
@@ -606,11 +602,6 @@ function handleMember(path: NodePath<t.MemberExpression>, patchedRoots: Set<stri
|
|
|
606
602
|
if (isReferenceRequiredPosition(path)) {
|
|
607
603
|
return
|
|
608
604
|
}
|
|
609
|
-
// Delay the ancestor walk until cheap shape checks pass.
|
|
610
|
-
if (isInsideWith(path)) {
|
|
611
|
-
return
|
|
612
|
-
}
|
|
613
|
-
|
|
614
605
|
const obj = path.node.object
|
|
615
606
|
|
|
616
607
|
if (t.isIdentifier(obj) && !path.scope.getBinding(obj.name) && !patchedRoots.has(obj.name)) {
|
|
@@ -190,7 +190,7 @@ function tryReassociateCommutative(path: NodePath<t.BinaryExpression>, op: Commu
|
|
|
190
190
|
}
|
|
191
191
|
// Grouping constants can move one operand's coercion past another operand's
|
|
192
192
|
// evaluation. Multiple non-constant operands must therefore be pure.
|
|
193
|
-
if (others.length > 1 && !others.every((leaf) => isPure(leaf, path.scope
|
|
193
|
+
if (others.length > 1 && !others.every((leaf) => isPure(leaf, path.scope))) {
|
|
194
194
|
return false
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -296,7 +296,7 @@ function tryReassociateSubtract(path: NodePath<t.BinaryExpression>): boolean {
|
|
|
296
296
|
}
|
|
297
297
|
// Reassociation can move `base` coercion past a subtractee's evaluation.
|
|
298
298
|
// Non-constant subtractees must be pure.
|
|
299
|
-
if (!otherSubs.every((leaf) => isPure(leaf, path.scope
|
|
299
|
+
if (!otherSubs.every((leaf) => isPure(leaf, path.scope))) {
|
|
300
300
|
return false
|
|
301
301
|
}
|
|
302
302
|
const sum = combineConstants(constSubs, '+')
|
|
@@ -6,14 +6,13 @@ import { traverseForChanges } from '../utils/change-tracking'
|
|
|
6
6
|
import type { ChangeState } from '../utils/change-tracking'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Rewrites
|
|
10
|
-
*
|
|
9
|
+
* Rewrites function declarations at script top level or directly inside a
|
|
10
|
+
* function body as `var` initializers. It leaves a declaration unchanged when
|
|
11
|
+
* an earlier statement could execute user code before the assignment.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
* still use the `var` binding.
|
|
13
|
+
* Name inference and recursive references still use the `var` binding.
|
|
14
14
|
*
|
|
15
|
-
* Block-level
|
|
16
|
-
* to Annex B aliases and live exports.
|
|
15
|
+
* Block-level and module top-level declarations stay unchanged.
|
|
17
16
|
*
|
|
18
17
|
* @example
|
|
19
18
|
* // ◀️ before
|
|
@@ -9,8 +9,7 @@ import type { ChangeState } from '../utils/change-tracking'
|
|
|
9
9
|
* Moves `var` declarations to the start of their var scope without moving
|
|
10
10
|
* initializer evaluation.
|
|
11
11
|
*
|
|
12
|
-
* Direct exports
|
|
13
|
-
* keep separate declarations.
|
|
12
|
+
* Direct exports stay in place. Static blocks keep separate declarations.
|
|
14
13
|
*
|
|
15
14
|
* @example
|
|
16
15
|
* // ◀️ before
|
|
@@ -43,7 +42,7 @@ export function hoistVariables(ast: File): boolean {
|
|
|
43
42
|
return
|
|
44
43
|
}
|
|
45
44
|
const owner = ownerPath(path)
|
|
46
|
-
if (owner === null
|
|
45
|
+
if (owner === null) {
|
|
47
46
|
return
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -98,15 +97,6 @@ function ownerPath(path: NodePath<t.VariableDeclaration>): OwnerPath | null {
|
|
|
98
97
|
return null
|
|
99
98
|
}
|
|
100
99
|
|
|
101
|
-
function isInsideOwnerWith(path: NodePath, owner: OwnerPath): boolean {
|
|
102
|
-
for (let current = path.parentPath; current && current !== owner; current = current.parentPath) {
|
|
103
|
-
if (current.isWithStatement()) {
|
|
104
|
-
return true
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return false
|
|
108
|
-
}
|
|
109
|
-
|
|
110
100
|
function hoistedScope(scopes: Map<t.Node, HoistedScope>, owner: OwnerPath): HoistedScope {
|
|
111
101
|
const existing = scopes.get(owner.node)
|
|
112
102
|
if (existing) {
|
|
@@ -6,10 +6,10 @@ import type { File } from '@babel/types'
|
|
|
6
6
|
import { analyzeInlineability } from '../analysis/inlineability'
|
|
7
7
|
import type { InlineCandidate } from '../analysis/inlineability'
|
|
8
8
|
import { isPure } from '../analysis/purity'
|
|
9
|
-
import {
|
|
9
|
+
import { walkOwnFunctionScope } from '../utils/ast'
|
|
10
10
|
import { reorderableToStatement } from '../utils/evaluation-order'
|
|
11
11
|
import { generateLoopLabel } from '../utils/loop-lowering'
|
|
12
|
-
import {
|
|
12
|
+
import { isInStrictContext } from '../utils/paths'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Replaces eligible calls with function bodies, working from inner calls out.
|
|
@@ -135,13 +135,6 @@ function isSafeAtCallSite(
|
|
|
135
135
|
callPath: NodePath<t.CallExpression>,
|
|
136
136
|
candidates: Map<string, InlineCandidate>,
|
|
137
137
|
): boolean {
|
|
138
|
-
// `with` and Annex B aliases can resolve a free name outside Babel's scope table.
|
|
139
|
-
if (isInsideWith(callPath)) {
|
|
140
|
-
return false
|
|
141
|
-
}
|
|
142
|
-
if (annexBHoistShadowsFreeVar(callPath, freeVars)) {
|
|
143
|
-
return false
|
|
144
|
-
}
|
|
145
138
|
// Spliced code inherits call-site strictness, so it must match the callee.
|
|
146
139
|
if (isInStrictContext(candidate.fnPath) !== isInStrictContext(callPath)) {
|
|
147
140
|
return false
|
|
@@ -192,32 +185,6 @@ function isSafeAtCallSite(
|
|
|
192
185
|
return true
|
|
193
186
|
}
|
|
194
187
|
|
|
195
|
-
// ECMA-262 B.3.2.1 creates a sloppy block-function alias that Babel misses in
|
|
196
|
-
// the enclosing scope.
|
|
197
|
-
function annexBHoistShadowsFreeVar(
|
|
198
|
-
callPath: NodePath<t.CallExpression>,
|
|
199
|
-
freeVars: Set<string>,
|
|
200
|
-
): boolean {
|
|
201
|
-
if (freeVars.size === 0 || isInStrictContext(callPath)) {
|
|
202
|
-
return false
|
|
203
|
-
}
|
|
204
|
-
const owner = callPath.getFunctionParent() ?? callPath.scope.getProgramParent().path
|
|
205
|
-
let found = false
|
|
206
|
-
owner.traverse({
|
|
207
|
-
Function(path) {
|
|
208
|
-
path.skip()
|
|
209
|
-
},
|
|
210
|
-
FunctionDeclaration(path) {
|
|
211
|
-
const id = path.node.id
|
|
212
|
-
if (id && freeVars.has(id.name) && hasAnnexBFunctionAlias(path)) {
|
|
213
|
-
found = true
|
|
214
|
-
path.stop()
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
})
|
|
218
|
-
return found
|
|
219
|
-
}
|
|
220
|
-
|
|
221
188
|
// Only loops in the same function repeat the spliced body.
|
|
222
189
|
function callIsInsideLoop(callPath: NodePath): boolean {
|
|
223
190
|
for (let current: NodePath | null = callPath.parentPath; current; current = current.parentPath) {
|
|
@@ -580,7 +547,9 @@ function inlineCallSite(
|
|
|
580
547
|
const replacement: t.Statement[] = []
|
|
581
548
|
if (directTarget.kind === 'declarator') {
|
|
582
549
|
replacement.push(
|
|
583
|
-
t.variableDeclaration(
|
|
550
|
+
t.variableDeclaration(directTarget.declarationKind, [
|
|
551
|
+
t.variableDeclarator(t.identifier(directTarget.name)),
|
|
552
|
+
]),
|
|
584
553
|
)
|
|
585
554
|
}
|
|
586
555
|
if (needsLabel) {
|
|
@@ -620,7 +589,9 @@ function inlineCallSite(
|
|
|
620
589
|
return true
|
|
621
590
|
}
|
|
622
591
|
|
|
623
|
-
type DirectTarget =
|
|
592
|
+
type DirectTarget =
|
|
593
|
+
| { kind: 'declarator'; name: string; declarationKind: 'let' | 'var' }
|
|
594
|
+
| { kind: 'assign'; name: string }
|
|
624
595
|
|
|
625
596
|
function identifyDirectAssignmentTarget(
|
|
626
597
|
callPath: NodePath<t.CallExpression>,
|
|
@@ -641,9 +612,8 @@ function identifyDirectAssignmentTarget(
|
|
|
641
612
|
if (!declPath || !declPath.isVariableDeclaration() || declPath.node.declarations.length !== 1) {
|
|
642
613
|
return null
|
|
643
614
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (declPath.node.kind !== 'var') {
|
|
615
|
+
const declarationKind = declPath.node.kind
|
|
616
|
+
if (declarationKind !== 'var' && declarationKind !== 'let') {
|
|
647
617
|
return null
|
|
648
618
|
}
|
|
649
619
|
const owner = declPath.parentPath
|
|
@@ -666,7 +636,7 @@ function identifyDirectAssignmentTarget(
|
|
|
666
636
|
if (argsContainIdentName(args, name)) {
|
|
667
637
|
return null
|
|
668
638
|
}
|
|
669
|
-
return { kind: 'declarator', name }
|
|
639
|
+
return { kind: 'declarator', name, declarationKind }
|
|
670
640
|
}
|
|
671
641
|
|
|
672
642
|
if (
|
|
@@ -713,10 +683,10 @@ function argEligibleForElision(argPath: NodePath, scope: Scope): boolean {
|
|
|
713
683
|
return false
|
|
714
684
|
}
|
|
715
685
|
const binding = scope.getBinding(argPath.node.name)
|
|
716
|
-
if (!binding || binding.constantViolations.length > 0) {
|
|
686
|
+
if (!binding || (binding.kind !== 'const' && binding.constantViolations.length > 0)) {
|
|
717
687
|
return false
|
|
718
688
|
}
|
|
719
|
-
return isPure(argPath.node, scope
|
|
689
|
+
return isPure(argPath.node, scope)
|
|
720
690
|
}
|
|
721
691
|
|
|
722
692
|
// Catch parameters stay scoped to their copied handlers. Counting them here
|
|
@@ -930,8 +900,8 @@ function isNonReferenceSlot(parent: t.Node, key: string): boolean {
|
|
|
930
900
|
interface StatementWalkContext {
|
|
931
901
|
insideNestedLoop: boolean
|
|
932
902
|
insideNestedSwitch: boolean
|
|
933
|
-
// Tail position propagates through final blocks, branches, and
|
|
934
|
-
//
|
|
903
|
+
// Tail position propagates through final blocks, branches, labels, and `with`.
|
|
904
|
+
// Try, switch, and loops interrupt it because more code may run afterward.
|
|
935
905
|
tailPosition: boolean
|
|
936
906
|
}
|
|
937
907
|
|
|
@@ -1051,7 +1021,7 @@ function walkIntoCompound(
|
|
|
1051
1021
|
}
|
|
1052
1022
|
|
|
1053
1023
|
if (t.isWithStatement(node)) {
|
|
1054
|
-
node.body = walkSingleChild(node.body, hooks,
|
|
1024
|
+
node.body = walkSingleChild(node.body, hooks, ctx)
|
|
1055
1025
|
}
|
|
1056
1026
|
}
|
|
1057
1027
|
|
|
@@ -3,10 +3,10 @@ import * as t from '@babel/types'
|
|
|
3
3
|
import type { File } from '@babel/types'
|
|
4
4
|
|
|
5
5
|
import { isPure } from '../analysis/purity'
|
|
6
|
-
import { isInsideWith } from '../utils/ast'
|
|
7
6
|
import { traverseForChanges } from '../utils/change-tracking'
|
|
8
7
|
import type { ChangeState } from '../utils/change-tracking'
|
|
9
8
|
import { lowerLogicalWriteStatement } from '../utils/logical-lowering'
|
|
9
|
+
import { buildNonNullishCheck, buildNullishCheck } from '../utils/nullish'
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Rewrites logical expressions as guarded assignments or ternaries. A
|
|
@@ -23,7 +23,7 @@ import { lowerLogicalWriteStatement } from '../utils/logical-lowering'
|
|
|
23
23
|
* if (!x) x = b;
|
|
24
24
|
* y = c;
|
|
25
25
|
* if (y) y = d;
|
|
26
|
-
* f((_t = get())
|
|
26
|
+
* f(((_t = get()) !== null ? _t !== void 0 : false) ? _t : fallback);
|
|
27
27
|
*/
|
|
28
28
|
export function logicalToTernary(ast: File): boolean {
|
|
29
29
|
return traverseForChanges(ast, visitor)
|
|
@@ -45,11 +45,6 @@ function lowerLogical(path: NodePath<t.LogicalExpression>): boolean {
|
|
|
45
45
|
if (op !== '&&' && op !== '||' && op !== '??') {
|
|
46
46
|
return false
|
|
47
47
|
}
|
|
48
|
-
// `with` can turn repeated names into getters and shadow a cache temp.
|
|
49
|
-
if (isInsideWith(path)) {
|
|
50
|
-
return false
|
|
51
|
-
}
|
|
52
|
-
|
|
53
48
|
if (lowerLogicalWriteStatement(path, (target) => buildFallbackTest(op, target))) {
|
|
54
49
|
return true
|
|
55
50
|
}
|
|
@@ -60,7 +55,7 @@ function lowerLogical(path: NodePath<t.LogicalExpression>): boolean {
|
|
|
60
55
|
let valueSrc: t.Expression
|
|
61
56
|
|
|
62
57
|
// Babel purity misses user coercion in unary and binary expressions.
|
|
63
|
-
if (isPure(leftPath.node, scope
|
|
58
|
+
if (isPure(leftPath.node, scope)) {
|
|
64
59
|
testSrc = node.left
|
|
65
60
|
valueSrc = t.cloneNode(node.left)
|
|
66
61
|
} else {
|
|
@@ -71,7 +66,7 @@ function lowerLogical(path: NodePath<t.LogicalExpression>): boolean {
|
|
|
71
66
|
}
|
|
72
67
|
|
|
73
68
|
const test: t.Expression =
|
|
74
|
-
op === '??' ?
|
|
69
|
+
op === '??' ? buildNonNullishCheck(testSrc, t.cloneNode(valueSrc)) : testSrc
|
|
75
70
|
|
|
76
71
|
path.replaceWith(
|
|
77
72
|
op === '&&'
|
|
@@ -88,5 +83,5 @@ function buildFallbackTest(op: '&&' | '||' | '??', target: t.Identifier): t.Expr
|
|
|
88
83
|
if (op === '&&') {
|
|
89
84
|
return target
|
|
90
85
|
}
|
|
91
|
-
return
|
|
86
|
+
return buildNullishCheck(target, t.cloneNode(target))
|
|
92
87
|
}
|
|
@@ -13,11 +13,8 @@ import { StringGenerator } from '../utils/string-generator'
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Renames statically known public properties through one program-wide mapping.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* This pass runs before shape preparation, while Babel still distinguishes
|
|
20
|
-
* `obj.name` from `obj["name"]`.
|
|
16
|
+
* Options select or reserve names, and collision checks keep every unchanged
|
|
17
|
+
* property distinct from the generated names.
|
|
21
18
|
*
|
|
22
19
|
* @example
|
|
23
20
|
* // ◀️ before
|