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,336 @@
|
|
|
1
|
+
import traverse from '@babel/traverse'
|
|
2
|
+
import type { Binding, NodePath, Visitor } from '@babel/traverse'
|
|
3
|
+
import * as t from '@babel/types'
|
|
4
|
+
import type { File } from '@babel/types'
|
|
5
|
+
|
|
6
|
+
import { walkOwnFunctionScope } from 'src/utils/ast'
|
|
7
|
+
import { hasAnnexBFunctionAlias, referencesOrWritesVariable } from 'src/utils/paths'
|
|
8
|
+
|
|
9
|
+
export interface InlineCandidate {
|
|
10
|
+
name: string
|
|
11
|
+
fnNode: t.Function
|
|
12
|
+
fnPath: NodePath<t.Function>
|
|
13
|
+
binding: Binding
|
|
14
|
+
params: string[]
|
|
15
|
+
declarationPath: NodePath
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Tarjan SCC removal prevents endless expansion of mutually recursive bodies.
|
|
19
|
+
export function analyzeInlineability(ast: File): Map<string, InlineCandidate> {
|
|
20
|
+
const rawCandidates: InlineCandidate[] = []
|
|
21
|
+
|
|
22
|
+
// Earlier passes can leave `binding.referencePaths` on detached nodes.
|
|
23
|
+
traverse(ast, {
|
|
24
|
+
Program(path) {
|
|
25
|
+
path.scope.crawl()
|
|
26
|
+
},
|
|
27
|
+
FunctionDeclaration(path) {
|
|
28
|
+
if (!path.node.id) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
// Babel misses the sloppy Annex B `var` alias of a block-level function.
|
|
32
|
+
if (hasAnnexBFunctionAlias(path)) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
const binding = path.scope.getBinding(path.node.id.name)
|
|
36
|
+
if (!binding) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const candidate = checkCandidate(path.node.id.name, binding, path.node, path, path)
|
|
41
|
+
if (candidate) {
|
|
42
|
+
rawCandidates.push(candidate)
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
VariableDeclarator(path) {
|
|
47
|
+
if (!t.isIdentifier(path.node.id)) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
const { init } = path.node
|
|
51
|
+
if (!t.isFunctionExpression(init)) {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const binding = path.scope.getBinding(path.node.id.name)
|
|
56
|
+
if (!binding) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const declPath = path.parentPath
|
|
61
|
+
if (!declPath) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const initPath = path.get('init') as NodePath<t.FunctionExpression>
|
|
66
|
+
const candidate = checkCandidate(path.node.id.name, binding, init, initPath, declPath)
|
|
67
|
+
if (candidate) {
|
|
68
|
+
rawCandidates.push(candidate)
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const candidateByName = new Map<string, InlineCandidate>()
|
|
74
|
+
for (const candidate of rawCandidates) {
|
|
75
|
+
candidateByName.set(candidate.name, candidate)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Self-reference is already excluded, leaving Tarjan to remove larger cycles.
|
|
79
|
+
const edges = new Map<string, Set<string>>()
|
|
80
|
+
for (const candidate of rawCandidates) {
|
|
81
|
+
const callees = new Set<string>()
|
|
82
|
+
walkOwnFunctionScope(candidate.fnNode.body, (node) => {
|
|
83
|
+
if (t.isIdentifier(node) && node.name !== candidate.name && candidateByName.has(node.name)) {
|
|
84
|
+
callees.add(node.name)
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
edges.set(candidate.name, callees)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const inCycle = new Set<string>()
|
|
91
|
+
for (const scc of tarjanSCC(
|
|
92
|
+
rawCandidates.map((candidate) => candidate.name),
|
|
93
|
+
edges,
|
|
94
|
+
)) {
|
|
95
|
+
if (scc.length > 1) {
|
|
96
|
+
for (const name of scc) {
|
|
97
|
+
inCycle.add(name)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const name of inCycle) {
|
|
103
|
+
candidateByName.delete(name)
|
|
104
|
+
}
|
|
105
|
+
return candidateByName
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function checkCandidate(
|
|
109
|
+
name: string,
|
|
110
|
+
binding: Binding,
|
|
111
|
+
fnNode: t.Function,
|
|
112
|
+
fnPath: NodePath,
|
|
113
|
+
declarationPath: NodePath,
|
|
114
|
+
): InlineCandidate | null {
|
|
115
|
+
if (fnNode.generator || fnNode.async) {
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
if (!t.isBlockStatement(fnNode.body)) {
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
// Writes live in `constantViolations`, not `referencePaths`. Any write can
|
|
122
|
+
// make a call resolve to a different function.
|
|
123
|
+
if (binding.constantViolations.length > 0) {
|
|
124
|
+
return null
|
|
125
|
+
}
|
|
126
|
+
// Splicing loses a named function expression's inner binding.
|
|
127
|
+
if (t.isFunctionExpression(fnNode) && fnNode.id) {
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
// The flat renamer cannot preserve nested declarations after relocation.
|
|
131
|
+
if (bodyHasUnrelocatableDeclaration(fnNode.body)) {
|
|
132
|
+
return null
|
|
133
|
+
}
|
|
134
|
+
if (!validateCallSites(binding)) {
|
|
135
|
+
return null
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const params: (string | null)[] = fnNode.params.map((param) =>
|
|
139
|
+
t.isIdentifier(param) ? param.name : null,
|
|
140
|
+
)
|
|
141
|
+
if (params.some((param) => param === null)) {
|
|
142
|
+
return null
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const analysis = analyzeBody(name, fnPath as NodePath<t.Function>)
|
|
146
|
+
if (analysis.unsafe || analysis.closureCapture) {
|
|
147
|
+
return null
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
name,
|
|
152
|
+
fnNode,
|
|
153
|
+
fnPath: fnPath as NodePath<t.Function>,
|
|
154
|
+
binding,
|
|
155
|
+
params: params as string[],
|
|
156
|
+
declarationPath,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function bodyHasUnrelocatableDeclaration(body: t.BlockStatement): boolean {
|
|
161
|
+
let found = false
|
|
162
|
+
t.traverseFast(body, (node) => {
|
|
163
|
+
if (found) {
|
|
164
|
+
return t.traverseFast.skip
|
|
165
|
+
}
|
|
166
|
+
// Function declarations hoist into the relocated scope. Expressions do not.
|
|
167
|
+
if (node !== body && t.isFunction(node)) {
|
|
168
|
+
if (t.isFunctionDeclaration(node)) {
|
|
169
|
+
found = true
|
|
170
|
+
}
|
|
171
|
+
return t.traverseFast.skip
|
|
172
|
+
}
|
|
173
|
+
if (
|
|
174
|
+
t.isClassDeclaration(node) ||
|
|
175
|
+
(t.isVariableDeclaration(node) && node.kind !== 'var') ||
|
|
176
|
+
(t.isVariableDeclarator(node) && !t.isIdentifier(node.id))
|
|
177
|
+
) {
|
|
178
|
+
found = true
|
|
179
|
+
}
|
|
180
|
+
return found ? t.traverseFast.skip : undefined
|
|
181
|
+
})
|
|
182
|
+
return found
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function validateCallSites(binding: Binding): boolean {
|
|
186
|
+
for (const ref of binding.referencePaths) {
|
|
187
|
+
const parent = ref.parentPath
|
|
188
|
+
if (!parent || !parent.isCallExpression() || parent.node.callee !== ref.node) {
|
|
189
|
+
return false
|
|
190
|
+
}
|
|
191
|
+
if (parent.node.arguments.some((arg) => t.isSpreadElement(arg))) {
|
|
192
|
+
return false
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return true
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface BodyAnalysis {
|
|
199
|
+
unsafe: boolean
|
|
200
|
+
closureCapture: boolean
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function analyzeBody(name: string, fnPath: NodePath<t.Function>): BodyAnalysis {
|
|
204
|
+
const state: AnalyzeBodyState = {
|
|
205
|
+
candidateName: name,
|
|
206
|
+
outerScope: fnPath.scope,
|
|
207
|
+
innerFnDepth: 0,
|
|
208
|
+
unsafe: false,
|
|
209
|
+
closureCapture: false,
|
|
210
|
+
}
|
|
211
|
+
// Start at the body to exclude the function id and parameter bindings.
|
|
212
|
+
const bodyPath = fnPath.get('body') as NodePath
|
|
213
|
+
bodyPath.traverse(analyzeBodyVisitor, state)
|
|
214
|
+
|
|
215
|
+
return { unsafe: state.unsafe, closureCapture: state.closureCapture }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
interface AnalyzeBodyState {
|
|
219
|
+
candidateName: string
|
|
220
|
+
outerScope: NodePath<t.Function>['scope']
|
|
221
|
+
innerFnDepth: number
|
|
222
|
+
unsafe: boolean
|
|
223
|
+
closureCapture: boolean
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const analyzeBodyVisitor: Visitor<AnalyzeBodyState> = {
|
|
227
|
+
Function: {
|
|
228
|
+
enter(path, state) {
|
|
229
|
+
if (state.unsafe || state.closureCapture) {
|
|
230
|
+
path.stop()
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
if (!path.isArrowFunctionExpression()) {
|
|
234
|
+
state.innerFnDepth++
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
exit(path, state) {
|
|
238
|
+
if (!path.isArrowFunctionExpression()) {
|
|
239
|
+
state.innerFnDepth--
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
ThisExpression(path, state) {
|
|
244
|
+
if (state.innerFnDepth === 0) {
|
|
245
|
+
state.unsafe = true
|
|
246
|
+
path.stop()
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
Super(path, state) {
|
|
250
|
+
if (state.innerFnDepth === 0) {
|
|
251
|
+
state.unsafe = true
|
|
252
|
+
path.stop()
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
MetaProperty(path, state) {
|
|
256
|
+
if (
|
|
257
|
+
state.innerFnDepth === 0 &&
|
|
258
|
+
path.node.meta.name === 'new' &&
|
|
259
|
+
path.node.property.name === 'target'
|
|
260
|
+
) {
|
|
261
|
+
state.unsafe = true
|
|
262
|
+
path.stop()
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
WithStatement(path, state) {
|
|
266
|
+
state.unsafe = true
|
|
267
|
+
path.stop()
|
|
268
|
+
},
|
|
269
|
+
Identifier(path, state) {
|
|
270
|
+
const { name } = path.node
|
|
271
|
+
if (
|
|
272
|
+
state.innerFnDepth === 0 &&
|
|
273
|
+
(name === 'arguments' || name === 'eval' || name === state.candidateName)
|
|
274
|
+
) {
|
|
275
|
+
state.unsafe = true
|
|
276
|
+
path.stop()
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
// Destructured writes are neither references nor direct assignment children.
|
|
280
|
+
if (!referencesOrWritesVariable(path)) {
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
if (state.innerFnDepth > 0 && state.outerScope.bindings[name]) {
|
|
284
|
+
const binding = path.scope.getBinding(name)
|
|
285
|
+
if (binding && state.outerScope.bindings[name] === binding) {
|
|
286
|
+
state.closureCapture = true
|
|
287
|
+
path.stop()
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function tarjanSCC(nodes: string[], edges: Map<string, Set<string>>): string[][] {
|
|
294
|
+
let index = 0
|
|
295
|
+
const stack: string[] = []
|
|
296
|
+
const onStack = new Set<string>()
|
|
297
|
+
const indices = new Map<string, number>()
|
|
298
|
+
const lowlinks = new Map<string, number>()
|
|
299
|
+
const result: string[][] = []
|
|
300
|
+
|
|
301
|
+
function strongConnect(vertex: string): void {
|
|
302
|
+
indices.set(vertex, index)
|
|
303
|
+
lowlinks.set(vertex, index)
|
|
304
|
+
index++
|
|
305
|
+
stack.push(vertex)
|
|
306
|
+
onStack.add(vertex)
|
|
307
|
+
|
|
308
|
+
for (const successor of edges.get(vertex) ?? []) {
|
|
309
|
+
if (!indices.has(successor)) {
|
|
310
|
+
strongConnect(successor)
|
|
311
|
+
lowlinks.set(vertex, Math.min(lowlinks.get(vertex)!, lowlinks.get(successor)!))
|
|
312
|
+
} else if (onStack.has(successor)) {
|
|
313
|
+
lowlinks.set(vertex, Math.min(lowlinks.get(vertex)!, indices.get(successor)!))
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (lowlinks.get(vertex) === indices.get(vertex)) {
|
|
318
|
+
const scc: string[] = []
|
|
319
|
+
let popped: string
|
|
320
|
+
do {
|
|
321
|
+
popped = stack.pop()!
|
|
322
|
+
onStack.delete(popped)
|
|
323
|
+
scc.push(popped)
|
|
324
|
+
} while (popped !== vertex)
|
|
325
|
+
result.push(scc)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
for (const vertex of nodes) {
|
|
330
|
+
if (!indices.has(vertex)) {
|
|
331
|
+
strongConnect(vertex)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return result
|
|
336
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import type { Binding, NodePath, Scope } from '@babel/traverse'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
declarationReaches,
|
|
6
|
+
initializerReaches,
|
|
7
|
+
readCrossesFunctionBoundary,
|
|
8
|
+
} from 'src/analysis/document-order'
|
|
9
|
+
import { isInsideWith } from 'src/utils/ast'
|
|
10
|
+
import { isPrimitiveLiteral } from 'src/utils/literal'
|
|
11
|
+
|
|
12
|
+
// Purity also requires deterministic repetition and no throw. Identifier reads
|
|
13
|
+
// need scope and reference paths to prove resolution, initialization, and the
|
|
14
|
+
// absence of `with` interception.
|
|
15
|
+
export function isPure(node: t.Node, scope?: Scope, referencePath?: NodePath): boolean {
|
|
16
|
+
if (
|
|
17
|
+
t.isNumericLiteral(node) ||
|
|
18
|
+
t.isStringLiteral(node) ||
|
|
19
|
+
t.isBooleanLiteral(node) ||
|
|
20
|
+
t.isNullLiteral(node) ||
|
|
21
|
+
t.isBigIntLiteral(node) ||
|
|
22
|
+
t.isRegExpLiteral(node) ||
|
|
23
|
+
t.isThisExpression(node)
|
|
24
|
+
) {
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
if (t.isIdentifier(node)) {
|
|
28
|
+
return identifierReadIsSafe(node, scope, referencePath)
|
|
29
|
+
}
|
|
30
|
+
if (t.isTemplateLiteral(node)) {
|
|
31
|
+
// ToString can call object code or throw on Symbol.
|
|
32
|
+
for (const expr of node.expressions) {
|
|
33
|
+
if (!t.isExpression(expr) || primitiveLiteralKind(expr) === null) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
if (t.isUnaryExpression(node)) {
|
|
40
|
+
// Numeric unary operators invoke ToPrimitive. `delete` and `throw` act.
|
|
41
|
+
if (node.operator === '!' || node.operator === 'typeof' || node.operator === 'void') {
|
|
42
|
+
return isPure(node.argument, scope, referencePath)
|
|
43
|
+
}
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
if (t.isBinaryExpression(node) && node.operator !== 'in' && node.operator !== 'instanceof') {
|
|
47
|
+
if (!t.isExpression(node.left)) {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
if (!isPure(node.left, scope, referencePath) || !isPure(node.right, scope, referencePath)) {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
// Only strict equality avoids coercion. Other operators need safe literals.
|
|
54
|
+
if (node.operator === '===' || node.operator === '!==') {
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
return coercesWithoutEffect(node.left, node.right, node.operator)
|
|
58
|
+
}
|
|
59
|
+
if (t.isLogicalExpression(node)) {
|
|
60
|
+
return isPure(node.left, scope, referencePath) && isPure(node.right, scope, referencePath)
|
|
61
|
+
}
|
|
62
|
+
if (t.isConditionalExpression(node)) {
|
|
63
|
+
return (
|
|
64
|
+
isPure(node.test, scope, referencePath) &&
|
|
65
|
+
isPure(node.consequent, scope, referencePath) &&
|
|
66
|
+
isPure(node.alternate, scope, referencePath)
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
if (t.isSequenceExpression(node)) {
|
|
70
|
+
for (const expr of node.expressions) {
|
|
71
|
+
if (!isPure(expr, scope, referencePath)) {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// A droppable identifier read must resolve after binding initialization.
|
|
81
|
+
export function isSideEffectFree(node: t.Node, scope?: Scope, referencePath?: NodePath): boolean {
|
|
82
|
+
if (
|
|
83
|
+
t.isNumericLiteral(node) ||
|
|
84
|
+
t.isStringLiteral(node) ||
|
|
85
|
+
t.isBooleanLiteral(node) ||
|
|
86
|
+
t.isNullLiteral(node) ||
|
|
87
|
+
t.isBigIntLiteral(node) ||
|
|
88
|
+
t.isRegExpLiteral(node)
|
|
89
|
+
) {
|
|
90
|
+
return true
|
|
91
|
+
}
|
|
92
|
+
if (t.isTemplateLiteral(node)) {
|
|
93
|
+
// ToString can call object code or throw on Symbol.
|
|
94
|
+
for (const expr of node.expressions) {
|
|
95
|
+
if (!t.isExpression(expr) || primitiveLiteralKind(expr) === null) {
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
if (t.isIdentifier(node)) {
|
|
102
|
+
return identifierReadIsSafe(node, scope, referencePath)
|
|
103
|
+
}
|
|
104
|
+
if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
|
|
105
|
+
return true
|
|
106
|
+
}
|
|
107
|
+
if (t.isClassExpression(node)) {
|
|
108
|
+
// `extends` and computed keys run during class definition.
|
|
109
|
+
if (node.superClass) {
|
|
110
|
+
if (!isSideEffectFree(node.superClass, scope, referencePath)) {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
// ClassDefinitionEvaluation accepts only `null` or a constructor.
|
|
114
|
+
if (!isConstructableSuperClass(node.superClass)) {
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
for (const member of node.body.body) {
|
|
119
|
+
// Static fields and blocks run during class definition.
|
|
120
|
+
if ((t.isClassProperty(member) || t.isClassPrivateProperty(member)) && member.static) {
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
if (t.isStaticBlock(member)) {
|
|
124
|
+
return false
|
|
125
|
+
}
|
|
126
|
+
if (
|
|
127
|
+
'computed' in member &&
|
|
128
|
+
member.computed &&
|
|
129
|
+
'key' in member &&
|
|
130
|
+
!isSideEffectFree(member.key, scope, referencePath)
|
|
131
|
+
) {
|
|
132
|
+
return false
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return true
|
|
136
|
+
}
|
|
137
|
+
if (t.isArrayExpression(node)) {
|
|
138
|
+
for (const element of node.elements) {
|
|
139
|
+
if (element === null) {
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
if (t.isSpreadElement(element)) {
|
|
143
|
+
return false
|
|
144
|
+
}
|
|
145
|
+
if (!isSideEffectFree(element, scope, referencePath)) {
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
if (t.isObjectExpression(node)) {
|
|
152
|
+
for (const prop of node.properties) {
|
|
153
|
+
if (t.isSpreadElement(prop)) {
|
|
154
|
+
return false
|
|
155
|
+
}
|
|
156
|
+
if (t.isObjectMethod(prop)) {
|
|
157
|
+
if (prop.computed && !isSideEffectFree(prop.key, scope, referencePath)) {
|
|
158
|
+
return false
|
|
159
|
+
}
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
if (!t.isObjectProperty(prop)) {
|
|
163
|
+
return false
|
|
164
|
+
}
|
|
165
|
+
if (prop.computed && !isSideEffectFree(prop.key, scope, referencePath)) {
|
|
166
|
+
return false
|
|
167
|
+
}
|
|
168
|
+
if (!t.isExpression(prop.value)) {
|
|
169
|
+
return false
|
|
170
|
+
}
|
|
171
|
+
if (!isSideEffectFree(prop.value, scope, referencePath)) {
|
|
172
|
+
return false
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return true
|
|
176
|
+
}
|
|
177
|
+
if (t.isUnaryExpression(node)) {
|
|
178
|
+
if (node.operator === 'delete' || node.operator === 'throw') {
|
|
179
|
+
return false
|
|
180
|
+
}
|
|
181
|
+
if (node.operator === '!' || node.operator === 'typeof' || node.operator === 'void') {
|
|
182
|
+
return isSideEffectFree(node.argument, scope, referencePath)
|
|
183
|
+
}
|
|
184
|
+
return false
|
|
185
|
+
}
|
|
186
|
+
if (t.isBinaryExpression(node) && node.operator !== 'in' && node.operator !== 'instanceof') {
|
|
187
|
+
if (!t.isExpression(node.left)) {
|
|
188
|
+
return false
|
|
189
|
+
}
|
|
190
|
+
if (
|
|
191
|
+
!isSideEffectFree(node.left, scope, referencePath) ||
|
|
192
|
+
!isSideEffectFree(node.right, scope, referencePath)
|
|
193
|
+
) {
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
// Coercing operators need compatible literals that cannot throw.
|
|
197
|
+
if (node.operator === '===' || node.operator === '!==') {
|
|
198
|
+
return true
|
|
199
|
+
}
|
|
200
|
+
return coercesWithoutEffect(node.left, node.right, node.operator)
|
|
201
|
+
}
|
|
202
|
+
if (t.isLogicalExpression(node)) {
|
|
203
|
+
return (
|
|
204
|
+
isSideEffectFree(node.left, scope, referencePath) &&
|
|
205
|
+
isSideEffectFree(node.right, scope, referencePath)
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
if (t.isConditionalExpression(node)) {
|
|
209
|
+
return (
|
|
210
|
+
isSideEffectFree(node.test, scope, referencePath) &&
|
|
211
|
+
isSideEffectFree(node.consequent, scope, referencePath) &&
|
|
212
|
+
isSideEffectFree(node.alternate, scope, referencePath)
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
if (t.isSequenceExpression(node)) {
|
|
216
|
+
for (const expr of node.expressions) {
|
|
217
|
+
if (!isSideEffectFree(expr, scope, referencePath)) {
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return true
|
|
222
|
+
}
|
|
223
|
+
return false
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// A safe read needs a resolved, initialized binding outside `with`. Unbound
|
|
227
|
+
// `undefined` is safe because the global property is immutable.
|
|
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
|
+
}
|
|
237
|
+
const isUndefinedName = node.name === 'undefined'
|
|
238
|
+
if (!scope) {
|
|
239
|
+
return isUndefinedName
|
|
240
|
+
}
|
|
241
|
+
const binding = scope.getBinding(node.name)
|
|
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)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Only `null` and plain function expressions are known-valid superclass values.
|
|
266
|
+
function isConstructableSuperClass(node: t.Node): boolean {
|
|
267
|
+
if (t.isNullLiteral(node)) {
|
|
268
|
+
return true
|
|
269
|
+
}
|
|
270
|
+
return t.isFunctionExpression(node) && !node.generator && !node.async
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Track BigInt separately because mixed arithmetic throws.
|
|
274
|
+
function primitiveLiteralKind(node: t.Node): 'bigint' | 'other' | null {
|
|
275
|
+
if (t.isBigIntLiteral(node)) {
|
|
276
|
+
return 'bigint'
|
|
277
|
+
}
|
|
278
|
+
return isPrimitiveLiteral(node) ? 'other' : null
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// BigInt division, remainder, exponentiation, and unsigned shift can throw from
|
|
282
|
+
// values or operator support that the type alone cannot prove safe.
|
|
283
|
+
const THROWING_BIGINT_OPERATORS = new Set<string>(['/', '%', '**', '>>>'])
|
|
284
|
+
|
|
285
|
+
function coercesWithoutEffect(left: t.Node, right: t.Node, operator: string): boolean {
|
|
286
|
+
const leftKind = primitiveLiteralKind(left)
|
|
287
|
+
const rightKind = primitiveLiteralKind(right)
|
|
288
|
+
if (leftKind === null || rightKind === null) {
|
|
289
|
+
return false
|
|
290
|
+
}
|
|
291
|
+
// Mixed BigInt arithmetic throws.
|
|
292
|
+
if (leftKind !== rightKind) {
|
|
293
|
+
return false
|
|
294
|
+
}
|
|
295
|
+
return leftKind !== 'bigint' || !THROWING_BIGINT_OPERATORS.has(operator)
|
|
296
|
+
}
|
|
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
|
+
}
|
package/src/index.ts
ADDED