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,289 @@
|
|
|
1
|
+
import traverse from '@babel/traverse'
|
|
2
|
+
import type { Binding, Scope } from '@babel/traverse'
|
|
3
|
+
import * as t from '@babel/types'
|
|
4
|
+
import type { File } from '@babel/types'
|
|
5
|
+
|
|
6
|
+
import type { TransformContext } from 'src/options'
|
|
7
|
+
import { isDirectEvalCall, isInsideWith } from 'src/utils/ast'
|
|
8
|
+
import { hasAnnexBFunctionAlias, referencesOrWritesVariable } from 'src/utils/paths'
|
|
9
|
+
import { mulberry32 } from 'src/utils/random'
|
|
10
|
+
import { StringGenerator } from 'src/utils/string-generator'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Renames bindings and labels with a fresh `StringGenerator`. Each scope avoids
|
|
14
|
+
* live ancestor names but otherwise restarts the cursor, allowing safe reuse in
|
|
15
|
+
* sibling scopes.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // ◀️ before
|
|
19
|
+
* function compute(value, multiplier) {
|
|
20
|
+
* var scaled = value * multiplier;
|
|
21
|
+
* return scaled;
|
|
22
|
+
* }
|
|
23
|
+
* compute(3, 4);
|
|
24
|
+
*
|
|
25
|
+
* // ▶️ after
|
|
26
|
+
* function a(b, c) {
|
|
27
|
+
* var d = b * c;
|
|
28
|
+
* return d;
|
|
29
|
+
* }
|
|
30
|
+
* a(3, 4);
|
|
31
|
+
*/
|
|
32
|
+
export function renameIdentifiers(ast: File, ctx: TransformContext): boolean {
|
|
33
|
+
const generator = new StringGenerator(ctx.stringGeneratorMode, mulberry32(ctx.seed))
|
|
34
|
+
|
|
35
|
+
let programScope: Scope | undefined
|
|
36
|
+
traverse(ast, {
|
|
37
|
+
Program(path) {
|
|
38
|
+
programScope = path.scope
|
|
39
|
+
path.stop()
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
if (!programScope) {
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
programScope.crawl()
|
|
46
|
+
|
|
47
|
+
const liveAncestors = new Map<Scope, Set<Binding>>()
|
|
48
|
+
// Reserve unbound names so generated bindings cannot capture them.
|
|
49
|
+
const freeNames = new Set<string>()
|
|
50
|
+
// `with` and direct eval pin bindings to their source names.
|
|
51
|
+
const unrenamable = new Set<Binding>()
|
|
52
|
+
// Reserve pinned source names globally, including before their binding is seen.
|
|
53
|
+
const pinnedNames = new Set<string>()
|
|
54
|
+
const pin = (binding: Binding, name: string): void => {
|
|
55
|
+
unrenamable.add(binding)
|
|
56
|
+
pinnedNames.add(name)
|
|
57
|
+
}
|
|
58
|
+
traverse(ast, {
|
|
59
|
+
Identifier(path) {
|
|
60
|
+
if (isNonBindingPosition(path)) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
const binding = path.scope.getBinding(path.node.name)
|
|
64
|
+
if (!binding) {
|
|
65
|
+
// Assignment-only globals are free names even without a read reference.
|
|
66
|
+
if (referencesOrWritesVariable(path)) {
|
|
67
|
+
freeNames.add(path.node.name)
|
|
68
|
+
}
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
if (isInsideWith(path)) {
|
|
72
|
+
pin(binding, path.node.name)
|
|
73
|
+
}
|
|
74
|
+
let scope: Scope | undefined = path.scope
|
|
75
|
+
while (scope && scope !== binding.scope) {
|
|
76
|
+
let live = liveAncestors.get(scope)
|
|
77
|
+
if (!live) {
|
|
78
|
+
live = new Set()
|
|
79
|
+
liveAncestors.set(scope, live)
|
|
80
|
+
}
|
|
81
|
+
live.add(binding)
|
|
82
|
+
scope = scope.parent
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
CallExpression(path) {
|
|
86
|
+
// Direct eval can read every visible binding by its source name.
|
|
87
|
+
if (!isDirectEvalCall(path.node)) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
let scope: Scope | undefined = path.scope
|
|
91
|
+
while (scope) {
|
|
92
|
+
for (const name of Object.keys(scope.bindings)) {
|
|
93
|
+
pin(scope.bindings[name]!, name)
|
|
94
|
+
}
|
|
95
|
+
scope = scope.parent
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
FunctionDeclaration(path) {
|
|
99
|
+
// ECMA-262 B.3.3 gives sloppy block functions an alias Babel does not model.
|
|
100
|
+
if (!hasAnnexBFunctionAlias(path)) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
const name = path.node.id?.name
|
|
104
|
+
const binding = name === undefined ? undefined : path.scope.getBinding(name)
|
|
105
|
+
if (binding && name !== undefined) {
|
|
106
|
+
pin(binding, name)
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
CatchClause(path) {
|
|
110
|
+
// Babel splits a catch parameter and same-named `var` that share one runtime
|
|
111
|
+
// variable. They must keep the same name.
|
|
112
|
+
const fnScope = path.scope.getFunctionParent() ?? path.scope.getProgramParent()
|
|
113
|
+
for (const name of Object.keys(path.scope.bindings)) {
|
|
114
|
+
const varBinding = fnScope.getOwnBinding(name)
|
|
115
|
+
if (varBinding) {
|
|
116
|
+
pin(path.scope.bindings[name]!, name)
|
|
117
|
+
pin(varBinding, name)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const bindingToName = new Map<Binding, string>()
|
|
124
|
+
const idToName = new WeakMap<t.Identifier, string>()
|
|
125
|
+
|
|
126
|
+
traverse(ast, {
|
|
127
|
+
Scopable(path) {
|
|
128
|
+
const { scope } = path
|
|
129
|
+
// Test global reservations during allocation to avoid copying them per scope.
|
|
130
|
+
const offLimits = new Set<string>()
|
|
131
|
+
const live = liveAncestors.get(scope)
|
|
132
|
+
if (live) {
|
|
133
|
+
for (const ancestor of live) {
|
|
134
|
+
const name = bindingToName.get(ancestor)
|
|
135
|
+
if (name !== undefined) {
|
|
136
|
+
offLimits.add(name)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let cursor = 0
|
|
142
|
+
for (const oldName of Object.keys(scope.bindings)) {
|
|
143
|
+
const binding = scope.bindings[oldName]!
|
|
144
|
+
if (!t.isIdentifier(binding.identifier)) {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
// `pin` already reserved this binding's source name.
|
|
148
|
+
if (unrenamable.has(binding)) {
|
|
149
|
+
continue
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const inherited = idToName.get(binding.identifier)
|
|
153
|
+
if (inherited !== undefined) {
|
|
154
|
+
bindingToName.set(binding, inherited)
|
|
155
|
+
offLimits.add(inherited)
|
|
156
|
+
continue
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let newName: string
|
|
160
|
+
do {
|
|
161
|
+
newName = generator.at(cursor++)
|
|
162
|
+
} while (offLimits.has(newName) || freeNames.has(newName) || pinnedNames.has(newName))
|
|
163
|
+
|
|
164
|
+
offLimits.add(newName)
|
|
165
|
+
bindingToName.set(binding, newName)
|
|
166
|
+
idToName.set(binding.identifier, newName)
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
if (bindingToName.size === 0) {
|
|
172
|
+
return renameLabels(ast, generator)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
traverse(ast, {
|
|
176
|
+
Identifier(path) {
|
|
177
|
+
const { node } = path
|
|
178
|
+
const directName = idToName.get(node)
|
|
179
|
+
if (directName !== undefined) {
|
|
180
|
+
node.name = directName
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
if (isNonBindingPosition(path)) {
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
const binding = path.scope.getBinding(node.name)
|
|
187
|
+
if (!binding) {
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
const newName = bindingToName.get(binding)
|
|
191
|
+
if (newName === undefined) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
node.name = newName
|
|
195
|
+
},
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
renameLabels(ast, generator)
|
|
199
|
+
return true
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function isNonBindingPosition(path: { node: t.Identifier; parent: t.Node }): boolean {
|
|
203
|
+
const { node, parent } = path
|
|
204
|
+
// A PrivateName id is not a variable and must match its class declaration.
|
|
205
|
+
if (t.isPrivateName(parent)) {
|
|
206
|
+
return true
|
|
207
|
+
}
|
|
208
|
+
if (t.isMemberExpression(parent) && parent.property === node && !parent.computed) {
|
|
209
|
+
return true
|
|
210
|
+
}
|
|
211
|
+
if (t.isOptionalMemberExpression(parent) && parent.property === node && !parent.computed) {
|
|
212
|
+
return true
|
|
213
|
+
}
|
|
214
|
+
if (
|
|
215
|
+
(t.isObjectProperty(parent) || t.isObjectMethod(parent)) &&
|
|
216
|
+
parent.key === node &&
|
|
217
|
+
!parent.computed
|
|
218
|
+
) {
|
|
219
|
+
return true
|
|
220
|
+
}
|
|
221
|
+
if (
|
|
222
|
+
(t.isClassMethod(parent) || t.isClassProperty(parent) || t.isClassPrivateProperty(parent)) &&
|
|
223
|
+
'key' in parent &&
|
|
224
|
+
parent.key === node &&
|
|
225
|
+
!('computed' in parent && parent.computed)
|
|
226
|
+
) {
|
|
227
|
+
return true
|
|
228
|
+
}
|
|
229
|
+
// External import and export names are part of the module contract.
|
|
230
|
+
if (t.isImportSpecifier(parent) && parent.imported === node) {
|
|
231
|
+
return true
|
|
232
|
+
}
|
|
233
|
+
if (t.isExportSpecifier(parent) && parent.exported === node) {
|
|
234
|
+
return true
|
|
235
|
+
}
|
|
236
|
+
if (t.isLabeledStatement(parent) && parent.label === node) {
|
|
237
|
+
return true
|
|
238
|
+
}
|
|
239
|
+
if (t.isBreakStatement(parent) && parent.label === node) {
|
|
240
|
+
return true
|
|
241
|
+
}
|
|
242
|
+
if (t.isContinueStatement(parent) && parent.label === node) {
|
|
243
|
+
return true
|
|
244
|
+
}
|
|
245
|
+
if (t.isMetaProperty(parent)) {
|
|
246
|
+
return true
|
|
247
|
+
}
|
|
248
|
+
return false
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function renameLabels(ast: File, generator: StringGenerator): boolean {
|
|
252
|
+
const labelRenames: { node: t.LabeledStatement; oldName: string; newName: string }[] = []
|
|
253
|
+
traverse(ast, {
|
|
254
|
+
LabeledStatement(path) {
|
|
255
|
+
labelRenames.push({
|
|
256
|
+
node: path.node,
|
|
257
|
+
oldName: path.node.label.name,
|
|
258
|
+
newName: generator.next(),
|
|
259
|
+
})
|
|
260
|
+
},
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
if (labelRenames.length === 0) {
|
|
264
|
+
return false
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for (const { node, oldName, newName } of labelRenames) {
|
|
268
|
+
node.label.name = newName
|
|
269
|
+
rewriteLabelReferences(node, oldName, newName)
|
|
270
|
+
}
|
|
271
|
+
return true
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// A same-named inner label owns its references and is renamed separately.
|
|
275
|
+
function rewriteLabelReferences(node: t.LabeledStatement, oldName: string, newName: string): void {
|
|
276
|
+
t.traverseFast(node.body, (visited) => {
|
|
277
|
+
const shadowedByInnerLabel =
|
|
278
|
+
t.isLabeledStatement(visited) && visited !== node && visited.label.name === oldName
|
|
279
|
+
if (!shadowedByInnerLabel) {
|
|
280
|
+
if (t.isBreakStatement(visited) && visited.label?.name === oldName) {
|
|
281
|
+
visited.label.name = newName
|
|
282
|
+
}
|
|
283
|
+
if (t.isContinueStatement(visited) && visited.label?.name === oldName) {
|
|
284
|
+
visited.label.name = newName
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return shadowedByInnerLabel ? t.traverseFast.skip : undefined
|
|
288
|
+
})
|
|
289
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import traverse from '@babel/traverse'
|
|
2
|
+
import type { NodePath, Visitor } from '@babel/traverse'
|
|
3
|
+
import * as t from '@babel/types'
|
|
4
|
+
import type { File } from '@babel/types'
|
|
5
|
+
|
|
6
|
+
import type { TransformContext } from 'src/options'
|
|
7
|
+
import { enclosingScopeHasDirectEval, isInsideWith } from 'src/utils/ast'
|
|
8
|
+
import type { ChangeState } from 'src/utils/change-tracking'
|
|
9
|
+
import { mulberry32 } from 'src/utils/random'
|
|
10
|
+
import { StringGenerator } from 'src/utils/string-generator'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Rewrites five constant-like values through strings for later string mangling:
|
|
14
|
+
*
|
|
15
|
+
* - `true` -> `!!"<random>"`
|
|
16
|
+
* - `false` -> `!"<random>"`
|
|
17
|
+
* - `NaN` -> `+"<random>"`
|
|
18
|
+
* - `undefined`-> `void "<random>"`
|
|
19
|
+
* - `Infinity` -> `1/!"<random>"`
|
|
20
|
+
*
|
|
21
|
+
* Generated strings are nonempty and nonnumeric, fixing their truthiness and
|
|
22
|
+
* numeric coercion.
|
|
23
|
+
*
|
|
24
|
+
* `NaN`, `Infinity`, and `undefined` require genuine global reads. Delete and
|
|
25
|
+
* write positions stay unchanged because the replacement is not a reference.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // ◀️ before
|
|
29
|
+
* if (x === undefined) return NaN;
|
|
30
|
+
* var lim = !done ? Infinity : 0;
|
|
31
|
+
* var flag = !x ? true : false;
|
|
32
|
+
*
|
|
33
|
+
* // ▶️ after
|
|
34
|
+
* if (x === void "ab") return +"cd";
|
|
35
|
+
* var lim = !done ? 1 / !"ef" : 0;
|
|
36
|
+
* var flag = !x ? !!"gh" : !"ij";
|
|
37
|
+
*/
|
|
38
|
+
export function specialsToStrings(ast: File, ctx: TransformContext): boolean {
|
|
39
|
+
const state: State = {
|
|
40
|
+
changed: false,
|
|
41
|
+
generator: new StringGenerator(ctx.stringGeneratorMode, mulberry32(ctx.seed)),
|
|
42
|
+
}
|
|
43
|
+
traverse(ast, visitor, undefined, state)
|
|
44
|
+
return state.changed
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface State extends ChangeState {
|
|
48
|
+
generator: StringGenerator
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const GLOBAL_SPECIALS = new Set(['NaN', 'Infinity', 'undefined'])
|
|
52
|
+
|
|
53
|
+
const visitor: Visitor<State> = {
|
|
54
|
+
BooleanLiteral(path, state) {
|
|
55
|
+
const str = t.stringLiteral(state.generator.next())
|
|
56
|
+
const negated = t.unaryExpression('!', str)
|
|
57
|
+
path.replaceWith(path.node.value ? t.unaryExpression('!', negated) : negated)
|
|
58
|
+
path.skip()
|
|
59
|
+
state.changed = true
|
|
60
|
+
},
|
|
61
|
+
Identifier(path, state) {
|
|
62
|
+
const { name } = path.node
|
|
63
|
+
if (!GLOBAL_SPECIALS.has(name)) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
if (!path.isReferencedIdentifier()) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
if (path.scope.getBinding(name)) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
// Replacements are not write targets. `with` and eval can also shadow globals.
|
|
73
|
+
if (isWriteTarget(path) || isInsideWith(path) || enclosingScopeHasDirectEval(path)) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
const str = t.stringLiteral(state.generator.next())
|
|
77
|
+
path.replaceWith(buildGlobalReplacement(name, str))
|
|
78
|
+
path.skip()
|
|
79
|
+
state.changed = true
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function buildGlobalReplacement(name: string, str: t.StringLiteral): t.Expression {
|
|
84
|
+
if (name === 'undefined') {
|
|
85
|
+
return t.unaryExpression('void', str)
|
|
86
|
+
}
|
|
87
|
+
if (name === 'NaN') {
|
|
88
|
+
return t.unaryExpression('+', str)
|
|
89
|
+
}
|
|
90
|
+
return t.binaryExpression('/', t.numericLiteral(1), t.unaryExpression('!', str))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Babel calls update and iterator targets references, but replacements there
|
|
94
|
+
// would not be assignable. Delete also distinguishes references from values.
|
|
95
|
+
function isWriteTarget(path: NodePath<t.Identifier>): boolean {
|
|
96
|
+
const { parentPath } = path
|
|
97
|
+
if (!parentPath) {
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
if (parentPath.isUpdateExpression({ argument: path.node })) {
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
if (parentPath.isAssignmentExpression({ left: path.node })) {
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
if (parentPath.isUnaryExpression({ argument: path.node, operator: 'delete' })) {
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
if (
|
|
110
|
+
(parentPath.isForInStatement() || parentPath.isForOfStatement()) &&
|
|
111
|
+
parentPath.node.left === path.node
|
|
112
|
+
) {
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
return false
|
|
116
|
+
}
|