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,404 @@
|
|
|
1
|
+
import type { Binding, NodePath, Visitor } from '@babel/traverse'
|
|
2
|
+
import * as t from '@babel/types'
|
|
3
|
+
import type { File } from '@babel/types'
|
|
4
|
+
|
|
5
|
+
import { initializerReaches } from 'src/analysis/document-order'
|
|
6
|
+
import { isInsideWith, isProtoKey } from 'src/utils/ast'
|
|
7
|
+
import { traverseForChanges } from 'src/utils/change-tracking'
|
|
8
|
+
import type { ChangeState } from 'src/utils/change-tracking'
|
|
9
|
+
import { isCalleeOrTagOf } from 'src/utils/paths'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Replaces a constant object or array wrapper with one binding per entry when
|
|
13
|
+
* every reference is a static-key read. The inliner can then see function
|
|
14
|
+
* values directly.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // ◀️ before
|
|
18
|
+
* var ops = {
|
|
19
|
+
* double: function (x) { return x * 2; },
|
|
20
|
+
* negate: function (x) { return -x; },
|
|
21
|
+
* };
|
|
22
|
+
* log(ops.double(3), ops.negate(7));
|
|
23
|
+
*
|
|
24
|
+
* // ▶️ after
|
|
25
|
+
* var double = function (x) { return x * 2; };
|
|
26
|
+
* var negate = function (x) { return -x; };
|
|
27
|
+
* log(double(3), negate(7));
|
|
28
|
+
*/
|
|
29
|
+
export function extractObjectProperties(ast: File): boolean {
|
|
30
|
+
return traverseForChanges(ast, extractVisitor)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const extractVisitor: Visitor<ChangeState> = {
|
|
34
|
+
VariableDeclaration: {
|
|
35
|
+
exit(path, state) {
|
|
36
|
+
if (tryExtractDeclaration(path)) {
|
|
37
|
+
state.changed = true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function tryExtractDeclaration(path: NodePath<t.VariableDeclaration>): boolean {
|
|
44
|
+
if (path.node.declarations.length !== 1) {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
// Babel hides lexical replacements for a `for` initializer inside an IIFE.
|
|
48
|
+
if (path.key === 'init' && path.parentPath?.isForStatement() && path.node.kind !== 'var') {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
const [declaratorPath] = path.get('declarations')
|
|
52
|
+
if (!declaratorPath) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const idPath = declaratorPath.get('id')
|
|
57
|
+
if (!idPath.isIdentifier()) {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
const idName = idPath.node.name
|
|
61
|
+
|
|
62
|
+
const initPath = declaratorPath.get('init')
|
|
63
|
+
if (!initPath.node) {
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (initPath.isObjectExpression()) {
|
|
68
|
+
const entries = collectObjectEntries(initPath)
|
|
69
|
+
if (entries === null) {
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
return performExtraction(path, idName, initPath, entries, false)
|
|
73
|
+
}
|
|
74
|
+
if (initPath.isArrayExpression()) {
|
|
75
|
+
const entries = collectArrayEntries(initPath)
|
|
76
|
+
if (entries === null) {
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
return performExtraction(path, idName, initPath, entries, true)
|
|
80
|
+
}
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface Entry {
|
|
85
|
+
key: string
|
|
86
|
+
value: t.Expression
|
|
87
|
+
// Bare calls are safe only for arrows or functions that never read `this`.
|
|
88
|
+
callSafe: boolean
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function collectObjectEntries(initPath: NodePath<t.ObjectExpression>): Entry[] | null {
|
|
92
|
+
const entries: Entry[] = []
|
|
93
|
+
const seen = new Set<string>()
|
|
94
|
+
for (const propPath of initPath.get('properties')) {
|
|
95
|
+
if (!propPath.isObjectProperty()) {
|
|
96
|
+
// Object methods receive the literal as `this`.
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
// The `__proto__` setter form creates no own property to extract.
|
|
100
|
+
if (isProtoSetter(propPath.node)) {
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
const key = staticPropertyKey(propPath.node)
|
|
104
|
+
if (key === null) {
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
if (seen.has(key)) {
|
|
108
|
+
return null
|
|
109
|
+
}
|
|
110
|
+
seen.add(key)
|
|
111
|
+
|
|
112
|
+
const valuePath = propPath.get('value')
|
|
113
|
+
if (Array.isArray(valuePath)) {
|
|
114
|
+
return null
|
|
115
|
+
}
|
|
116
|
+
if (!valuePath.isExpression()) {
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
if (functionRefsThis(valuePath)) {
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
entries.push({ key, value: valuePath.node, callSafe: isCallSafeValue(valuePath.node) })
|
|
124
|
+
}
|
|
125
|
+
return entries
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isProtoSetter(node: t.ObjectProperty): boolean {
|
|
129
|
+
return !node.computed && !node.shorthand && isProtoKey(node.key)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function isCallSafeValue(node: t.Expression): boolean {
|
|
133
|
+
// Surviving function values are receiver-independent.
|
|
134
|
+
return t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function collectArrayEntries(initPath: NodePath<t.ArrayExpression>): Entry[] | null {
|
|
138
|
+
const entries: Entry[] = []
|
|
139
|
+
const elementPaths = initPath.get('elements')
|
|
140
|
+
for (let i = 0; i < elementPaths.length; i++) {
|
|
141
|
+
const elementPath = elementPaths[i]!
|
|
142
|
+
// An array hole has no value node to bind.
|
|
143
|
+
if (!elementPath.node) {
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
if (elementPath.isSpreadElement()) {
|
|
147
|
+
return null
|
|
148
|
+
}
|
|
149
|
+
if (!elementPath.isExpression()) {
|
|
150
|
+
return null
|
|
151
|
+
}
|
|
152
|
+
if (functionRefsThis(elementPath)) {
|
|
153
|
+
return null
|
|
154
|
+
}
|
|
155
|
+
entries.push({
|
|
156
|
+
key: String(i),
|
|
157
|
+
value: elementPath.node,
|
|
158
|
+
callSafe: isCallSafeValue(elementPath.node),
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
return entries
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function performExtraction(
|
|
165
|
+
path: NodePath<t.VariableDeclaration>,
|
|
166
|
+
idName: string,
|
|
167
|
+
initPath: NodePath<t.ObjectExpression | t.ArrayExpression>,
|
|
168
|
+
entries: Entry[],
|
|
169
|
+
isArray: boolean,
|
|
170
|
+
): boolean {
|
|
171
|
+
const binding = path.scope.getBinding(idName)
|
|
172
|
+
if (!binding) {
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
if (!binding.constant) {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// `with` can shadow a synthesized entry binding.
|
|
180
|
+
if (isInsideWith(path)) {
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (initContainsSelfReference(binding, initPath)) {
|
|
185
|
+
return false
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const callSafeByKey = new Map(entries.map((entry) => [entry.key, entry.callSafe]))
|
|
189
|
+
const validKeys = new Set(entries.map((entry) => entry.key))
|
|
190
|
+
|
|
191
|
+
interface Replacement {
|
|
192
|
+
memberPath: NodePath<t.MemberExpression>
|
|
193
|
+
key: string
|
|
194
|
+
}
|
|
195
|
+
const replacements: Replacement[] = []
|
|
196
|
+
const seenMembers = new Set<t.MemberExpression>()
|
|
197
|
+
|
|
198
|
+
for (const ref of binding.referencePaths) {
|
|
199
|
+
// Before initialization, a wrapper read throws while a bare binding may not.
|
|
200
|
+
if (!initializerReaches(path, ref)) {
|
|
201
|
+
return false
|
|
202
|
+
}
|
|
203
|
+
// `with` can redirect the rewritten reference.
|
|
204
|
+
if (isInsideWith(ref)) {
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const parent = ref.parentPath
|
|
209
|
+
if (!parent || !parent.isMemberExpression()) {
|
|
210
|
+
return false
|
|
211
|
+
}
|
|
212
|
+
if (parent.node.object !== ref.node) {
|
|
213
|
+
return false
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const key = staticMemberKey(parent.node)
|
|
217
|
+
if (key === null) {
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
220
|
+
if (!validKeys.has(key)) {
|
|
221
|
+
return false
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (isWriteTargetMember(parent)) {
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
// Extracting a method call drops its receiver.
|
|
228
|
+
if (isCalleeOrTagOf(parent) && !callSafeByKey.get(key)) {
|
|
229
|
+
return false
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Earlier extraction can publish the same member twice. Replace each node
|
|
233
|
+
// once to avoid a detached-path error.
|
|
234
|
+
if (seenMembers.has(parent.node)) {
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
seenMembers.add(parent.node)
|
|
238
|
+
|
|
239
|
+
replacements.push({ memberPath: parent, key })
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const { scope } = path
|
|
243
|
+
const usedNames = new Set<string>()
|
|
244
|
+
const keyToName = new Map<string, string>()
|
|
245
|
+
for (const { key } of entries) {
|
|
246
|
+
const baseName = isArray ? `${idName}${key}` : key
|
|
247
|
+
const baseNameIsFree =
|
|
248
|
+
t.isValidIdentifier(baseName) &&
|
|
249
|
+
!usedNames.has(baseName) &&
|
|
250
|
+
!scope.hasBinding(baseName) &&
|
|
251
|
+
!scope.hasGlobal(baseName) &&
|
|
252
|
+
!scope.hasReference(baseName)
|
|
253
|
+
const chosen = baseNameIsFree
|
|
254
|
+
? baseName
|
|
255
|
+
: scope.generateUid(t.isValidIdentifier(baseName) ? baseName : 'property')
|
|
256
|
+
usedNames.add(chosen)
|
|
257
|
+
keyToName.set(key, chosen)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const newDecls = entries.map(({ key, value }) =>
|
|
261
|
+
t.variableDeclaration(path.node.kind, [
|
|
262
|
+
t.variableDeclarator(t.identifier(keyToName.get(key)!), value),
|
|
263
|
+
]),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
const newPaths = path.replaceWithMultiple(newDecls)
|
|
267
|
+
for (const newPath of newPaths) {
|
|
268
|
+
newPath.scope.registerDeclaration(newPath)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Publish rewritten references for the next fixed-point iteration.
|
|
272
|
+
for (const { memberPath, key } of replacements) {
|
|
273
|
+
const name = keyToName.get(key)!
|
|
274
|
+
const [newRefPath] = memberPath.replaceWith(t.identifier(name))
|
|
275
|
+
if (newRefPath) {
|
|
276
|
+
const refBinding = newRefPath.scope.getBinding(name)
|
|
277
|
+
if (refBinding) {
|
|
278
|
+
refBinding.referencePaths.push(newRefPath)
|
|
279
|
+
refBinding.references++
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Remove the dead wrapper binding before the same name is visited again.
|
|
285
|
+
path.scope.removeBinding(idName)
|
|
286
|
+
|
|
287
|
+
return true
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function staticPropertyKey(prop: t.ObjectProperty): string | null {
|
|
291
|
+
const { key } = prop
|
|
292
|
+
if (!prop.computed && t.isIdentifier(key)) {
|
|
293
|
+
return key.name
|
|
294
|
+
}
|
|
295
|
+
return literalKeyToString(key)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function staticMemberKey(node: t.MemberExpression): string | null {
|
|
299
|
+
if (!node.computed) {
|
|
300
|
+
if (t.isIdentifier(node.property)) {
|
|
301
|
+
return node.property.name
|
|
302
|
+
}
|
|
303
|
+
return null
|
|
304
|
+
}
|
|
305
|
+
return literalKeyToString(node.property)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function literalKeyToString(node: t.Node): string | null {
|
|
309
|
+
if (t.isStringLiteral(node)) {
|
|
310
|
+
return node.value
|
|
311
|
+
}
|
|
312
|
+
if (t.isNumericLiteral(node)) {
|
|
313
|
+
return String(node.value)
|
|
314
|
+
}
|
|
315
|
+
if (t.isBigIntLiteral(node)) {
|
|
316
|
+
return node.value.toString()
|
|
317
|
+
}
|
|
318
|
+
return null
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// A member nested inside a destructuring target is still a write.
|
|
322
|
+
function isWriteTargetMember(memberPath: NodePath<t.MemberExpression>): boolean {
|
|
323
|
+
let current: NodePath = memberPath
|
|
324
|
+
let parent: NodePath | null = current.parentPath
|
|
325
|
+
while (parent) {
|
|
326
|
+
if (parent.isAssignmentExpression() && parent.node.left === current.node) {
|
|
327
|
+
return true
|
|
328
|
+
}
|
|
329
|
+
if (parent.isUpdateExpression() && parent.node.argument === current.node) {
|
|
330
|
+
return true
|
|
331
|
+
}
|
|
332
|
+
if (
|
|
333
|
+
parent.isUnaryExpression() &&
|
|
334
|
+
parent.node.operator === 'delete' &&
|
|
335
|
+
parent.node.argument === current.node
|
|
336
|
+
) {
|
|
337
|
+
return true
|
|
338
|
+
}
|
|
339
|
+
if (
|
|
340
|
+
(parent.isForInStatement() || parent.isForOfStatement()) &&
|
|
341
|
+
parent.node.left === current.node
|
|
342
|
+
) {
|
|
343
|
+
return true
|
|
344
|
+
}
|
|
345
|
+
if (
|
|
346
|
+
parent.isArrayPattern() ||
|
|
347
|
+
parent.isObjectPattern() ||
|
|
348
|
+
parent.isObjectProperty() ||
|
|
349
|
+
parent.isRestElement() ||
|
|
350
|
+
(parent.isAssignmentPattern() && parent.node.left === current.node)
|
|
351
|
+
) {
|
|
352
|
+
current = parent
|
|
353
|
+
parent = current.parentPath
|
|
354
|
+
continue
|
|
355
|
+
}
|
|
356
|
+
break
|
|
357
|
+
}
|
|
358
|
+
return false
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function functionRefsThis(path: NodePath): boolean {
|
|
362
|
+
const { node } = path
|
|
363
|
+
if (!t.isFunctionExpression(node) && !t.isFunctionDeclaration(node)) {
|
|
364
|
+
return false
|
|
365
|
+
}
|
|
366
|
+
const state: { found: boolean } = { found: false }
|
|
367
|
+
path.traverse(functionRefsThisVisitor, state)
|
|
368
|
+
return state.found
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const functionRefsThisVisitor: Visitor<{ found: boolean }> = {
|
|
372
|
+
ThisExpression(_path, state) {
|
|
373
|
+
state.found = true
|
|
374
|
+
},
|
|
375
|
+
Function(path, state) {
|
|
376
|
+
if (path.isArrowFunctionExpression()) {
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
// Computed method keys use the enclosing `this`. The key node itself needs a
|
|
380
|
+
// separate check because traversal starts below it.
|
|
381
|
+
if ((path.isObjectMethod() || path.isClassMethod()) && path.node.computed) {
|
|
382
|
+
const keyPath = path.get('key') as NodePath
|
|
383
|
+
if (keyPath.isThisExpression()) {
|
|
384
|
+
state.found = true
|
|
385
|
+
} else {
|
|
386
|
+
keyPath.traverse(functionRefsThisVisitor, state)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
path.skip()
|
|
390
|
+
},
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function initContainsSelfReference(binding: Binding, initPath: NodePath): boolean {
|
|
394
|
+
for (const ref of binding.referencePaths) {
|
|
395
|
+
let ancestor: NodePath | null = ref
|
|
396
|
+
while (ancestor) {
|
|
397
|
+
if (ancestor === initPath) {
|
|
398
|
+
return true
|
|
399
|
+
}
|
|
400
|
+
ancestor = ancestor.parentPath
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return false
|
|
404
|
+
}
|