jz 0.5.0 → 0.6.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.
Files changed (80) hide show
  1. package/README.md +288 -314
  2. package/bench/README.md +319 -0
  3. package/bench/bench.svg +112 -0
  4. package/cli.js +32 -24
  5. package/index.js +177 -55
  6. package/interop.js +88 -159
  7. package/jz.svg +5 -0
  8. package/jzify/arguments.js +97 -0
  9. package/jzify/bundler.js +382 -0
  10. package/jzify/classes.js +328 -0
  11. package/jzify/hoist-vars.js +177 -0
  12. package/jzify/index.js +51 -0
  13. package/jzify/names.js +37 -0
  14. package/jzify/switch.js +106 -0
  15. package/jzify/transform.js +349 -0
  16. package/layout.js +179 -0
  17. package/module/array.js +322 -153
  18. package/module/collection.js +603 -145
  19. package/module/console.js +55 -43
  20. package/module/core.js +281 -142
  21. package/module/date.js +15 -3
  22. package/module/function.js +73 -6
  23. package/module/index.js +2 -1
  24. package/module/json.js +226 -61
  25. package/module/math.js +461 -185
  26. package/module/number.js +306 -60
  27. package/module/object.js +448 -184
  28. package/module/regex.js +255 -25
  29. package/module/schema.js +24 -6
  30. package/module/simd.js +85 -0
  31. package/module/string.js +591 -220
  32. package/module/symbol.js +1 -1
  33. package/module/timer.js +9 -14
  34. package/module/typedarray.js +45 -48
  35. package/package.json +41 -12
  36. package/src/abi/index.js +39 -23
  37. package/src/abi/string.js +38 -41
  38. package/src/ast.js +460 -0
  39. package/src/autoload.js +26 -24
  40. package/src/bridge.js +111 -0
  41. package/src/compile/analyze-scans.js +661 -0
  42. package/src/compile/analyze.js +1565 -0
  43. package/src/compile/emit-assign.js +408 -0
  44. package/src/compile/emit.js +3201 -0
  45. package/src/compile/flow-types.js +103 -0
  46. package/src/{compile.js → compile/index.js} +497 -125
  47. package/src/{infer.js → compile/infer.js} +27 -98
  48. package/src/{narrow.js → compile/narrow.js} +302 -96
  49. package/src/compile/plan/advise.js +316 -0
  50. package/src/compile/plan/common.js +150 -0
  51. package/src/compile/plan/index.js +118 -0
  52. package/src/compile/plan/inline.js +679 -0
  53. package/src/compile/plan/literals.js +984 -0
  54. package/src/compile/plan/loops.js +472 -0
  55. package/src/compile/plan/scope.js +573 -0
  56. package/src/compile/program-facts.js +404 -0
  57. package/src/ctx.js +176 -58
  58. package/src/ir.js +540 -171
  59. package/src/kind-traits.js +105 -0
  60. package/src/kind.js +462 -0
  61. package/src/op-policy.js +57 -0
  62. package/src/{optimize.js → optimize/index.js} +1106 -446
  63. package/src/optimize/vectorize.js +1874 -0
  64. package/src/param-reps.js +65 -0
  65. package/src/parse.js +44 -0
  66. package/src/{prepare.js → prepare/index.js} +600 -205
  67. package/src/reps.js +115 -0
  68. package/src/resolve.js +12 -3
  69. package/src/static.js +199 -0
  70. package/src/type.js +647 -0
  71. package/src/{assemble.js → wat/assemble.js} +86 -48
  72. package/src/wat/optimize.js +3760 -0
  73. package/transform.js +21 -0
  74. package/wasi.js +47 -5
  75. package/src/analyze.js +0 -3818
  76. package/src/emit.js +0 -2997
  77. package/src/jzify.js +0 -1553
  78. package/src/plan.js +0 -2132
  79. package/src/vectorize.js +0 -1088
  80. /package/src/{codegen.js → wat/codegen.js} +0 -0
@@ -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
+ }