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,178 @@
|
|
|
1
|
+
import type { NodePath } from '@babel/traverse'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
|
|
4
|
+
import { isPure } from 'src/analysis/purity'
|
|
5
|
+
|
|
6
|
+
// Moving a gated node can change whether it runs.
|
|
7
|
+
export function isConditionalGate(parent: NodePath, cur: NodePath): boolean {
|
|
8
|
+
if (parent.isLogicalExpression() && parent.node.right === cur.node) {
|
|
9
|
+
return true
|
|
10
|
+
}
|
|
11
|
+
if (
|
|
12
|
+
parent.isConditionalExpression() &&
|
|
13
|
+
(parent.node.consequent === cur.node || parent.node.alternate === cur.node)
|
|
14
|
+
) {
|
|
15
|
+
return true
|
|
16
|
+
}
|
|
17
|
+
if (parent.isOptionalMemberExpression()) {
|
|
18
|
+
// Computed keys run only after the optional base proves non-nullish.
|
|
19
|
+
if (parent.node.object === cur.node) {
|
|
20
|
+
return parent.node.optional
|
|
21
|
+
}
|
|
22
|
+
if (parent.node.computed && parent.node.property === cur.node) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (parent.isOptionalCallExpression()) {
|
|
27
|
+
if (parent.node.callee === cur.node) {
|
|
28
|
+
return parent.node.optional
|
|
29
|
+
}
|
|
30
|
+
// Arguments run only once the callee proves non-nullish.
|
|
31
|
+
if (parent.node.arguments.some((arg) => arg === cur.node)) {
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Hoisting to statement position requires no gate or earlier observable work.
|
|
39
|
+
export function reorderableToStatement(path: NodePath, stmtPath: NodePath): boolean {
|
|
40
|
+
let cur: NodePath = path
|
|
41
|
+
let parent = cur.parentPath
|
|
42
|
+
while (parent && parent !== stmtPath) {
|
|
43
|
+
if (isConditionalGate(parent, cur)) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
if (precedingEvaluationHasSideEffect(parent, cur)) {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
cur = parent
|
|
50
|
+
parent = cur.parentPath
|
|
51
|
+
}
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Unknown parent shapes are unsafe. Known operands use `isPure` with their live
|
|
56
|
+
// scope and path.
|
|
57
|
+
function precedingEvaluationHasSideEffect(parentPath: NodePath, curPath: NodePath): boolean {
|
|
58
|
+
const parent = parentPath.node
|
|
59
|
+
const cur = curPath.node
|
|
60
|
+
|
|
61
|
+
if (t.isBinaryExpression(parent)) {
|
|
62
|
+
return parent.right === cur && impure([parentPath.get('left') as NodePath])
|
|
63
|
+
}
|
|
64
|
+
if (
|
|
65
|
+
t.isLogicalExpression(parent) ||
|
|
66
|
+
t.isUnaryExpression(parent) ||
|
|
67
|
+
t.isSpreadElement(parent) ||
|
|
68
|
+
t.isVariableDeclarator(parent)
|
|
69
|
+
) {
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
if (t.isVariableDeclaration(parent)) {
|
|
73
|
+
const declarators = precedingSiblings(parentPath, 'declarations', cur)
|
|
74
|
+
return impure(declarators.map((decl) => decl.get('init') as NodePath))
|
|
75
|
+
}
|
|
76
|
+
if (t.isAssignmentExpression(parent) && parent.right === cur) {
|
|
77
|
+
const target = parentPath.get('left') as NodePath
|
|
78
|
+
// Compound assignment reads its target before the right side. Plain `=`
|
|
79
|
+
// only resolves the write reference.
|
|
80
|
+
if (parent.operator === '=') {
|
|
81
|
+
return !writeReferenceIsPure(target)
|
|
82
|
+
}
|
|
83
|
+
return !compoundTargetReadIsPure(target)
|
|
84
|
+
}
|
|
85
|
+
if (t.isSequenceExpression(parent)) {
|
|
86
|
+
return impure(precedingSiblings(parentPath, 'expressions', cur))
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
t.isCallExpression(parent) ||
|
|
90
|
+
t.isOptionalCallExpression(parent) ||
|
|
91
|
+
t.isNewExpression(parent)
|
|
92
|
+
) {
|
|
93
|
+
const args = parentPath.get('arguments') as NodePath[]
|
|
94
|
+
const index = args.findIndex((arg) => arg.node === cur)
|
|
95
|
+
if (index === -1) {
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
return impure([parentPath.get('callee') as NodePath, ...args.slice(0, index)])
|
|
99
|
+
}
|
|
100
|
+
if (t.isMemberExpression(parent) || t.isOptionalMemberExpression(parent)) {
|
|
101
|
+
return parent.property === cur && impure([parentPath.get('object') as NodePath])
|
|
102
|
+
}
|
|
103
|
+
if (t.isArrayExpression(parent)) {
|
|
104
|
+
return impure(precedingSiblings(parentPath, 'elements', cur))
|
|
105
|
+
}
|
|
106
|
+
if (t.isObjectProperty(parent)) {
|
|
107
|
+
return parent.value === cur && parent.computed && impure([parentPath.get('key') as NodePath])
|
|
108
|
+
}
|
|
109
|
+
if (t.isObjectExpression(parent)) {
|
|
110
|
+
return precedingSiblings(parentPath, 'properties', cur).some((prop) => !propertyIsPure(prop))
|
|
111
|
+
}
|
|
112
|
+
if (t.isTemplateLiteral(parent)) {
|
|
113
|
+
return impure(precedingSiblings(parentPath, 'expressions', cur))
|
|
114
|
+
}
|
|
115
|
+
if (t.isConditionalExpression(parent)) {
|
|
116
|
+
// Branches were caught as gates, leaving only the test here.
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
return true
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// List entries evaluated before `cur` are the ones a hoist must cross.
|
|
123
|
+
function precedingSiblings<T extends NodePath>(
|
|
124
|
+
parentPath: NodePath,
|
|
125
|
+
listKey: string,
|
|
126
|
+
cur: t.Node,
|
|
127
|
+
): T[] {
|
|
128
|
+
const siblings = parentPath.get(listKey) as T[]
|
|
129
|
+
return siblings.slice(
|
|
130
|
+
0,
|
|
131
|
+
siblings.findIndex((sibling) => sibling.node === cur),
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isPurePath(path: NodePath): boolean {
|
|
136
|
+
return isPure(path.node, path.scope, path)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function impure(paths: (NodePath | null | undefined)[]): boolean {
|
|
140
|
+
return paths.some((path) => path != null && path.node != null && !isPurePath(path))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Member targets evaluate their base and computed key before the right side.
|
|
144
|
+
function writeReferenceIsPure(target: NodePath): boolean {
|
|
145
|
+
if (target.isIdentifier()) {
|
|
146
|
+
return true
|
|
147
|
+
}
|
|
148
|
+
if (target.isMemberExpression()) {
|
|
149
|
+
return (
|
|
150
|
+
isPurePath(target.get('object') as NodePath) &&
|
|
151
|
+
(!target.node.computed || isPurePath(target.get('property') as NodePath))
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
return false
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Compound assignment reads the target. Members and unresolved names may invoke
|
|
158
|
+
// accessors.
|
|
159
|
+
function compoundTargetReadIsPure(target: NodePath): boolean {
|
|
160
|
+
return target.isIdentifier() && isPurePath(target)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function propertyIsPure(propPath: NodePath): boolean {
|
|
164
|
+
const prop = propPath.node
|
|
165
|
+
if (t.isSpreadElement(prop)) {
|
|
166
|
+
return false
|
|
167
|
+
}
|
|
168
|
+
if (t.isObjectMethod(prop)) {
|
|
169
|
+
return !prop.computed || isPurePath(propPath.get('key') as NodePath)
|
|
170
|
+
}
|
|
171
|
+
if (t.isObjectProperty(prop)) {
|
|
172
|
+
if (prop.computed && !isPurePath(propPath.get('key') as NodePath)) {
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
return t.isExpression(prop.value) && isPurePath(propPath.get('value') as NodePath)
|
|
176
|
+
}
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as t from '@babel/types'
|
|
2
|
+
|
|
3
|
+
const MAX_STRING_LITERAL_LENGTH = 4096
|
|
4
|
+
|
|
5
|
+
// RegExp literals allocate mutable objects and are not primitive values.
|
|
6
|
+
export function isPrimitiveLiteral(node: t.Node): boolean {
|
|
7
|
+
return (
|
|
8
|
+
t.isStringLiteral(node) ||
|
|
9
|
+
t.isNumericLiteral(node) ||
|
|
10
|
+
t.isBooleanLiteral(node) ||
|
|
11
|
+
t.isNullLiteral(node) ||
|
|
12
|
+
t.isBigIntLiteral(node)
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// `+bigint` is the only literal-shaped unary form that throws.
|
|
17
|
+
export function isLiteralShaped(node: t.Node): boolean {
|
|
18
|
+
return literalShapeKind(node) !== null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Unary minus and bitwise not preserve BigInt; unary plus throws on it.
|
|
22
|
+
function literalShapeKind(node: t.Node): 'bigint' | 'other' | null {
|
|
23
|
+
if (t.isBigIntLiteral(node)) {
|
|
24
|
+
return 'bigint'
|
|
25
|
+
}
|
|
26
|
+
if (isPrimitiveLiteral(node) || t.isRegExpLiteral(node)) {
|
|
27
|
+
return 'other'
|
|
28
|
+
}
|
|
29
|
+
if (t.isUnaryExpression(node)) {
|
|
30
|
+
const inner = literalShapeKind(node.argument)
|
|
31
|
+
if (inner === null) {
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
if (node.operator === '!') {
|
|
35
|
+
return 'other'
|
|
36
|
+
}
|
|
37
|
+
if (node.operator === '+') {
|
|
38
|
+
return inner === 'bigint' ? null : 'other'
|
|
39
|
+
}
|
|
40
|
+
if (node.operator === '-' || node.operator === '~') {
|
|
41
|
+
return inner
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function valueToLiteral(value: unknown): t.Expression | null {
|
|
48
|
+
if (typeof value === 'string') {
|
|
49
|
+
if (value.length > MAX_STRING_LITERAL_LENGTH) {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
return t.stringLiteral(value)
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === 'boolean') {
|
|
55
|
+
return t.booleanLiteral(value)
|
|
56
|
+
}
|
|
57
|
+
if (value === null) {
|
|
58
|
+
return t.nullLiteral()
|
|
59
|
+
}
|
|
60
|
+
if (value === undefined) {
|
|
61
|
+
// `void 0` cannot be shadowed like the identifier `undefined`.
|
|
62
|
+
return t.unaryExpression('void', t.numericLiteral(0))
|
|
63
|
+
}
|
|
64
|
+
if (typeof value === 'bigint') {
|
|
65
|
+
// A UnaryExpression lets the generator parenthesize negative BigInt where
|
|
66
|
+
// precedence requires it.
|
|
67
|
+
if (value < 0n) {
|
|
68
|
+
return t.unaryExpression('-', t.bigIntLiteral(-value))
|
|
69
|
+
}
|
|
70
|
+
return t.bigIntLiteral(value)
|
|
71
|
+
}
|
|
72
|
+
if (typeof value === 'number') {
|
|
73
|
+
if (Number.isNaN(value) || !Number.isFinite(value)) {
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
if (Object.is(value, -0)) {
|
|
77
|
+
return t.unaryExpression('-', t.numericLiteral(0))
|
|
78
|
+
}
|
|
79
|
+
if (value < 0) {
|
|
80
|
+
return t.unaryExpression('-', t.numericLiteral(-value))
|
|
81
|
+
}
|
|
82
|
+
return t.numericLiteral(value)
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(value)) {
|
|
85
|
+
const elements: t.Expression[] = []
|
|
86
|
+
for (const item of value) {
|
|
87
|
+
const lit = valueToLiteral(item)
|
|
88
|
+
if (!lit) {
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
elements.push(lit)
|
|
92
|
+
}
|
|
93
|
+
return t.arrayExpression(elements)
|
|
94
|
+
}
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { NodePath } from '@babel/traverse'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
|
|
4
|
+
import { isSideEffectFree } from 'src/analysis/purity'
|
|
5
|
+
import { isStandaloneDeclaration } from 'src/utils/paths'
|
|
6
|
+
|
|
7
|
+
// Lowers a short-circuit expression assigned to a bare identifier:
|
|
8
|
+
//
|
|
9
|
+
// x = L;
|
|
10
|
+
// if (<fallbackTest(x)>) x = R;
|
|
11
|
+
//
|
|
12
|
+
// `fallbackTest` selects `!x`, `x`, or `x == null`. The parent must accept a
|
|
13
|
+
// multi-statement replacement.
|
|
14
|
+
export function lowerLogicalWriteStatement(
|
|
15
|
+
path: NodePath<t.LogicalExpression>,
|
|
16
|
+
fallbackTest: (target: t.Identifier) => t.Expression,
|
|
17
|
+
): boolean {
|
|
18
|
+
const { node } = path
|
|
19
|
+
const parent = path.parentPath
|
|
20
|
+
if (!parent) {
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
parent.isVariableDeclarator() &&
|
|
26
|
+
parent.node.init === node &&
|
|
27
|
+
t.isIdentifier(parent.node.id)
|
|
28
|
+
) {
|
|
29
|
+
const declStmt = parent.parentPath
|
|
30
|
+
if (
|
|
31
|
+
!declStmt?.isVariableDeclaration() ||
|
|
32
|
+
// The split would emit illegal `const x;` and then write it.
|
|
33
|
+
declStmt.node.kind === 'const' ||
|
|
34
|
+
declStmt.node.declarations.length !== 1 ||
|
|
35
|
+
!isStandaloneDeclaration(declStmt)
|
|
36
|
+
) {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
const target = parent.node.id
|
|
40
|
+
// The split writes `x` before evaluating the right side. Refuse any right
|
|
41
|
+
// side that could observe the new value or the ended TDZ.
|
|
42
|
+
if (rightCanObserveTarget(node.right, target.name)) {
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
declStmt.replaceWithMultiple([
|
|
46
|
+
t.variableDeclaration(declStmt.node.kind, [
|
|
47
|
+
t.variableDeclarator(t.cloneNode(target), node.left),
|
|
48
|
+
]),
|
|
49
|
+
guardedReassignment(target, fallbackTest, node.right),
|
|
50
|
+
])
|
|
51
|
+
return true
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
parent.isAssignmentExpression() &&
|
|
56
|
+
parent.node.operator === '=' &&
|
|
57
|
+
parent.node.right === node &&
|
|
58
|
+
t.isIdentifier(parent.node.left)
|
|
59
|
+
) {
|
|
60
|
+
const exprStmt = parent.parentPath
|
|
61
|
+
if (!exprStmt?.isExpressionStatement() || exprStmt.node.expression !== parent.node) {
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
const target = parent.node.left
|
|
65
|
+
// An unresolved name may be a global accessor. The split adds a getter read
|
|
66
|
+
// and can add a second setter call.
|
|
67
|
+
if (!path.scope.getBinding(target.name)) {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
// Writing first would change `x = 0 || x` before the right side reads `x`.
|
|
71
|
+
if (
|
|
72
|
+
!isSideEffectFree(node.right, path.scope, path) ||
|
|
73
|
+
referencesName(node.right, target.name)
|
|
74
|
+
) {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
exprStmt.replaceWithMultiple([
|
|
78
|
+
t.expressionStatement(t.assignmentExpression('=', t.cloneNode(target), node.left)),
|
|
79
|
+
guardedReassignment(target, fallbackTest, node.right),
|
|
80
|
+
])
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Calls, getters, and suspension can observe the target through code outside the
|
|
88
|
+
// expression, even when the target name does not appear directly.
|
|
89
|
+
function rightCanObserveTarget(rightNode: t.Node, targetName: string): boolean {
|
|
90
|
+
let observes = false
|
|
91
|
+
t.traverseFast(rightNode, (node) => {
|
|
92
|
+
if (
|
|
93
|
+
(t.isIdentifier(node) && node.name === targetName) ||
|
|
94
|
+
t.isCallExpression(node) ||
|
|
95
|
+
t.isOptionalCallExpression(node) ||
|
|
96
|
+
t.isNewExpression(node) ||
|
|
97
|
+
t.isTaggedTemplateExpression(node) ||
|
|
98
|
+
t.isMemberExpression(node) ||
|
|
99
|
+
t.isOptionalMemberExpression(node) ||
|
|
100
|
+
t.isAwaitExpression(node) ||
|
|
101
|
+
t.isYieldExpression(node)
|
|
102
|
+
) {
|
|
103
|
+
observes = true
|
|
104
|
+
}
|
|
105
|
+
return observes ? t.traverseFast.stop : undefined
|
|
106
|
+
})
|
|
107
|
+
return observes
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function referencesName(node: t.Node, name: string): boolean {
|
|
111
|
+
let found = false
|
|
112
|
+
t.traverseFast(node, (child) => {
|
|
113
|
+
if (!found && t.isIdentifier(child) && child.name === name) {
|
|
114
|
+
found = true
|
|
115
|
+
}
|
|
116
|
+
return found ? t.traverseFast.skip : undefined
|
|
117
|
+
})
|
|
118
|
+
return found
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function guardedReassignment(
|
|
122
|
+
target: t.Identifier,
|
|
123
|
+
fallbackTest: (target: t.Identifier) => t.Expression,
|
|
124
|
+
value: t.Expression,
|
|
125
|
+
): t.IfStatement {
|
|
126
|
+
return t.ifStatement(
|
|
127
|
+
fallbackTest(t.cloneNode(target)),
|
|
128
|
+
t.blockStatement([
|
|
129
|
+
t.expressionStatement(t.assignmentExpression('=', t.cloneNode(target), value)),
|
|
130
|
+
]),
|
|
131
|
+
)
|
|
132
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { NodePath, Visitor } from '@babel/traverse'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Collects labels whose body is this loop. Their `continue` statements must be
|
|
6
|
+
* redirected after lowering. A hoisted initializer belongs outside the
|
|
7
|
+
* outermost label.
|
|
8
|
+
*/
|
|
9
|
+
export function collectLoopLabels(path: NodePath<t.Statement>): {
|
|
10
|
+
outermost: NodePath<t.Statement>
|
|
11
|
+
ourLabels: Set<string>
|
|
12
|
+
} {
|
|
13
|
+
const ourLabels = new Set<string>()
|
|
14
|
+
let outermost: NodePath<t.Statement> = path
|
|
15
|
+
for (;;) {
|
|
16
|
+
const parent = outermost.parentPath
|
|
17
|
+
if (!parent?.isLabeledStatement() || parent.node.body !== outermost.node) {
|
|
18
|
+
break
|
|
19
|
+
}
|
|
20
|
+
outermost = parent
|
|
21
|
+
ourLabels.add(parent.node.label.name)
|
|
22
|
+
}
|
|
23
|
+
return { outermost, ourLabels }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Generates an inner-block label after reserving labels above and inside the
|
|
28
|
+
* loop. `scope.generateUid` alone can miss an enclosing user label.
|
|
29
|
+
*/
|
|
30
|
+
export function generateLoopLabel(path: NodePath<t.Statement>, hint: string): string {
|
|
31
|
+
const taken = new Set<string>()
|
|
32
|
+
let ancestor: NodePath | null = path.parentPath
|
|
33
|
+
while (ancestor) {
|
|
34
|
+
if (ancestor.isLabeledStatement()) {
|
|
35
|
+
taken.add(ancestor.node.label.name)
|
|
36
|
+
}
|
|
37
|
+
ancestor = ancestor.parentPath
|
|
38
|
+
}
|
|
39
|
+
path.traverse({
|
|
40
|
+
LabeledStatement(inner) {
|
|
41
|
+
taken.add(inner.node.label.name)
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
// `generateUid` supplies its own counter, even after stripping hint digits.
|
|
45
|
+
let name = path.scope.generateUid(hint)
|
|
46
|
+
while (taken.has(name)) {
|
|
47
|
+
name = path.scope.generateUid(hint)
|
|
48
|
+
}
|
|
49
|
+
return name
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Redirects this loop's `continue` statements to the lowered inner block.
|
|
54
|
+
* Nested loops, other labels, and nested functions stay untouched. The count
|
|
55
|
+
* lets callers omit an unused label.
|
|
56
|
+
*/
|
|
57
|
+
export function redirectContinues(
|
|
58
|
+
rootPath: NodePath,
|
|
59
|
+
innerLabel: string,
|
|
60
|
+
ourLabels: Set<string>,
|
|
61
|
+
): number {
|
|
62
|
+
const state: RedirectState = { count: 0, innerLabel, ourLabels, rootPath }
|
|
63
|
+
rootPath.traverse(redirectVisitor, state)
|
|
64
|
+
return state.count
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface RedirectState {
|
|
68
|
+
count: number
|
|
69
|
+
innerLabel: string
|
|
70
|
+
ourLabels: Set<string>
|
|
71
|
+
rootPath: NodePath
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const redirectVisitor: Visitor<RedirectState> = {
|
|
75
|
+
Function(path) {
|
|
76
|
+
path.skip()
|
|
77
|
+
},
|
|
78
|
+
ContinueStatement(path, state) {
|
|
79
|
+
const { label } = path.node
|
|
80
|
+
if (!label) {
|
|
81
|
+
if (!hasInterposedLoop(path, state.rootPath)) {
|
|
82
|
+
path.replaceWith(t.breakStatement(t.identifier(state.innerLabel)))
|
|
83
|
+
state.count++
|
|
84
|
+
}
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
if (state.ourLabels.has(label.name)) {
|
|
88
|
+
path.replaceWith(t.breakStatement(t.identifier(state.innerLabel)))
|
|
89
|
+
state.count++
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function hasInterposedLoop(path: NodePath, rootPath: NodePath): boolean {
|
|
95
|
+
let current: NodePath | null = path.parentPath
|
|
96
|
+
while (current && current !== rootPath) {
|
|
97
|
+
if (
|
|
98
|
+
current.isForStatement() ||
|
|
99
|
+
current.isForInStatement() ||
|
|
100
|
+
current.isForOfStatement() ||
|
|
101
|
+
current.isWhileStatement() ||
|
|
102
|
+
current.isDoWhileStatement()
|
|
103
|
+
) {
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
current = current.parentPath
|
|
107
|
+
}
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { NodePath } from '@babel/traverse'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
|
|
4
|
+
// Only statement-list positions accept several replacements. Loop heads do not.
|
|
5
|
+
export function isStandaloneDeclaration(declPath: NodePath<t.VariableDeclaration>): boolean {
|
|
6
|
+
const owner = declPath.parentPath
|
|
7
|
+
if (!owner) {
|
|
8
|
+
return false
|
|
9
|
+
}
|
|
10
|
+
if (owner.isForStatement() && owner.node.init === declPath.node) {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
if (owner.isForInStatement() && owner.node.left === declPath.node) {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
if (owner.isForOfStatement() && owner.node.left === declPath.node) {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
return owner.isBlock() || owner.isSwitchCase()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Babel treats destructured writes as binding identifiers. Labels must still be
|
|
23
|
+
// excluded because they bind no variable.
|
|
24
|
+
export function referencesOrWritesVariable(path: NodePath<t.Identifier>): boolean {
|
|
25
|
+
if (path.parentPath?.isLabeledStatement()) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
// Separate calls avoid an incorrect `never` narrowing across `||`.
|
|
29
|
+
const isRead = path.isReferencedIdentifier()
|
|
30
|
+
const isWrite = path.isBindingIdentifier()
|
|
31
|
+
return isRead || isWrite
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Replacing a callee or tag member with a bare value would lose its `this`.
|
|
35
|
+
export function isCalleeOrTagOf(path: NodePath): boolean {
|
|
36
|
+
const parent = path.parentPath
|
|
37
|
+
if (!parent) {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
if (
|
|
41
|
+
(parent.isCallExpression() || parent.isOptionalCallExpression()) &&
|
|
42
|
+
parent.node.callee === path.node
|
|
43
|
+
) {
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
return parent.isTaggedTemplateExpression() && parent.node.tag === path.node
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Strictness comes from modules, classes, or an enclosing directive prologue.
|
|
50
|
+
export function isInStrictContext(path: NodePath): boolean {
|
|
51
|
+
for (let current: NodePath | null = path; current; current = current.parentPath) {
|
|
52
|
+
if (current.isClass()) {
|
|
53
|
+
return true
|
|
54
|
+
}
|
|
55
|
+
if (current.isProgram()) {
|
|
56
|
+
return current.node.sourceType === 'module' || hasUseStrictDirective(current.node.directives)
|
|
57
|
+
}
|
|
58
|
+
if (
|
|
59
|
+
current.isFunction() &&
|
|
60
|
+
t.isBlockStatement(current.node.body) &&
|
|
61
|
+
hasUseStrictDirective(current.node.body.directives)
|
|
62
|
+
) {
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function hasUseStrictDirective(directives: readonly t.Directive[]): boolean {
|
|
70
|
+
return directives.some((directive) => directive.value.value === 'use strict')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ECMA-262 B.3.3 gives sloppy block-level functions a `var` alias that Babel
|
|
74
|
+
// omits from the enclosing scope. Renaming, moving, or removing one can expose it.
|
|
75
|
+
export function hasAnnexBFunctionAlias(path: NodePath): boolean {
|
|
76
|
+
if (!path.isFunctionDeclaration()) {
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
const parent = path.parentPath
|
|
80
|
+
if (!parent || parent.isProgram()) {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
if (
|
|
84
|
+
parent.isBlockStatement() &&
|
|
85
|
+
parent.key === 'body' &&
|
|
86
|
+
parent.parentPath != null &&
|
|
87
|
+
parent.parentPath.isFunction()
|
|
88
|
+
) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
return !isInStrictContext(parent)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// A later write to `const` must keep throwing after transformation.
|
|
95
|
+
export function hasConstantViolation(path: NodePath<t.VariableDeclaration>): boolean {
|
|
96
|
+
for (const name of Object.keys(path.getBindingIdentifiers())) {
|
|
97
|
+
const binding = path.scope.getBinding(name)
|
|
98
|
+
if (binding && binding.constantViolations.length > 0) {
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Mulberry32 is deterministic per seed and is not cryptographic.
|
|
2
|
+
export function mulberry32(seed: number): () => number {
|
|
3
|
+
let state = seed >>> 0
|
|
4
|
+
return () => {
|
|
5
|
+
state = (state + 0x6d_2b_79_f5) >>> 0
|
|
6
|
+
let t = state
|
|
7
|
+
t = Math.imul(t ^ (t >>> 15), t | 1)
|
|
8
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
|
|
9
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4_294_967_296
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Fisher-Yates shuffle, in place.
|
|
14
|
+
export function shuffleInPlace<T>(items: T[], rng: () => number): T[] {
|
|
15
|
+
for (let index = items.length - 1; index > 0; index--) {
|
|
16
|
+
const pick = Math.floor(rng() * (index + 1))
|
|
17
|
+
const swapped = items[index]!
|
|
18
|
+
items[index] = items[pick]!
|
|
19
|
+
items[pick] = swapped
|
|
20
|
+
}
|
|
21
|
+
return items
|
|
22
|
+
}
|