jz 0.5.1 → 0.7.0
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/README.md +315 -318
- package/bench/README.md +369 -0
- package/bench/bench.svg +102 -0
- package/cli.js +104 -30
- package/dist/interop.js +1 -0
- package/dist/jz.js +6024 -0
- package/index.d.ts +126 -0
- package/index.js +362 -84
- package/interop.js +159 -186
- package/jz.svg +5 -0
- package/jzify/arguments.js +97 -0
- package/jzify/bundler.js +382 -0
- package/jzify/classes.js +328 -0
- package/jzify/hoist-vars.js +177 -0
- package/jzify/index.js +51 -0
- package/jzify/names.js +37 -0
- package/jzify/switch.js +106 -0
- package/jzify/transform.js +349 -0
- package/layout.js +184 -0
- package/module/array.js +377 -176
- package/module/collection.js +639 -145
- package/module/console.js +55 -43
- package/module/core.js +264 -153
- package/module/date.js +15 -3
- package/module/function.js +73 -6
- package/module/index.js +2 -1
- package/module/json.js +226 -61
- package/module/math.js +551 -187
- package/module/number.js +327 -60
- package/module/object.js +474 -184
- package/module/regex.js +255 -25
- package/module/schema.js +24 -6
- package/module/simd.js +117 -0
- package/module/string.js +621 -226
- package/module/symbol.js +1 -1
- package/module/timer.js +9 -14
- package/module/typedarray.js +459 -73
- package/package.json +58 -14
- package/src/abi/index.js +39 -23
- package/src/abi/string.js +38 -41
- package/src/ast.js +460 -0
- package/src/autoload.js +29 -24
- package/src/bridge.js +111 -0
- package/src/compile/analyze-scans.js +824 -0
- package/src/compile/analyze.js +1600 -0
- package/src/compile/emit-assign.js +411 -0
- package/src/compile/emit.js +3512 -0
- package/src/compile/flow-types.js +103 -0
- package/src/{compile.js → compile/index.js} +778 -128
- package/src/{infer.js → compile/infer.js} +62 -98
- package/src/compile/loop-divmod.js +155 -0
- package/src/{narrow.js → compile/narrow.js} +409 -106
- package/src/compile/peel-stencil.js +261 -0
- package/src/compile/plan/advise.js +370 -0
- package/src/compile/plan/common.js +150 -0
- package/src/compile/plan/index.js +122 -0
- package/src/compile/plan/inline.js +682 -0
- package/src/compile/plan/literals.js +1199 -0
- package/src/compile/plan/loops.js +472 -0
- package/src/compile/plan/scope.js +649 -0
- package/src/compile/program-facts.js +423 -0
- package/src/ctx.js +220 -64
- package/src/ir.js +589 -172
- package/src/kind-traits.js +132 -0
- package/src/kind.js +524 -0
- package/src/op-policy.js +57 -0
- package/src/{optimize.js → optimize/index.js} +1432 -473
- package/src/optimize/vectorize.js +4660 -0
- package/src/param-reps.js +65 -0
- package/src/parse.js +44 -0
- package/src/{prepare.js → prepare/index.js} +649 -205
- package/src/prepare/lift-iife.js +149 -0
- package/src/reps.js +116 -0
- package/src/resolve.js +12 -3
- package/src/static.js +208 -0
- package/src/type.js +651 -0
- package/src/{assemble.js → wat/assemble.js} +275 -55
- package/src/wat/optimize.js +3938 -0
- package/transform.js +21 -0
- package/wasi.js +47 -5
- package/src/analyze.js +0 -3818
- package/src/emit.js +0 -3040
- package/src/jzify.js +0 -1580
- package/src/plan.js +0 -2132
- package/src/vectorize.js +0 -1088
- /package/src/{codegen.js → wat/codegen.js} +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `arguments` object + destructuring-param lowering for function→arrow conversion.
|
|
3
|
+
* @module jzify/arguments
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { paramList } from '../src/ast.js'
|
|
7
|
+
import { isDestructurePat, prependDecls } from './hoist-vars.js'
|
|
8
|
+
|
|
9
|
+
function usesArguments(node) {
|
|
10
|
+
if (node === 'arguments') return true
|
|
11
|
+
if (!Array.isArray(node)) return false
|
|
12
|
+
if (node[0] === 'function') return false
|
|
13
|
+
// Literal node `[, value]` (op === null) — node[1] is a string/number VALUE, not an
|
|
14
|
+
// identifier. A string literal `'arguments'` must not read as the arguments object.
|
|
15
|
+
if (node[0] == null) return false
|
|
16
|
+
if (node[0] === '.' || node[0] === '?.') return usesArguments(node[1])
|
|
17
|
+
if (node[0] === ':') return usesArguments(node[2])
|
|
18
|
+
for (let i = 1; i < node.length; i++) if (usesArguments(node[i])) return true
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function bindsArguments(body) {
|
|
23
|
+
const isArgDecl = s => Array.isArray(s) && (s[0] === 'var' || s[0] === 'let' || s[0] === 'const') &&
|
|
24
|
+
s.slice(1).some(d => d === 'arguments' || (Array.isArray(d) && d[0] === '=' && d[1] === 'arguments'))
|
|
25
|
+
let n = body
|
|
26
|
+
if (Array.isArray(n) && n[0] === '{}') n = n[1]
|
|
27
|
+
if (Array.isArray(n) && n[0] === ';') return n.slice(1).some(isArgDecl)
|
|
28
|
+
return isArgDecl(n)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function renameArguments(node, to) {
|
|
32
|
+
if (node === 'arguments') return to
|
|
33
|
+
if (!Array.isArray(node)) return node
|
|
34
|
+
if (node[0] === 'function') return node
|
|
35
|
+
// Literal node `[, value]` — node[1] is a value, not an identifier; leave untouched
|
|
36
|
+
// so a string literal `'arguments'` survives the rename.
|
|
37
|
+
if (node[0] == null) return node
|
|
38
|
+
if (node[0] === '.' || node[0] === '?.')
|
|
39
|
+
return [node[0], renameArguments(node[1], to), node[2]]
|
|
40
|
+
if (node[0] === ':')
|
|
41
|
+
return [node[0], node[1], renameArguments(node[2], to)]
|
|
42
|
+
return node.map(n => renameArguments(n, to))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function prependParamDecls(decl, body) {
|
|
46
|
+
if (Array.isArray(body) && body[0] === '{}') {
|
|
47
|
+
const inner = body[1]
|
|
48
|
+
if (Array.isArray(inner) && inner[0] === ';') return ['{}', [';', decl, ...inner.slice(1)]]
|
|
49
|
+
if (inner == null) return ['{}', decl]
|
|
50
|
+
return ['{}', [';', decl, inner]]
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(body) && (body[0] === ';' || body[0] === 'return')) return [';', decl, body]
|
|
53
|
+
return ['{}', [';', decl, ['return', body]]]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @param {ReturnType<import('./names.js').createNames>} names */
|
|
57
|
+
export function createArgumentsLowering(names) {
|
|
58
|
+
function lowerArguments(params, body) {
|
|
59
|
+
if (bindsArguments(body)) body = renameArguments(body, names.arg())
|
|
60
|
+
const paramsNeedLowering = paramList(params).some(isDestructurePat)
|
|
61
|
+
const usesArgsObj = usesArguments(params) || usesArguments(body)
|
|
62
|
+
if (!paramsNeedLowering && !usesArgsObj) return [params, body]
|
|
63
|
+
const name = names.arg()
|
|
64
|
+
const decls = []
|
|
65
|
+
for (const [idx, param] of paramList(params).entries()) {
|
|
66
|
+
if (Array.isArray(param) && param[0] === '...') {
|
|
67
|
+
decls.push(['=', param[1], ['()', ['.', name, 'slice'], [null, idx]]])
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(param) && param[0] === '=') {
|
|
71
|
+
decls.push(['=', param[1], ['??', ['[]', name, [null, idx]], renameArguments(param[2], name)]])
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
decls.push(['=', param, ['[]', name, [null, idx]]])
|
|
75
|
+
}
|
|
76
|
+
const renamed = usesArgsObj ? renameArguments(body, name) : body
|
|
77
|
+
return [['()', ['...', name]], decls.length ? prependParamDecls(['let', ...decls], renamed) : renamed]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let transformRef = null
|
|
81
|
+
const transformPattern = (node) => {
|
|
82
|
+
const transform = transformRef
|
|
83
|
+
if (node == null || !Array.isArray(node)) return node
|
|
84
|
+
const op = node[0]
|
|
85
|
+
if (op === '=') return ['=', transformPattern(node[1]), transform(node[2])]
|
|
86
|
+
if (op === ':') return [':', transform(node[1]), transformPattern(node[2])]
|
|
87
|
+
if (op === '...') return ['...', transformPattern(node[1])]
|
|
88
|
+
if (op === '[]' || op === '{}' || op === ',') return [op, ...node.slice(1).map(transformPattern)]
|
|
89
|
+
return transform(node)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
lowerArguments,
|
|
94
|
+
transformPattern,
|
|
95
|
+
bindTransform(fn) { transformRef = fn },
|
|
96
|
+
}
|
|
97
|
+
}
|
package/jzify/bundler.js
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundler/export helper folds and object-literal idiom canonicalization.
|
|
3
|
+
* @module jzify/bundler
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
handlerArgs, JZ_BLOCK_OPS, bindingOf, cloneNode, nodeEqual, descriptorProps,
|
|
8
|
+
literalString, collectBareRefs, moduleStmts, someDeep, isZeroLiteral, paramList,
|
|
9
|
+
} from '../src/ast.js'
|
|
10
|
+
|
|
11
|
+
export function foldStaticExportHelpers(ast) {
|
|
12
|
+
const body = moduleStmts(ast)
|
|
13
|
+
if (!body) return ast
|
|
14
|
+
|
|
15
|
+
const defPropAliases = new Set()
|
|
16
|
+
for (const stmt of body) {
|
|
17
|
+
const b = bindingOf(stmt)
|
|
18
|
+
if (b && isObjectDefineProperty(b[1])) defPropAliases.add(b[0])
|
|
19
|
+
}
|
|
20
|
+
if (!defPropAliases.size) return ast
|
|
21
|
+
|
|
22
|
+
const helperNames = new Set()
|
|
23
|
+
for (const stmt of body) {
|
|
24
|
+
const b = bindingOf(stmt)
|
|
25
|
+
if (b && Array.isArray(b[1]) && b[1][0] === '=>' && containsDefinePropertyCall(b[1], defPropAliases))
|
|
26
|
+
helperNames.add(b[0])
|
|
27
|
+
}
|
|
28
|
+
if (!helperNames.size) return ast
|
|
29
|
+
|
|
30
|
+
const rewrites = new Map()
|
|
31
|
+
const removable = new Set()
|
|
32
|
+
for (const stmt of body) {
|
|
33
|
+
const ex = staticExportCall(stmt, helperNames)
|
|
34
|
+
if (!ex) continue
|
|
35
|
+
for (const [key, value] of ex.props) rewrites.set(`${ex.target}.${key}`, value)
|
|
36
|
+
removable.add(stmt)
|
|
37
|
+
}
|
|
38
|
+
if (!rewrites.size) return ast
|
|
39
|
+
|
|
40
|
+
const rewritten = body
|
|
41
|
+
.filter(stmt => !removable.has(stmt) && !isDefPropAliasAssign(stmt, defPropAliases) && !isExportHelperAssign(stmt, helperNames))
|
|
42
|
+
.map(stmt => replaceStaticExportReads(stmt, rewrites))
|
|
43
|
+
return rewritten.length === 0 ? null : rewritten.length === 1 ? rewritten[0] : [';', ...rewritten]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Esbuild's CommonJS/ESM interop helpers alias Object reflection built-ins into
|
|
47
|
+
// locals (`var __create = Object.create`, `var __getOwnPropNames =
|
|
48
|
+
// Object.getOwnPropertyNames`, ...). jz deliberately does not expose those
|
|
49
|
+
// built-ins as first-class function values, but the helpers are static enough to
|
|
50
|
+
// lower back to the supported direct calls and module reads.
|
|
51
|
+
export function foldStaticBundlerHelpers(ast) {
|
|
52
|
+
const body = moduleStmts(ast)
|
|
53
|
+
if (!body) return ast
|
|
54
|
+
const binds = body.map(bindingOf) // [name, init] | null, index-aligned with body
|
|
55
|
+
|
|
56
|
+
// Local aliases of Object reflection built-ins: name -> canonical built-in.
|
|
57
|
+
// esbuild's interop preamble always emits these (`var __defProp =
|
|
58
|
+
// Object.defineProperty`, ...); their absence proves the input is not a
|
|
59
|
+
// bundle, so the fold stays a strict no-op rather than guessing.
|
|
60
|
+
const aliases = new Map()
|
|
61
|
+
for (const b of binds) {
|
|
62
|
+
const key = b && objectBuiltinKey(b[1])
|
|
63
|
+
if (key) aliases.set(b[0], key)
|
|
64
|
+
}
|
|
65
|
+
if (!aliases.size) return ast
|
|
66
|
+
|
|
67
|
+
// __copyProps: an arrow driving both aliased getOwnPropertyNames + defineProperty.
|
|
68
|
+
const copyHelpers = new Set()
|
|
69
|
+
for (const b of binds)
|
|
70
|
+
if (b && isArrow(b[1]) &&
|
|
71
|
+
containsCall(b[1], c => aliases.get(c) === 'Object.getOwnPropertyNames') &&
|
|
72
|
+
containsCall(b[1], c => aliases.get(c) === 'Object.defineProperty'))
|
|
73
|
+
copyHelpers.add(b[0])
|
|
74
|
+
|
|
75
|
+
// __toESM: an arrow cloning a module behind a prototype, tagging default/__esModule.
|
|
76
|
+
const interopHelpers = new Set()
|
|
77
|
+
for (const b of binds)
|
|
78
|
+
if (b && isArrow(b[1]) &&
|
|
79
|
+
containsCall(b[1], c => aliases.get(c) === 'Object.create') &&
|
|
80
|
+
containsCall(b[1], c => aliases.get(c) === 'Object.getPrototypeOf') &&
|
|
81
|
+
containsCall(b[1], c => copyHelpers.has(c)) &&
|
|
82
|
+
someDeep(b[1], n => n === 'default') && someDeep(b[1], n => n === '__esModule'))
|
|
83
|
+
interopHelpers.add(b[0])
|
|
84
|
+
|
|
85
|
+
// Bindings produced by an interop-helper call: name -> wrapped module expression.
|
|
86
|
+
const interopBindings = new Map()
|
|
87
|
+
for (const b of binds)
|
|
88
|
+
if (b && Array.isArray(b[1]) && b[1][0] === '()' && interopHelpers.has(b[1][1])) {
|
|
89
|
+
const args = handlerArgs(b[1].slice(2))
|
|
90
|
+
if (args.length) interopBindings.set(b[0], args[0])
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let out = body.map(stmt => rewriteBundlerAliases(stmt, aliases, interopBindings))
|
|
94
|
+
if (interopBindings.size) out = out.map(stmt => replaceInteropReads(stmt, interopBindings))
|
|
95
|
+
out = out.map(stmt => rewriteBundlerAliases(stmt, aliases, interopBindings)).filter(s => s != null)
|
|
96
|
+
|
|
97
|
+
// Drop synthetic alias/helper bindings nothing references after rewriting.
|
|
98
|
+
const synthetic = n => aliases.has(n) || copyHelpers.has(n) || interopHelpers.has(n) || interopBindings.has(n)
|
|
99
|
+
const live = new Set()
|
|
100
|
+
for (const stmt of out) {
|
|
101
|
+
const b = bindingOf(stmt)
|
|
102
|
+
if (!(b && synthetic(b[0]))) collectBareRefs(stmt, live)
|
|
103
|
+
}
|
|
104
|
+
out = out.filter(stmt => {
|
|
105
|
+
const b = bindingOf(stmt)
|
|
106
|
+
return !(b && synthetic(b[0]) && !live.has(b[0]))
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return out.length === 0 ? null : out.length === 1 ? out[0] : [';', ...out]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const isArrow = node => Array.isArray(node) && node[0] === '=>'
|
|
113
|
+
|
|
114
|
+
const OBJECT_BUILTINS = new Set(['create', 'getPrototypeOf', 'getOwnPropertyNames', 'getOwnPropertyDescriptor', 'defineProperty'])
|
|
115
|
+
|
|
116
|
+
// Canonical name of the Object reflection built-in `node` references, or null.
|
|
117
|
+
function objectBuiltinKey(node) {
|
|
118
|
+
if (!Array.isArray(node) || node[0] !== '.') return null
|
|
119
|
+
if (node[1] === 'Object' && OBJECT_BUILTINS.has(node[2])) return 'Object.' + node[2]
|
|
120
|
+
return isObjectHasOwnPropertyRef(node) ? 'Object.prototype.hasOwnProperty' : null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Does `node` contain a `()` call whose string callee satisfies `ok`?
|
|
124
|
+
const containsCall = (node, ok) =>
|
|
125
|
+
someDeep(node, n => Array.isArray(n) && n[0] === '()' && typeof n[1] === 'string' && ok(n[1]))
|
|
126
|
+
|
|
127
|
+
function rewriteBundlerAliases(node, aliases, interopBindings) {
|
|
128
|
+
if (!Array.isArray(node)) return node
|
|
129
|
+
const rec = n => rewriteBundlerAliases(n, aliases, interopBindings)
|
|
130
|
+
|
|
131
|
+
if (node[0] === ';') {
|
|
132
|
+
const out = [';']
|
|
133
|
+
for (let i = 1; i < node.length; i++) {
|
|
134
|
+
const child = rec(node[i])
|
|
135
|
+
if (child != null) out.push(child)
|
|
136
|
+
}
|
|
137
|
+
return out.length === 1 ? null : out.length === 2 ? out[1] : out
|
|
138
|
+
}
|
|
139
|
+
if (node[0] === '{}' && node.length === 2) {
|
|
140
|
+
const wasBlock = Array.isArray(node[1]) && JZ_BLOCK_OPS.has(node[1][0])
|
|
141
|
+
const inner = rec(node[1])
|
|
142
|
+
if (!wasBlock || inner == null) return ['{}', inner]
|
|
143
|
+
const stayed = Array.isArray(inner) && JZ_BLOCK_OPS.has(inner[0])
|
|
144
|
+
return ['{}', stayed ? inner : [';', inner]]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (node[0] === '()') {
|
|
148
|
+
const callee = node[1]
|
|
149
|
+
const args = handlerArgs(node.slice(2))
|
|
150
|
+
|
|
151
|
+
if (typeof callee === 'string') {
|
|
152
|
+
const key = aliases.get(callee)
|
|
153
|
+
if (key === 'Object.defineProperty') {
|
|
154
|
+
const define = staticDefineProperty(args)
|
|
155
|
+
if (define !== undefined) return define
|
|
156
|
+
}
|
|
157
|
+
if (key === 'Object.getOwnPropertyNames' || key === 'Object.create') {
|
|
158
|
+
if (key === 'Object.create' && isGetPrototypeOfCall(args[0], aliases)) return ['{}', null]
|
|
159
|
+
return ['()', key, ...args.map(rec)]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// `__hasOwnProp.call(o, k)` -> `o.hasOwnProperty(k)`.
|
|
163
|
+
if (Array.isArray(callee) && callee[0] === '.' && callee[2] === 'call' && args.length >= 2 &&
|
|
164
|
+
typeof callee[1] === 'string' && aliases.get(callee[1]) === 'Object.prototype.hasOwnProperty')
|
|
165
|
+
return ['()', ['.', rec(args[0]), 'hasOwnProperty'], rec(args[1])]
|
|
166
|
+
// `(0, fn)(...)` comma-call resolving to an interop module read.
|
|
167
|
+
const seqCall = commaZeroCall(callee, interopBindings)
|
|
168
|
+
if (seqCall) return ['()', seqCall, ...args.map(rec)]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (node[0] === '.' || node[0] === '?.') return [node[0], rec(node[1]), node[2]]
|
|
172
|
+
if (node[0] === ':') return [node[0], node[1], rec(node[2])]
|
|
173
|
+
return node.map((part, i) => i === 0 ? part : rec(part))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function replaceInteropReads(node, bindings) {
|
|
177
|
+
if (typeof node === 'string' && bindings.has(node)) return cloneNode(bindings.get(node))
|
|
178
|
+
if (!Array.isArray(node)) return node
|
|
179
|
+
if (node[0] === '=' && typeof node[1] === 'string') return ['=', node[1], replaceInteropReads(node[2], bindings)]
|
|
180
|
+
if (node[0] === 'let' || node[0] === 'const' || node[0] === 'var')
|
|
181
|
+
return [node[0], ...node.slice(1).map(decl =>
|
|
182
|
+
Array.isArray(decl) && decl[0] === '=' ? ['=', decl[1], replaceInteropReads(decl[2], bindings)] : decl)]
|
|
183
|
+
if ((node[0] === '.' || node[0] === '?.') && typeof node[1] === 'string' && typeof node[2] === 'string' && bindings.has(node[1])) {
|
|
184
|
+
const mod = cloneNode(bindings.get(node[1]))
|
|
185
|
+
return node[2] === 'default' ? mod : [node[0], mod, node[2]]
|
|
186
|
+
}
|
|
187
|
+
if (node[0] === ':') return [node[0], node[1], replaceInteropReads(node[2], bindings)]
|
|
188
|
+
return node.map((part, i) => i === 0 ? part : replaceInteropReads(part, bindings))
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function isGetPrototypeOfCall(node, aliases) {
|
|
192
|
+
if (!Array.isArray(node) || node[0] !== '()') return false
|
|
193
|
+
const callee = node[1]
|
|
194
|
+
return (typeof callee === 'string' && aliases.get(callee) === 'Object.getPrototypeOf') ||
|
|
195
|
+
objectBuiltinKey(callee) === 'Object.getPrototypeOf'
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function commaZeroCall(callee, bindings) {
|
|
199
|
+
if (!Array.isArray(callee) || callee[0] !== '()' || !Array.isArray(callee[1]) || callee[1][0] !== ',') return null
|
|
200
|
+
const parts = callee[1].slice(1)
|
|
201
|
+
if (parts.length !== 2 || !isZeroLiteral(parts[0])) return null
|
|
202
|
+
const fn = replaceInteropReads(parts[1], bindings)
|
|
203
|
+
return fn === parts[1] ? null : fn
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// `defProp(obj, "key", descriptor)` -> `obj.key = value`; null drops `__esModule`.
|
|
207
|
+
function staticDefineProperty(args) {
|
|
208
|
+
if (args.length < 3) return undefined
|
|
209
|
+
const [obj, keyExpr, desc] = args
|
|
210
|
+
const key = literalString(keyExpr)
|
|
211
|
+
const props = descriptorProps(desc)
|
|
212
|
+
if (typeof key !== 'string' || !props) return undefined
|
|
213
|
+
if (key === '__esModule') return null
|
|
214
|
+
const prop = name => props.find(p => Array.isArray(p) && p[0] === ':' && p[1] === name)?.[2]
|
|
215
|
+
const value = prop('value')
|
|
216
|
+
if (value !== undefined) return ['=', ['.', obj, key], value]
|
|
217
|
+
const got = getterReturnExpr(prop('get'))
|
|
218
|
+
return got !== null ? ['=', ['.', obj, key], got] : undefined
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function isObjectDefineProperty(node) {
|
|
222
|
+
return Array.isArray(node) && node[0] === '.' && node[1] === 'Object' && node[2] === 'defineProperty'
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Unwrap an esbuild module binding to `[name, init]`. After hoistVars, a binding
|
|
226
|
+
* reaches this pass either split into a bare `['=', name, init]` (RHS hoisted out
|
|
227
|
+
* as a separate `let name;`) or kept as a single `['let', ['=', name, init]]` decl
|
|
228
|
+
* (arrow RHS — see the `;` handler in hoistVars). The fold keys on name/init,
|
|
229
|
+
* so it must see through both shapes. */
|
|
230
|
+
function isDefPropAliasAssign(stmt, aliases) {
|
|
231
|
+
const b = bindingOf(stmt)
|
|
232
|
+
return b != null && aliases.has(b[0]) && isObjectDefineProperty(b[1])
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function isExportHelperAssign(stmt, helpers) {
|
|
236
|
+
const b = bindingOf(stmt)
|
|
237
|
+
return b != null && helpers.has(b[0])
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function containsDefinePropertyCall(node, aliases) {
|
|
241
|
+
if (!Array.isArray(node)) return false
|
|
242
|
+
if (node[0] === '()' && (aliases.has(node[1]) || isObjectDefineProperty(node[1]))) return true
|
|
243
|
+
for (let i = 1; i < node.length; i++) if (containsDefinePropertyCall(node[i], aliases)) return true
|
|
244
|
+
return false
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function staticExportCall(stmt, helpers) {
|
|
248
|
+
if (!Array.isArray(stmt) || stmt[0] !== '()' || !helpers.has(stmt[1])) return null
|
|
249
|
+
const args = handlerArgs(stmt.slice(2))
|
|
250
|
+
if (args.length !== 2 || typeof args[0] !== 'string') return null
|
|
251
|
+
const props = descriptorProps(args[1])
|
|
252
|
+
if (!props) return null
|
|
253
|
+
const out = []
|
|
254
|
+
for (const prop of props) {
|
|
255
|
+
if (!Array.isArray(prop) || prop[0] !== ':' || typeof prop[1] !== 'string') return null
|
|
256
|
+
const value = getterReturnExpr(prop[2])
|
|
257
|
+
if (!value) return null
|
|
258
|
+
out.push([prop[1], value])
|
|
259
|
+
}
|
|
260
|
+
return { target: args[0], props: out }
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function getterReturnExpr(node) {
|
|
264
|
+
if (!Array.isArray(node) || node[0] !== '=>') return null
|
|
265
|
+
const params = paramList(node[1])
|
|
266
|
+
if (params.length !== 0) return null
|
|
267
|
+
const body = node[2]
|
|
268
|
+
if (Array.isArray(body) && body[0] === '{}' && Array.isArray(body[1]) && body[1][0] === 'return') return body[1][1]
|
|
269
|
+
if (Array.isArray(body) && body[0] === '{}' && Array.isArray(body[1]) && body[1][0] === ';' &&
|
|
270
|
+
Array.isArray(body[1][1]) && body[1][1][0] === 'return') return body[1][1][1]
|
|
271
|
+
if (Array.isArray(body) && body[0] === 'return') return body[1]
|
|
272
|
+
return body
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function replaceStaticExportReads(node, rewrites) {
|
|
276
|
+
if (node == null || typeof node !== 'object' || !Array.isArray(node)) return node
|
|
277
|
+
if ((node[0] === '.' || node[0] === '?.') && typeof node[1] === 'string' && typeof node[2] === 'string') {
|
|
278
|
+
const value = rewrites.get(`${node[1]}.${node[2]}`)
|
|
279
|
+
if (value) return cloneNode(value)
|
|
280
|
+
}
|
|
281
|
+
if (node[0] === ':') return [node[0], node[1], replaceStaticExportReads(node[2], rewrites)]
|
|
282
|
+
return node.map((part, i) => i === 0 ? part : replaceStaticExportReads(part, rewrites))
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function canonicalizeObjectIdioms(node) {
|
|
286
|
+
if (node == null || typeof node !== 'object' || !Array.isArray(node)) return node
|
|
287
|
+
|
|
288
|
+
const out = node.map((part, i) => i === 0 ? part : canonicalizeObjectIdioms(part))
|
|
289
|
+
|
|
290
|
+
const toStringCall = objectPrototypeToStringCall(out)
|
|
291
|
+
if (toStringCall) return ['()', '__object_toString', toStringCall.obj]
|
|
292
|
+
|
|
293
|
+
const hasOwnCall = objectHasOwnPropertyCall(out)
|
|
294
|
+
if (hasOwnCall) return ['()', ['.', hasOwnCall.obj, 'hasOwnProperty'], hasOwnCall.key]
|
|
295
|
+
|
|
296
|
+
const mapString = arrayMapStringCallback(out)
|
|
297
|
+
if (mapString) return mapString
|
|
298
|
+
|
|
299
|
+
if (out[0] === '&&') {
|
|
300
|
+
const leftCtor = constructorIsObject(out[1])
|
|
301
|
+
const rightKeys = objectKeysLengthZero(out[2])
|
|
302
|
+
if (leftCtor && rightKeys && nodeEqual(leftCtor.obj, rightKeys.obj)) return out[2]
|
|
303
|
+
|
|
304
|
+
const leftKeys = objectKeysLengthZero(out[1])
|
|
305
|
+
const rightCtor = constructorIsObject(out[2])
|
|
306
|
+
if (leftKeys && rightCtor && nodeEqual(leftKeys.obj, rightCtor.obj)) return out[1]
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return out
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function arrayMapStringCallback(node) {
|
|
313
|
+
if (!Array.isArray(node) || node[0] !== '()') return null
|
|
314
|
+
const callee = node[1]
|
|
315
|
+
if (!Array.isArray(callee) || callee[0] !== '.' || callee[2] !== 'map') return null
|
|
316
|
+
const args = handlerArgs(node.slice(2))
|
|
317
|
+
if (args.length !== 1 || args[0] !== 'String') return null
|
|
318
|
+
return ['()', callee, ['=>', 'value', ['()', 'String', 'value']]]
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function objectHasOwnPropertyCall(node) {
|
|
322
|
+
if (!Array.isArray(node) || node[0] !== '()') return null
|
|
323
|
+
const callee = node[1]
|
|
324
|
+
if (!Array.isArray(callee) || callee[0] !== '.' || callee[2] !== 'call') return null
|
|
325
|
+
if (!isObjectHasOwnPropertyRef(callee[1])) return null
|
|
326
|
+
const args = handlerArgs(node.slice(2))
|
|
327
|
+
if (args.length < 2) return null
|
|
328
|
+
return { obj: args[0], key: args[1] }
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function objectPrototypeToStringCall(node) {
|
|
332
|
+
if (!Array.isArray(node) || node[0] !== '()') return null
|
|
333
|
+
const callee = node[1]
|
|
334
|
+
if (!Array.isArray(callee) || callee[0] !== '.' || callee[2] !== 'call') return null
|
|
335
|
+
if (!isObjectPrototypeToStringRef(callee[1])) return null
|
|
336
|
+
const args = handlerArgs(node.slice(2))
|
|
337
|
+
if (args.length < 1) return null
|
|
338
|
+
return { obj: args[0] }
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function isObjectHasOwnPropertyRef(node) {
|
|
342
|
+
if (!Array.isArray(node) || node[0] !== '.' || node[2] !== 'hasOwnProperty') return false
|
|
343
|
+
if (node[1] === 'Object') return true
|
|
344
|
+
return Array.isArray(node[1]) && node[1][0] === '.' && node[1][1] === 'Object' && node[1][2] === 'prototype'
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function isObjectPrototypeToStringRef(node) {
|
|
348
|
+
return Array.isArray(node) && node[0] === '.' && node[2] === 'toString' &&
|
|
349
|
+
Array.isArray(node[1]) && node[1][0] === '.' && node[1][1] === 'Object' && node[1][2] === 'prototype'
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function constructorIsObject(node) {
|
|
353
|
+
if (!Array.isArray(node) || (node[0] !== '===' && node[0] !== '==')) return null
|
|
354
|
+
const left = constructorReceiver(node[1])
|
|
355
|
+
if (left && node[2] === 'Object') return { obj: left }
|
|
356
|
+
const right = constructorReceiver(node[2])
|
|
357
|
+
if (right && node[1] === 'Object') return { obj: right }
|
|
358
|
+
return null
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function constructorReceiver(node) {
|
|
362
|
+
return Array.isArray(node) && node[0] === '.' && node[2] === 'constructor' ? node[1] : null
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function objectKeysLengthZero(node) {
|
|
366
|
+
if (!Array.isArray(node) || (node[0] !== '===' && node[0] !== '==')) return null
|
|
367
|
+
const left = objectKeysLengthReceiver(node[1])
|
|
368
|
+
if (left && isZeroLiteral(node[2])) return { obj: left }
|
|
369
|
+
const right = objectKeysLengthReceiver(node[2])
|
|
370
|
+
if (right && isZeroLiteral(node[1])) return { obj: right }
|
|
371
|
+
return null
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function objectKeysLengthReceiver(node) {
|
|
375
|
+
if (!Array.isArray(node) || node[0] !== '.' || node[2] !== 'length') return null
|
|
376
|
+
const call = node[1]
|
|
377
|
+
if (!Array.isArray(call) || call[0] !== '()') return null
|
|
378
|
+
const callee = call[1]
|
|
379
|
+
if (!Array.isArray(callee) || callee[0] !== '.' || callee[1] !== 'Object' || callee[2] !== 'keys') return null
|
|
380
|
+
const args = handlerArgs(call.slice(2))
|
|
381
|
+
return args.length === 1 ? args[0] : null
|
|
382
|
+
}
|