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.
@@ -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
+ }