jz 0.0.0 → 0.1.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/LICENSE +21 -0
- package/README.md +381 -0
- package/cli.js +163 -0
- package/index.js +217 -0
- package/module/array.js +1317 -0
- package/module/collection.js +791 -0
- package/module/console.js +190 -0
- package/module/core.js +642 -0
- package/module/function.js +180 -0
- package/module/index.js +15 -0
- package/module/json.js +504 -0
- package/module/math.js +389 -0
- package/module/number.js +605 -0
- package/module/object.js +357 -0
- package/module/regex.js +913 -0
- package/module/schema.js +104 -0
- package/module/string.js +928 -0
- package/module/symbol.js +54 -0
- package/module/timer.js +253 -0
- package/module/typedarray.js +711 -0
- package/package.json +54 -5
- package/src/analyze.js +1906 -0
- package/src/compile.js +2175 -0
- package/src/ctx.js +243 -0
- package/src/emit.js +2095 -0
- package/src/host.js +524 -0
- package/src/ir.js +649 -0
- package/src/jzify.js +391 -0
- package/src/optimize.js +1352 -0
- package/src/prepare.js +1598 -0
- package/wasi.js +74 -0
package/module/schema.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema subsystem — object property layout registration, lookup, boxing.
|
|
3
|
+
*
|
|
4
|
+
* Owns: register, find, isBoxed, emitInner on ctx.schema.
|
|
5
|
+
* Used by: core.js (property dispatch), object.js (literals), prepare.js (tracking).
|
|
6
|
+
*
|
|
7
|
+
* @module schema
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { emit, typed, asF64, VAL, lookupValType, repOf } from '../src/compile.js'
|
|
11
|
+
import { err, inc } from '../src/ctx.js'
|
|
12
|
+
|
|
13
|
+
/** Initialize schema helpers on ctx. Called once per compilation from core module. */
|
|
14
|
+
export function initSchema(ctx) {
|
|
15
|
+
// key → schemaId for O(1) dedupe; prop → [{id, slot}] for O(matches) structural find.
|
|
16
|
+
// \x01 delimiter avoids collision with any legal JS identifier character.
|
|
17
|
+
const byKey = new Map()
|
|
18
|
+
const byProp = new Map()
|
|
19
|
+
ctx.schema._byKey = byKey
|
|
20
|
+
ctx.schema._byProp = byProp
|
|
21
|
+
|
|
22
|
+
ctx.schema.register = (props) => {
|
|
23
|
+
const key = props.join('\x01')
|
|
24
|
+
const existing = byKey.get(key)
|
|
25
|
+
if (existing != null) return existing
|
|
26
|
+
const id = ctx.schema.list.push(props) - 1
|
|
27
|
+
byKey.set(key, id)
|
|
28
|
+
for (let i = 0; i < props.length; i++) {
|
|
29
|
+
const p = props[i]
|
|
30
|
+
let bucket = byProp.get(p)
|
|
31
|
+
if (!bucket) byProp.set(p, bucket = [])
|
|
32
|
+
bucket.push({ id, slot: i })
|
|
33
|
+
}
|
|
34
|
+
return id
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** schemaId for a variable name: ValueRep first, then module-level ctx.schema.vars.
|
|
38
|
+
* Both paths exist because vars covers names without a per-function ValueRep
|
|
39
|
+
* (prepare-phase rest/destructure tracking, module-level autoboxes). */
|
|
40
|
+
ctx.schema.idOf = (name) => repOf(name)?.schemaId ?? ctx.schema.vars.get(name)
|
|
41
|
+
|
|
42
|
+
/** Resolve variable name to its schema props array, or null. */
|
|
43
|
+
ctx.schema.resolve = (varName) => {
|
|
44
|
+
const id = ctx.schema.idOf(varName)
|
|
45
|
+
return id != null ? ctx.schema.list[id] : null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Check if variable has a boxed schema (slot 0 = __inner__). */
|
|
49
|
+
ctx.schema.isBoxed = (varName) => {
|
|
50
|
+
const id = ctx.schema.idOf(varName)
|
|
51
|
+
return id != null && ctx.schema.list[id]?.[0] === '__inner__'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Emit code to load the inner value (slot 0) of a boxed variable. */
|
|
55
|
+
ctx.schema.emitInner = (varName) => {
|
|
56
|
+
inc('__ptr_offset')
|
|
57
|
+
return typed(['f64.load', ['call', '$__ptr_offset', asF64(emit(varName))]], 'f64')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Find property index by variable schema or structural subtyping.
|
|
61
|
+
* Returns -1 to signal "use dynamic lookup" in three cases:
|
|
62
|
+
* 1. Variable has precise schema but schema lacks the property
|
|
63
|
+
* 2. Variable's valType is known and is not an object
|
|
64
|
+
* 3. Structural search finds the property at inconsistent offsets across schemas
|
|
65
|
+
* Case 3 is a real ambiguity — the caller must route to runtime dispatch.
|
|
66
|
+
* `safe=true` disables structural subtyping when the variable's type is not
|
|
67
|
+
* known to be VAL.OBJECT. Use for writes: a wrong slot clobbers unrelated
|
|
68
|
+
* memory (e.g. arr.loc = ... corrupting arr[slot]). Reads only return wrong
|
|
69
|
+
* values, which callers can tolerate. */
|
|
70
|
+
ctx.schema.find = (varName, prop, safe = false) => {
|
|
71
|
+
// Precise: variable has known schema
|
|
72
|
+
const id = ctx.schema.idOf(varName)
|
|
73
|
+
if (id != null) return ctx.schema.list[id]?.indexOf(prop) ?? -1
|
|
74
|
+
// Known non-object pointer-backed values must use dynamic property lookup,
|
|
75
|
+
// not structural object schemas registered elsewhere in the function.
|
|
76
|
+
if (typeof varName === 'string') {
|
|
77
|
+
const vt = lookupValType(varName)
|
|
78
|
+
if (vt != null && vt !== VAL.OBJECT) return -1
|
|
79
|
+
if (safe && vt !== VAL.OBJECT) return -1
|
|
80
|
+
}
|
|
81
|
+
// Structural subtyping: walk only schemas that contain this prop.
|
|
82
|
+
// Consistent slot across all → return slot; any mismatch → -1 (dynamic lookup).
|
|
83
|
+
const bucket = byProp.get(prop)
|
|
84
|
+
if (!bucket) return -1
|
|
85
|
+
const slot = bucket[0].slot
|
|
86
|
+
for (let i = 1; i < bucket.length; i++) if (bucket[i].slot !== slot) return -1
|
|
87
|
+
return slot
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Resolve the monomorphic slot value-type for `varName.prop`, or null.
|
|
91
|
+
* Precise path only: requires the variable to have a bound `schemaId`
|
|
92
|
+
* (ValueRep or `ctx.schema.vars`). Structural-subtyping is intentionally
|
|
93
|
+
* off — without per-call-site flow inference, structural agreement on a
|
|
94
|
+
* slot can lead `analyzeValTypes` to bind locals as VAL.NUMBER (or other
|
|
95
|
+
* kinds) when in fact the holder isn't an object of any registered
|
|
96
|
+
* schema. That mistyping then routes downstream property accesses
|
|
97
|
+
* through __hash_get instead of __dyn_get_any, growing the binary. */
|
|
98
|
+
ctx.schema.slotVT = (varName, prop) => {
|
|
99
|
+
const id = ctx.schema.idOf(varName)
|
|
100
|
+
if (id == null) return null
|
|
101
|
+
const idx = ctx.schema.list[id]?.indexOf(prop)
|
|
102
|
+
return idx >= 0 ? (ctx.schema.slotTypes.get(id)?.[idx] ?? null) : null
|
|
103
|
+
}
|
|
104
|
+
}
|