jz 0.3.1 → 0.5.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 +277 -258
- package/cli.js +10 -52
- package/index.js +181 -29
- package/{src/host.js → interop.js} +291 -88
- package/module/array.js +195 -76
- package/module/collection.js +249 -20
- package/module/console.js +1 -1
- package/module/core.js +267 -119
- package/module/date.js +9 -5
- package/module/function.js +11 -1
- package/module/json.js +367 -61
- package/module/math.js +568 -128
- package/module/number.js +390 -227
- package/module/object.js +352 -39
- package/module/regex.js +123 -16
- package/module/schema.js +15 -1
- package/module/string.js +555 -96
- package/module/timer.js +1 -1
- package/module/typedarray.js +674 -57
- package/package.json +12 -6
- package/src/abi/array.js +89 -0
- package/src/abi/index.js +35 -0
- package/src/abi/number.js +137 -0
- package/src/abi/object.js +57 -0
- package/src/abi/string.js +529 -0
- package/src/analyze.js +1872 -487
- package/src/assemble.js +58 -20
- package/src/autoload.js +13 -1
- package/src/codegen.js +177 -0
- package/src/compile.js +462 -186
- package/src/ctx.js +85 -18
- package/src/emit.js +668 -197
- package/src/infer.js +548 -0
- package/src/ir.js +302 -27
- package/src/jzify.js +898 -259
- package/src/narrow.js +455 -246
- package/src/optimize.js +263 -99
- package/src/plan.js +713 -35
- package/src/prepare.js +818 -293
- package/src/resolve.js +85 -0
- package/src/vectorize.js +99 -18
- package/src/ast.js +0 -160
- package/src/auto-config.js +0 -120
package/module/object.js
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* @module object
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { typed, asF64, asI64, temp, tempI32, allocPtr, needsDynShadow, mkPtrIR, extractF64Bits, appendStaticSlots, slotAddr, elemStore } from '../src/ir.js'
|
|
10
|
+
import { typed, asF64, asI64, temp, tempI32, allocPtr, needsDynShadow, mkPtrIR, extractF64Bits, appendStaticSlots, slotAddr, elemLoad, elemStore } from '../src/ir.js'
|
|
11
11
|
import { emit } from '../src/emit.js'
|
|
12
12
|
import { valTypeOf, lookupValType, VAL, repOf, updateRep, shapeOf } from '../src/analyze.js'
|
|
13
13
|
import { ctx, err, inc, PTR, LAYOUT } from '../src/ctx.js'
|
|
14
|
+
import { includeModule } from '../src/autoload.js'
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
export default (ctx) => {
|
|
@@ -30,9 +31,12 @@ export default (ctx) => {
|
|
|
30
31
|
// schema-slot writes to offsets >= 8 land out-of-bounds.
|
|
31
32
|
const target = takeLiteralTarget()
|
|
32
33
|
const merged = target ? ctx.schema.resolve(target) : null
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// Register the empty schema so schemaId always indexes a real schema.list
|
|
35
|
+
// entry — __json_obj and dyn-get load keys via $__schema_tbl[sid] and would
|
|
36
|
+
// crash on an unregistered id 0 (table left uninitialized when list empty).
|
|
37
|
+
const schemaId = merged ? ctx.schema.idOf(target) : ctx.schema.register([])
|
|
38
|
+
const cap = ctx.abi.object.ops.allocSlots(merged ? merged.length : 0)
|
|
39
|
+
return mkPtrIR(PTR.OBJECT, schemaId, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', cap]])
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
// Flatten comma-grouped props: [',', p1, p2] → [p1, p2]
|
|
@@ -63,7 +67,7 @@ export default (ctx) => {
|
|
|
63
67
|
// R: Static data segment for objects of pure-literal property values (own-memory only).
|
|
64
68
|
// Even with shadow needed, we can skip alloc + N stores; just feed literal values to __dyn_set.
|
|
65
69
|
const shadow = needsDynShadow(target)
|
|
66
|
-
if (values.length >= 2 && !ctx.memory.shared) {
|
|
70
|
+
if (values.length >= 2 && values.length === schema.length && !ctx.memory.shared) {
|
|
67
71
|
const emitted = values.map(emit)
|
|
68
72
|
// asF64 folds i32.const → f64.const so int-literal values also qualify.
|
|
69
73
|
const slots = emitted.map(v => extractF64Bits(asF64(v)))
|
|
@@ -82,16 +86,16 @@ export default (ctx) => {
|
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
const body = [
|
|
85
|
-
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const',
|
|
89
|
+
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', ctx.abi.object.ops.allocSlots(schema.length)]]],
|
|
86
90
|
]
|
|
87
91
|
for (let i = 0; i < values.length; i++)
|
|
88
|
-
body.push(['
|
|
92
|
+
body.push(ctx.abi.object.ops.store(['local.get', `$${t}`], i, asF64(emit(values[i]))))
|
|
89
93
|
body.push(['local.set', `$${ptr}`, mkPtrIR(PTR.OBJECT, schemaId, ['local.get', `$${t}`])])
|
|
90
94
|
if (shadow) {
|
|
91
95
|
inc('__dyn_set')
|
|
92
96
|
for (let i = 0; i < schema.length; i++)
|
|
93
97
|
body.push(['drop', ['call', '$__dyn_set', ['i64.reinterpret_f64', ['local.get', `$${ptr}`]], asI64(emit(['str', String(schema[i])])),
|
|
94
|
-
['
|
|
98
|
+
ctx.abi.object.ops.loadBits(['local.get', `$${t}`], i)]])
|
|
95
99
|
}
|
|
96
100
|
body.push(['local.get', `$${ptr}`])
|
|
97
101
|
|
|
@@ -100,14 +104,82 @@ export default (ctx) => {
|
|
|
100
104
|
|
|
101
105
|
// === Object static methods ===
|
|
102
106
|
|
|
107
|
+
ctx.core.emit['Object.freeze'] = (obj) => asF64(emit(obj))
|
|
108
|
+
|
|
109
|
+
// Object.isExtensible / isSealed / isFrozen.
|
|
110
|
+
// jz fixes an object's schema at construction: a `{…}` literal can
|
|
111
|
+
// neither grow nor lose keys, so an OBJECT value is non-extensible and
|
|
112
|
+
// sealed; its slots stay writable, so it is not frozen. Arrays, maps,
|
|
113
|
+
// sets and hashes grow dynamically → extensible. Primitives are
|
|
114
|
+
// non-objects → ES2015 reports them sealed & frozen, not extensible.
|
|
115
|
+
const extKind = (obj) => {
|
|
116
|
+
const t = typeof obj === 'string' ? lookupValType(obj) : valTypeOf(obj)
|
|
117
|
+
if (t === VAL.OBJECT) return { ext: 0, sealed: 1, frozen: 0 }
|
|
118
|
+
if (t === VAL.NUMBER || t === VAL.STRING || t === VAL.BIGINT) return { ext: 0, sealed: 1, frozen: 1 }
|
|
119
|
+
return { ext: 1, sealed: 0, frozen: 0 }
|
|
120
|
+
}
|
|
121
|
+
const objQuery = (pick) => (obj) => {
|
|
122
|
+
const v = pick(extKind(obj))
|
|
123
|
+
if (obj == null) return typed(['f64.const', v], 'f64')
|
|
124
|
+
return typed(['block', ['result', 'f64'], ['drop', asF64(emit(obj))], ['f64.const', v]], 'f64')
|
|
125
|
+
}
|
|
126
|
+
ctx.core.emit['Object.isExtensible'] = objQuery((k) => k.ext)
|
|
127
|
+
ctx.core.emit['Object.isSealed'] = objQuery((k) => k.sealed)
|
|
128
|
+
ctx.core.emit['Object.isFrozen'] = objQuery((k) => k.frozen)
|
|
129
|
+
|
|
130
|
+
// RequireObjectCoercible: Object.keys/values/entries reject null & undefined
|
|
131
|
+
// with a TypeError. A literal lowers to a [null, value] node — so `null` is
|
|
132
|
+
// [null, null] and `undefined` is [null, undefined] (both JSON-print alike);
|
|
133
|
+
// a missing argument arrives as JS undefined. Anything else (incl.
|
|
134
|
+
// booleans/numbers, which JS boxes) is left to the normal path.
|
|
135
|
+
const isNullishLiteral = (node) => node === undefined
|
|
136
|
+
|| (Array.isArray(node) && node.length === 2 && node[0] == null && node[1] == null)
|
|
137
|
+
const requireCoercible = (node) => {
|
|
138
|
+
if (!isNullishLiteral(node)) return null
|
|
139
|
+
ctx.runtime.throws = true
|
|
140
|
+
return typed(['block', ['result', 'f64'], ['throw', '$__jz_err', ['f64.const', 0]]], 'f64')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Arrays and (coerced) strings expose their indices as own enumerable
|
|
144
|
+
// keys — Object.keys/entries iterate "0".."n-1". `arrayValType` mirrors
|
|
145
|
+
// `stringValType` below: a string arg is a variable name, anything else
|
|
146
|
+
// an AST node.
|
|
147
|
+
const arrayValType = (obj) => (typeof obj === 'string' ? lookupValType(obj) : valTypeOf(obj)) === VAL.ARRAY
|
|
148
|
+
// Index-string key array for an array-like receiver. `lenCall` is the
|
|
149
|
+
// length builtin: __len for jz arrays, __str_len for strings.
|
|
150
|
+
const emitIndexKeys = (obj, lenCall) => {
|
|
151
|
+
inc(lenCall, '__to_str')
|
|
152
|
+
const v = temp('ik'), i = tempI32('iki'), len = tempI32('ikl')
|
|
153
|
+
const vPtr = () => ['i64.reinterpret_f64', ['local.get', `$${v}`]]
|
|
154
|
+
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${len}`], tag: 'ik' })
|
|
155
|
+
const id = ctx.func.uniq++
|
|
156
|
+
return typed(['block', ['result', 'f64'],
|
|
157
|
+
['local.set', `$${v}`, asF64(emit(obj))],
|
|
158
|
+
['local.set', `$${len}`, ['call', `$${lenCall}`, vPtr()]],
|
|
159
|
+
out.init,
|
|
160
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
161
|
+
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
162
|
+
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
163
|
+
elemStore(out.local, i, ['f64.reinterpret_i64',
|
|
164
|
+
['call', '$__to_str', ['i64.reinterpret_f64', ['f64.convert_i32_s', ['local.get', `$${i}`]]]]]),
|
|
165
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
166
|
+
['br', `$loop${id}`]]],
|
|
167
|
+
out.ptr], 'f64')
|
|
168
|
+
}
|
|
169
|
+
|
|
103
170
|
ctx.core.emit['Object.keys'] = (obj) => {
|
|
171
|
+
const nullish = requireCoercible(obj)
|
|
172
|
+
if (nullish) return nullish
|
|
104
173
|
if (isHashTyped(obj)) return emitHashKeys(obj)
|
|
174
|
+
if (arrayValType(obj)) return emitIndexKeys(obj, '__len')
|
|
175
|
+
if (stringValType(obj)) return emitIndexKeys(obj, '__str_len')
|
|
105
176
|
const schema = resolveSchema(obj)
|
|
106
177
|
if (schema) return emitStringArray(schema)
|
|
107
178
|
// Receiver type unknown at compile time. Dispatch on ptr-type at
|
|
108
179
|
// runtime: HASH walks the probe table, anything else returns [].
|
|
109
180
|
return emitRuntimeKeys(obj)
|
|
110
181
|
}
|
|
182
|
+
ctx.core.emit['Object.getOwnPropertyNames'] = ctx.core.emit['Object.keys']
|
|
111
183
|
|
|
112
184
|
// Object.prototype.hasOwnProperty(key) — own-property presence check.
|
|
113
185
|
// Compile-time fold for literal keys against object literals or variables
|
|
@@ -132,8 +204,36 @@ export default (ctx) => {
|
|
|
132
204
|
ctx.core.emit[`.${VAL.ARRAY}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
133
205
|
ctx.core.emit[`.${VAL.STRING}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
134
206
|
ctx.core.emit[`.${VAL.CLOSURE}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
207
|
+
// Object.hasOwn(o, k) — ES2022 static equivalent of o.hasOwnProperty(k).
|
|
208
|
+
// Reuses the same own-property emitter; receiver-type variants above apply.
|
|
209
|
+
ctx.core.emit['Object.hasOwn'] = (obj, key) => ctx.core.emit['.hasOwnProperty'](obj, key)
|
|
210
|
+
|
|
211
|
+
// String primitives are coerced to exotic String objects whose own enumerable
|
|
212
|
+
// properties are the indexed characters. Object.values/entries iterate them.
|
|
213
|
+
const stringValType = (obj) => (typeof obj === 'string' ? lookupValType(obj) : valTypeOf(obj)) === VAL.STRING
|
|
135
214
|
|
|
136
215
|
ctx.core.emit['Object.values'] = (obj) => {
|
|
216
|
+
const nullish = requireCoercible(obj)
|
|
217
|
+
if (nullish) return nullish
|
|
218
|
+
if (stringValType(obj)) {
|
|
219
|
+
inc('__str_idx', '__str_len')
|
|
220
|
+
const s = temp('osv'), i = tempI32('osvi'), len = tempI32('osvl')
|
|
221
|
+
const sPtr = () => ['i64.reinterpret_f64', ['local.get', `$${s}`]]
|
|
222
|
+
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${len}`], tag: 'osv' })
|
|
223
|
+
const id = ctx.func.uniq++
|
|
224
|
+
return typed(['block', ['result', 'f64'],
|
|
225
|
+
['local.set', `$${s}`, asF64(emit(obj))],
|
|
226
|
+
['local.set', `$${len}`, ['call', '$__str_len', sPtr()]],
|
|
227
|
+
out.init,
|
|
228
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
229
|
+
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
230
|
+
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
231
|
+
elemStore(out.local, i, ['call', '$__str_idx', sPtr(), ['local.get', `$${i}`]]),
|
|
232
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
233
|
+
['br', `$loop${id}`]]],
|
|
234
|
+
out.ptr], 'f64')
|
|
235
|
+
}
|
|
236
|
+
if (arrayValType(obj)) { inc('__arr_from'); return typed(['call', '$__arr_from', asI64(emit(obj))], 'f64') }
|
|
137
237
|
if (isHashTyped(obj)) return emitHashValues(obj)
|
|
138
238
|
const schema = resolveSchema(obj)
|
|
139
239
|
if (!schema) return emitRuntimeValues(obj)
|
|
@@ -144,14 +244,62 @@ export default (ctx) => {
|
|
|
144
244
|
const body = [['local.set', `$${t}`, va], out.init,
|
|
145
245
|
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
146
246
|
for (let i = 0; i < n; i++)
|
|
147
|
-
body.push(['f64.store', slotAddr(out.local, i), ['
|
|
247
|
+
body.push(['f64.store', slotAddr(out.local, i), ctx.abi.object.ops.load(['local.get', `$${base}`], i)])
|
|
148
248
|
body.push(out.ptr)
|
|
149
249
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
150
250
|
}
|
|
151
251
|
|
|
152
252
|
ctx.core.emit['Object.entries'] = (obj) => {
|
|
253
|
+
const nullish = requireCoercible(obj)
|
|
254
|
+
if (nullish) return nullish
|
|
255
|
+
if (stringValType(obj)) {
|
|
256
|
+
inc('__str_idx', '__str_len', '__to_str')
|
|
257
|
+
const s = temp('oes'), i = tempI32('oesi'), len = tempI32('oesl'), pair = tempI32('oep')
|
|
258
|
+
const sPtr = () => ['i64.reinterpret_f64', ['local.get', `$${s}`]]
|
|
259
|
+
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${len}`], tag: 'oes' })
|
|
260
|
+
const id = ctx.func.uniq++
|
|
261
|
+
return typed(['block', ['result', 'f64'],
|
|
262
|
+
['local.set', `$${s}`, asF64(emit(obj))],
|
|
263
|
+
['local.set', `$${len}`, ['call', '$__str_len', sPtr()]],
|
|
264
|
+
out.init,
|
|
265
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
266
|
+
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
267
|
+
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
268
|
+
['local.set', `$${pair}`, ['call', '$__alloc_hdr', ['i32.const', 2], ['i32.const', 2]]],
|
|
269
|
+
['f64.store', slotAddr(pair, 0), ['f64.reinterpret_i64',
|
|
270
|
+
['call', '$__to_str', ['i64.reinterpret_f64', ['f64.convert_i32_s', ['local.get', `$${i}`]]]]]],
|
|
271
|
+
['f64.store', slotAddr(pair, 1), ['call', '$__str_idx', sPtr(), ['local.get', `$${i}`]]],
|
|
272
|
+
elemStore(out.local, i, mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${pair}`])),
|
|
273
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
274
|
+
['br', `$loop${id}`]]],
|
|
275
|
+
out.ptr], 'f64')
|
|
276
|
+
}
|
|
277
|
+
if (arrayValType(obj)) {
|
|
278
|
+
inc('__len', '__to_str', '__ptr_offset', '__alloc_hdr')
|
|
279
|
+
const v = temp('oea'), i = tempI32('oeai'), len = tempI32('oeal'), base = tempI32('oeab'), pair = tempI32('oeap')
|
|
280
|
+
const vPtr = () => ['i64.reinterpret_f64', ['local.get', `$${v}`]]
|
|
281
|
+
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${len}`], tag: 'oea' })
|
|
282
|
+
const id = ctx.func.uniq++
|
|
283
|
+
return typed(['block', ['result', 'f64'],
|
|
284
|
+
['local.set', `$${v}`, asF64(emit(obj))],
|
|
285
|
+
['local.set', `$${len}`, ['call', '$__len', vPtr()]],
|
|
286
|
+
out.init,
|
|
287
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', vPtr()]],
|
|
288
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
289
|
+
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
290
|
+
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
291
|
+
['local.set', `$${pair}`, ['call', '$__alloc_hdr', ['i32.const', 2], ['i32.const', 2]]],
|
|
292
|
+
['f64.store', slotAddr(pair, 0), ['f64.reinterpret_i64',
|
|
293
|
+
['call', '$__to_str', ['i64.reinterpret_f64', ['f64.convert_i32_s', ['local.get', `$${i}`]]]]]],
|
|
294
|
+
['f64.store', slotAddr(pair, 1), elemLoad(base, i)],
|
|
295
|
+
elemStore(out.local, i, mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${pair}`])),
|
|
296
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
297
|
+
['br', `$loop${id}`]]],
|
|
298
|
+
out.ptr], 'f64')
|
|
299
|
+
}
|
|
300
|
+
if (isHashTyped(obj)) return emitHashEntries(obj)
|
|
153
301
|
const schema = resolveSchema(obj)
|
|
154
|
-
if (!schema)
|
|
302
|
+
if (!schema) return emitRuntimeEntries(obj)
|
|
155
303
|
const va = asF64(emit(obj))
|
|
156
304
|
const n = schema.length
|
|
157
305
|
const t = temp('oe'), pair = tempI32('op'), base = tempI32('eb')
|
|
@@ -160,9 +308,9 @@ export default (ctx) => {
|
|
|
160
308
|
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
161
309
|
for (let i = 0; i < n; i++) {
|
|
162
310
|
body.push(
|
|
163
|
-
['local.set', `$${pair}`, ['call', '$__alloc_hdr', ['i32.const', 2], ['i32.const', 2]
|
|
311
|
+
['local.set', `$${pair}`, ['call', '$__alloc_hdr', ['i32.const', 2], ['i32.const', 2]]],
|
|
164
312
|
['f64.store', slotAddr(pair, 0), emit(['str', schema[i]])],
|
|
165
|
-
['f64.store', slotAddr(pair, 1), ['
|
|
313
|
+
['f64.store', slotAddr(pair, 1), ctx.abi.object.ops.load(['local.get', `$${base}`], i)],
|
|
166
314
|
['f64.store', slotAddr(out.local, i), mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${pair}`])])
|
|
167
315
|
}
|
|
168
316
|
body.push(out.ptr)
|
|
@@ -170,6 +318,9 @@ export default (ctx) => {
|
|
|
170
318
|
}
|
|
171
319
|
|
|
172
320
|
ctx.core.emit['Object.assign'] = (target, ...sources) => {
|
|
321
|
+
// RequireObjectCoercible(target) — null/undefined is a TypeError.
|
|
322
|
+
const nullish = requireCoercible(target)
|
|
323
|
+
if (nullish) return nullish
|
|
173
324
|
if (typeof target === 'string') {
|
|
174
325
|
const vt = repOf(target)?.val
|
|
175
326
|
if (vt && vt !== VAL.OBJECT) {
|
|
@@ -182,11 +333,14 @@ export default (ctx) => {
|
|
|
182
333
|
const boxedSchema = ['__inner__', ...allProps]
|
|
183
334
|
const schemaId = ctx.schema.register(boxedSchema)
|
|
184
335
|
ctx.schema.vars.set(target, schemaId)
|
|
336
|
+
// Emit-time rep mutation: Object.assign's target gains a freshly-registered
|
|
337
|
+
// boxed-schema binding here; downstream `.prop` reads in the same emit pass
|
|
338
|
+
// depend on schemaId being live on the rep, not just in ctx.schema.vars.
|
|
185
339
|
updateRep(target, { schemaId })
|
|
186
340
|
const t = tempI32('bx'), s = temp('bs')
|
|
187
341
|
const body = [
|
|
188
|
-
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const',
|
|
189
|
-
|
|
342
|
+
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', ctx.abi.object.ops.allocSlots(boxedSchema.length)]]],
|
|
343
|
+
ctx.abi.object.ops.store(['local.get', `$${t}`], 0, asF64(emit(target))),
|
|
190
344
|
]
|
|
191
345
|
const sBase = tempI32('sb')
|
|
192
346
|
for (const source of sources) {
|
|
@@ -196,7 +350,7 @@ export default (ctx) => {
|
|
|
196
350
|
for (let si = 0; si < sSchema.length; si++) {
|
|
197
351
|
const ti = boxedSchema.indexOf(sSchema[si])
|
|
198
352
|
if (ti < 0) continue
|
|
199
|
-
body.push(['
|
|
353
|
+
body.push(ctx.abi.object.ops.store(['local.get', `$${t}`], ti, ctx.abi.object.ops.load(['local.get', `$${sBase}`], si)))
|
|
200
354
|
}
|
|
201
355
|
}
|
|
202
356
|
body.push(['local.set', `$${target}`,
|
|
@@ -206,20 +360,22 @@ export default (ctx) => {
|
|
|
206
360
|
}
|
|
207
361
|
}
|
|
208
362
|
const tSchema = resolveSchema(target)
|
|
363
|
+
const sourceSchemas = sources.map(resolveSchema)
|
|
209
364
|
if (!tSchema) err('Object.assign: target needs known schema')
|
|
365
|
+
if (sourceSchemas.some(s => !s)) return emitDynamicAssign(target, sources, sourceSchemas)
|
|
210
366
|
const t = temp('at'), s = temp('as')
|
|
211
367
|
const tBase = tempI32('tb'), sBase2 = tempI32('sb')
|
|
212
368
|
const body = [['local.set', `$${t}`, asF64(emit(target))],
|
|
213
369
|
['local.set', `$${tBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
214
|
-
for (
|
|
215
|
-
const
|
|
216
|
-
|
|
370
|
+
for (let i = 0; i < sources.length; i++) {
|
|
371
|
+
const source = sources[i]
|
|
372
|
+
const sSchema = sourceSchemas[i]
|
|
217
373
|
body.push(['local.set', `$${s}`, asF64(emit(source))])
|
|
218
374
|
body.push(['local.set', `$${sBase2}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]])
|
|
219
375
|
for (let si = 0; si < sSchema.length; si++) {
|
|
220
376
|
const ti = tSchema.indexOf(sSchema[si])
|
|
221
377
|
if (ti < 0) continue
|
|
222
|
-
body.push(['
|
|
378
|
+
body.push(ctx.abi.object.ops.store(['local.get', `$${tBase}`], ti, ctx.abi.object.ops.load(['local.get', `$${sBase2}`], si)))
|
|
223
379
|
}
|
|
224
380
|
}
|
|
225
381
|
body.push(['local.get', `$${t}`])
|
|
@@ -230,8 +386,12 @@ export default (ctx) => {
|
|
|
230
386
|
err('Object.defineProperty descriptor semantics are outside jz scope; jzify only folds static bundler export helpers')
|
|
231
387
|
}
|
|
232
388
|
|
|
233
|
-
// Object.fromEntries(arr) → creates HASH from array of [key, value] pairs
|
|
389
|
+
// Object.fromEntries(arr) → creates HASH from array of [key, value] pairs.
|
|
390
|
+
// Spec step 1 is RequireObjectCoercible(iterable): a missing/nullish argument
|
|
391
|
+
// is a TypeError, not an `emit(undefined)` compiler crash.
|
|
234
392
|
ctx.core.emit['Object.fromEntries'] = (arr) => {
|
|
393
|
+
const nullishThrow = requireCoercible(arr)
|
|
394
|
+
if (nullishThrow) return nullishThrow
|
|
235
395
|
inc('__hash_new', '__hash_set')
|
|
236
396
|
inc('__str_hash', '__str_eq')
|
|
237
397
|
const va = asF64(emit(arr))
|
|
@@ -266,6 +426,7 @@ export default (ctx) => {
|
|
|
266
426
|
// Header propsPtr lives at $off-16 (current ARRAY layout). We alias src's hash
|
|
267
427
|
// by copying the slot; __dyn_move covers the shifted-array case where props
|
|
268
428
|
// were migrated to the global __dyn_props.
|
|
429
|
+
includeModule('array')
|
|
269
430
|
inc('__arr_from', '__dyn_move', '__ptr_offset')
|
|
270
431
|
const src = temp('ocs')
|
|
271
432
|
const dst = temp('ocd')
|
|
@@ -288,6 +449,7 @@ export default (ctx) => {
|
|
|
288
449
|
if (!schema) {
|
|
289
450
|
if (protoType == null) {
|
|
290
451
|
const value = temp('ocr')
|
|
452
|
+
includeModule('array')
|
|
291
453
|
inc('__arr_from', '__dyn_move', '__ptr_offset')
|
|
292
454
|
const dst2 = temp('ocd')
|
|
293
455
|
const srcOff2 = tempI32('ocso')
|
|
@@ -317,12 +479,12 @@ export default (ctx) => {
|
|
|
317
479
|
const srcBase = tempI32('cb')
|
|
318
480
|
const body = [
|
|
319
481
|
['local.set', `$${s}`, asF64(emit(proto))],
|
|
320
|
-
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const',
|
|
482
|
+
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', ctx.abi.object.ops.allocSlots(n)]]],
|
|
321
483
|
['local.set', `$${srcBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]],
|
|
322
484
|
]
|
|
323
485
|
// Copy all properties from proto
|
|
324
486
|
for (let i = 0; i < n; i++)
|
|
325
|
-
body.push(['
|
|
487
|
+
body.push(ctx.abi.object.ops.store(['local.get', `$${t}`], i, ctx.abi.object.ops.load(['local.get', `$${srcBase}`], i)))
|
|
326
488
|
body.push(mkPtrIR(PTR.OBJECT, schemaId, ['local.get', `$${t}`]))
|
|
327
489
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
328
490
|
}
|
|
@@ -330,6 +492,57 @@ export default (ctx) => {
|
|
|
330
492
|
|
|
331
493
|
// --- Helpers ---
|
|
332
494
|
|
|
495
|
+
// Used only after the target schema is known. Unknown HASH targets can grow by
|
|
496
|
+
// returning a new pointer, which would not preserve aliases to the old value.
|
|
497
|
+
function emitDynamicAssign(target, sources, sourceSchemas = sources.map(resolveSchema)) {
|
|
498
|
+
includeModule('collection')
|
|
499
|
+
inc('__hash_set', '__dyn_get_any', '__ptr_offset', '__len')
|
|
500
|
+
const t = temp('adt'), s = temp('ads'), sBase = tempI32('adsb')
|
|
501
|
+
const keys = temp('adk'), keysBase = tempI32('adkb'), len = tempI32('adn')
|
|
502
|
+
const i = tempI32('adi'), key = temp('adkey')
|
|
503
|
+
const id = ctx.func.uniq++
|
|
504
|
+
const body = [['local.set', `$${t}`, asF64(emit(target))]]
|
|
505
|
+
|
|
506
|
+
for (let si = 0; si < sources.length; si++) {
|
|
507
|
+
const source = sources[si]
|
|
508
|
+
const sSchema = sourceSchemas[si]
|
|
509
|
+
body.push(['local.set', `$${s}`, asF64(emit(source))])
|
|
510
|
+
if (sSchema) {
|
|
511
|
+
body.push(['local.set', `$${sBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]])
|
|
512
|
+
for (let pi = 0; pi < sSchema.length; pi++)
|
|
513
|
+
body.push(['local.set', `$${t}`, ['f64.reinterpret_i64',
|
|
514
|
+
['call', '$__hash_set', ['i64.reinterpret_f64', ['local.get', `$${t}`]],
|
|
515
|
+
asI64(emit(['str', String(sSchema[pi])])),
|
|
516
|
+
ctx.abi.object.ops.loadBits(['local.get', `$${sBase}`], pi)]]])
|
|
517
|
+
continue
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
body.push(
|
|
521
|
+
['local.set', `$${keys}`, runtimeKeysFromTemp(s, 'adk')],
|
|
522
|
+
['local.set', `$${keysBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${keys}`]]]],
|
|
523
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${keys}`]]]],
|
|
524
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
525
|
+
['block', `$adbrk${id}_${si}`, ['loop', `$adloop${id}_${si}`,
|
|
526
|
+
['br_if', `$adbrk${id}_${si}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
527
|
+
['local.set', `$${key}`, ['f64.load',
|
|
528
|
+
['i32.add', ['local.get', `$${keysBase}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]],
|
|
529
|
+
['local.set', `$${t}`, ['f64.reinterpret_i64',
|
|
530
|
+
['call', '$__hash_set',
|
|
531
|
+
['i64.reinterpret_f64', ['local.get', `$${t}`]],
|
|
532
|
+
['i64.reinterpret_f64', ['local.get', `$${key}`]],
|
|
533
|
+
['call', '$__dyn_get_any',
|
|
534
|
+
['i64.reinterpret_f64', ['local.get', `$${s}`]],
|
|
535
|
+
['i64.reinterpret_f64', ['local.get', `$${key}`]]]]]],
|
|
536
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
537
|
+
['br', `$adloop${id}_${si}`]]])
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (typeof target === 'string' && ctx.func.locals.get(target) === 'f64')
|
|
541
|
+
body.push(['local.set', `$${target}`, ['local.get', `$${t}`]])
|
|
542
|
+
body.push(['local.get', `$${t}`])
|
|
543
|
+
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
544
|
+
}
|
|
545
|
+
|
|
333
546
|
function resolveSchema(obj) {
|
|
334
547
|
if (typeof obj === 'string') return ctx.schema.resolve(obj)
|
|
335
548
|
if (Array.isArray(obj) && obj[0] === '{}')
|
|
@@ -337,7 +550,7 @@ function resolveSchema(obj) {
|
|
|
337
550
|
// JSON-shape inferred: JSON.parse(constStr) call or `.prop`/`[i]` chain
|
|
338
551
|
// resolving to a known OBJECT shape carries its key list as `names`.
|
|
339
552
|
const sh = shapeOf(obj)
|
|
340
|
-
if (sh?.
|
|
553
|
+
if (sh?.val === VAL.OBJECT && sh.names) return sh.names
|
|
341
554
|
return null
|
|
342
555
|
}
|
|
343
556
|
|
|
@@ -378,7 +591,7 @@ function emitObjectSpread(props, spreadTarget = takeLiteralTarget()) {
|
|
|
378
591
|
const ptr = temp('objp')
|
|
379
592
|
const src = tempI32('osp')
|
|
380
593
|
|
|
381
|
-
const body = [['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const',
|
|
594
|
+
const body = [['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', ctx.abi.object.ops.allocSlots(schema.length)]]]]
|
|
382
595
|
|
|
383
596
|
// Process props in order — later props override earlier (JS semantics)
|
|
384
597
|
let srcF
|
|
@@ -387,17 +600,17 @@ function emitObjectSpread(props, spreadTarget = takeLiteralTarget()) {
|
|
|
387
600
|
const sSchema = resolveSchema(p[1])
|
|
388
601
|
if (!sSchema) {
|
|
389
602
|
// Unknown-schema source (e.g. parameter). Override each slot via runtime
|
|
390
|
-
// __dyn_get_or using existing value as fallback.
|
|
391
|
-
|
|
603
|
+
// __dyn_get_or using existing value as fallback.
|
|
604
|
+
includeModule('collection')
|
|
392
605
|
inc('__dyn_get_or')
|
|
393
606
|
srcF ??= temp('ospf')
|
|
394
607
|
body.push(['local.set', `$${srcF}`, asF64(emit(p[1]))])
|
|
395
608
|
for (let i = 0; i < schema.length; i++) {
|
|
396
|
-
const
|
|
397
|
-
body.push(
|
|
609
|
+
const base = ['local.get', `$${t}`]
|
|
610
|
+
body.push(ctx.abi.object.ops.store(base, i,
|
|
398
611
|
['f64.reinterpret_i64', ['call', '$__dyn_get_or', ['i64.reinterpret_f64', ['local.get', `$${srcF}`]],
|
|
399
612
|
asI64(emit(['str', String(schema[i])])),
|
|
400
|
-
|
|
613
|
+
ctx.abi.object.ops.loadBits(base, i)]]))
|
|
401
614
|
}
|
|
402
615
|
continue
|
|
403
616
|
}
|
|
@@ -405,11 +618,11 @@ function emitObjectSpread(props, spreadTarget = takeLiteralTarget()) {
|
|
|
405
618
|
for (let si = 0; si < sSchema.length; si++) {
|
|
406
619
|
const ti = schema.indexOf(sSchema[si])
|
|
407
620
|
if (ti < 0) continue
|
|
408
|
-
body.push(['
|
|
621
|
+
body.push(ctx.abi.object.ops.store(['local.get', `$${t}`], ti, ctx.abi.object.ops.load(['local.get', `$${src}`], si)))
|
|
409
622
|
}
|
|
410
623
|
} else if (Array.isArray(p) && p[0] === ':') {
|
|
411
624
|
const ti = schema.indexOf(p[1])
|
|
412
|
-
if (ti >= 0) body.push(['
|
|
625
|
+
if (ti >= 0) body.push(ctx.abi.object.ops.store(['local.get', `$${t}`], ti, asF64(emit(p[2]))))
|
|
413
626
|
}
|
|
414
627
|
}
|
|
415
628
|
|
|
@@ -418,7 +631,7 @@ function emitObjectSpread(props, spreadTarget = takeLiteralTarget()) {
|
|
|
418
631
|
inc('__dyn_set')
|
|
419
632
|
for (let i = 0; i < schema.length; i++)
|
|
420
633
|
body.push(['drop', ['call', '$__dyn_set', ['i64.reinterpret_f64', ['local.get', `$${ptr}`]], asI64(emit(['str', String(schema[i])])),
|
|
421
|
-
['
|
|
634
|
+
ctx.abi.object.ops.loadBits(['local.get', `$${t}`], i)]])
|
|
422
635
|
}
|
|
423
636
|
body.push(['local.get', `$${ptr}`])
|
|
424
637
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
@@ -462,6 +675,13 @@ function emitHashValues(obj) {
|
|
|
462
675
|
hashValuesFromTemp(t)], 'f64')
|
|
463
676
|
}
|
|
464
677
|
|
|
678
|
+
function emitHashEntries(obj) {
|
|
679
|
+
const t = temp('he')
|
|
680
|
+
return typed(['block', ['result', 'f64'],
|
|
681
|
+
['local.set', `$${t}`, asF64(emit(obj))],
|
|
682
|
+
hashEntriesFromTemp(t)], 'f64')
|
|
683
|
+
}
|
|
684
|
+
|
|
465
685
|
// Inline body of the HASH walk against an already-bound f64 local. Shared by
|
|
466
686
|
// the static-HASH path and the runtime-dispatch path so both produce the same
|
|
467
687
|
// IR shape from the same source — only difference is whether they enter from
|
|
@@ -520,22 +740,59 @@ function hashValuesFromTemp(t) {
|
|
|
520
740
|
out.ptr]
|
|
521
741
|
}
|
|
522
742
|
|
|
743
|
+
function hashEntriesFromTemp(t) {
|
|
744
|
+
inc('__ptr_offset', '__cap', '__len', '__alloc_hdr')
|
|
745
|
+
const off = tempI32('heo'), cap = tempI32('hec'), n = tempI32('hen')
|
|
746
|
+
const i = tempI32('hei'), o = tempI32('hej'), slot = tempI32('hes'), pair = tempI32('hep')
|
|
747
|
+
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${n}`], tag: 'hea' })
|
|
748
|
+
const id = ctx.func.uniq++
|
|
749
|
+
return ['block', ['result', 'f64'],
|
|
750
|
+
['local.set', `$${n}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
751
|
+
out.init,
|
|
752
|
+
['local.set', `$${off}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
753
|
+
['local.set', `$${cap}`, ['call', '$__cap', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
754
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
755
|
+
['local.set', `$${o}`, ['i32.const', 0]],
|
|
756
|
+
['block', `$ebrk${id}`, ['loop', `$eloop${id}`,
|
|
757
|
+
['br_if', `$ebrk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${cap}`]]],
|
|
758
|
+
['local.set', `$${slot}`, ['i32.add', ['local.get', `$${off}`],
|
|
759
|
+
['i32.mul', ['local.get', `$${i}`], ['i32.const', 24]]]],
|
|
760
|
+
['if', ['f64.ne', ['f64.load', ['local.get', `$${slot}`]], ['f64.const', 0]],
|
|
761
|
+
['then',
|
|
762
|
+
['local.set', `$${pair}`, ['call', '$__alloc_hdr', ['i32.const', 2], ['i32.const', 2]]],
|
|
763
|
+
['f64.store', ['local.get', `$${pair}`],
|
|
764
|
+
['f64.load', ['i32.add', ['local.get', `$${slot}`], ['i32.const', 8]]]],
|
|
765
|
+
['f64.store', ['i32.add', ['local.get', `$${pair}`], ['i32.const', 8]],
|
|
766
|
+
['f64.load', ['i32.add', ['local.get', `$${slot}`], ['i32.const', 16]]]],
|
|
767
|
+
elemStore(out.local, o, mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${pair}`])),
|
|
768
|
+
['local.set', `$${o}`, ['i32.add', ['local.get', `$${o}`], ['i32.const', 1]]]]],
|
|
769
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
770
|
+
['br', `$eloop${id}`]]],
|
|
771
|
+
out.ptr]
|
|
772
|
+
}
|
|
773
|
+
|
|
523
774
|
// Type-unknown receiver: bind the value, branch on ptr-type. HASH walks the
|
|
524
775
|
// probe table; OBJECT loads the schema's key array (registered statically at
|
|
525
776
|
// compile time or lazily at runtime by JSON.parse via __jp_schema_get); other
|
|
526
777
|
// types (ARRAY, nullish, primitives) return an empty array. The empty-array
|
|
527
778
|
// fallback is allocated in all arms for type uniformity at the if boundary.
|
|
528
779
|
function emitRuntimeKeys(obj) {
|
|
780
|
+
const t = temp('rk')
|
|
781
|
+
return typed(['block', ['result', 'f64'],
|
|
782
|
+
['local.set', `$${t}`, asF64(emit(obj))],
|
|
783
|
+
runtimeKeysFromTemp(t, 'rk')], 'f64')
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function runtimeKeysFromTemp(t, tag) {
|
|
529
787
|
inc('__ptr_type')
|
|
530
788
|
// Ensure the schema table global exists even in programs that never use
|
|
531
789
|
// JSON.parse or compile-time schemas — the OBJECT arm reads it at runtime
|
|
532
790
|
// and the watr resolver requires the symbol to be declared.
|
|
533
791
|
if (!ctx.scope.globals.has('__schema_tbl'))
|
|
534
792
|
ctx.scope.globals.set('__schema_tbl', '(global $__schema_tbl (mut i32) (i32.const 0))')
|
|
535
|
-
const
|
|
536
|
-
const empty = allocPtr({ type: PTR.ARRAY, len: 0, tag:
|
|
537
|
-
return
|
|
538
|
-
['local.set', `$${t}`, asF64(emit(obj))],
|
|
793
|
+
const tt = tempI32(`${tag}t`)
|
|
794
|
+
const empty = allocPtr({ type: PTR.ARRAY, len: 0, tag: `${tag}e` })
|
|
795
|
+
return ['block', ['result', 'f64'],
|
|
539
796
|
['local.set', `$${tt}`, ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
540
797
|
['if', ['result', 'f64'],
|
|
541
798
|
['i32.eq', ['local.get', `$${tt}`], ['i32.const', PTR.HASH]],
|
|
@@ -545,7 +802,7 @@ function emitRuntimeKeys(obj) {
|
|
|
545
802
|
['i32.eq', ['local.get', `$${tt}`], ['i32.const', PTR.OBJECT]],
|
|
546
803
|
['i32.ne', ['global.get', '$__schema_tbl'], ['i32.const', 0]]],
|
|
547
804
|
['then', objectKeysFromTemp(t)],
|
|
548
|
-
['else', ['block', ['result', 'f64'], empty.init, empty.ptr]]]]]]
|
|
805
|
+
['else', ['block', ['result', 'f64'], empty.init, empty.ptr]]]]]]
|
|
549
806
|
}
|
|
550
807
|
|
|
551
808
|
function emitRuntimeValues(obj) {
|
|
@@ -568,6 +825,26 @@ function emitRuntimeValues(obj) {
|
|
|
568
825
|
['else', ['block', ['result', 'f64'], empty.init, empty.ptr]]]]]], 'f64')
|
|
569
826
|
}
|
|
570
827
|
|
|
828
|
+
function emitRuntimeEntries(obj) {
|
|
829
|
+
inc('__ptr_type')
|
|
830
|
+
if (!ctx.scope.globals.has('__schema_tbl'))
|
|
831
|
+
ctx.scope.globals.set('__schema_tbl', '(global $__schema_tbl (mut i32) (i32.const 0))')
|
|
832
|
+
const t = temp('re'), tt = tempI32('ret')
|
|
833
|
+
const empty = allocPtr({ type: PTR.ARRAY, len: 0, tag: 'ree' })
|
|
834
|
+
return typed(['block', ['result', 'f64'],
|
|
835
|
+
['local.set', `$${t}`, asF64(emit(obj))],
|
|
836
|
+
['local.set', `$${tt}`, ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
837
|
+
['if', ['result', 'f64'],
|
|
838
|
+
['i32.eq', ['local.get', `$${tt}`], ['i32.const', PTR.HASH]],
|
|
839
|
+
['then', hashEntriesFromTemp(t)],
|
|
840
|
+
['else', ['if', ['result', 'f64'],
|
|
841
|
+
['i32.and',
|
|
842
|
+
['i32.eq', ['local.get', `$${tt}`], ['i32.const', PTR.OBJECT]],
|
|
843
|
+
['i32.ne', ['global.get', '$__schema_tbl'], ['i32.const', 0]]],
|
|
844
|
+
['then', objectEntriesFromTemp(t)],
|
|
845
|
+
['else', ['block', ['result', 'f64'], empty.init, empty.ptr]]]]]], 'f64')
|
|
846
|
+
}
|
|
847
|
+
|
|
571
848
|
// Schema-keyed Object.keys: copy the schema's keys array (a jz Array of
|
|
572
849
|
// STRINGs registered in __schema_tbl[sid]) into a fresh ARRAY so callers can
|
|
573
850
|
// mutate without aliasing the schema substrate.
|
|
@@ -584,7 +861,7 @@ function objectKeysFromTemp(t) {
|
|
|
584
861
|
['i64.const', LAYOUT.OFFSET_MASK]]]],
|
|
585
862
|
['local.set', `$${n}`, ['i32.load', ['i32.sub', ['local.get', `$${src}`], ['i32.const', 8]]]],
|
|
586
863
|
['local.set', `$${out}`, ['call', '$__alloc_hdr',
|
|
587
|
-
['local.get', `$${n}`], ['local.get', `$${n}`]
|
|
864
|
+
['local.get', `$${n}`], ['local.get', `$${n}`]]],
|
|
588
865
|
['local.set', `$${i}`, ['i32.const', 0]],
|
|
589
866
|
['block', `$kbrk${id}`, ['loop', `$kloop${id}`,
|
|
590
867
|
['br_if', `$kbrk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${n}`]]],
|
|
@@ -612,7 +889,7 @@ function objectValuesFromTemp(t) {
|
|
|
612
889
|
['local.set', `$${n}`, ['i32.load', ['i32.sub', ['local.get', `$${src}`], ['i32.const', 8]]]],
|
|
613
890
|
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
614
891
|
['local.set', `$${out}`, ['call', '$__alloc_hdr',
|
|
615
|
-
['local.get', `$${n}`], ['local.get', `$${n}`]
|
|
892
|
+
['local.get', `$${n}`], ['local.get', `$${n}`]]],
|
|
616
893
|
['local.set', `$${i}`, ['i32.const', 0]],
|
|
617
894
|
['block', `$ovbrk${id}`, ['loop', `$ovloop${id}`,
|
|
618
895
|
['br_if', `$ovbrk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${n}`]]],
|
|
@@ -624,3 +901,39 @@ function objectValuesFromTemp(t) {
|
|
|
624
901
|
['br', `$ovloop${id}`]]],
|
|
625
902
|
mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${out}`])]
|
|
626
903
|
}
|
|
904
|
+
|
|
905
|
+
function objectEntriesFromTemp(t) {
|
|
906
|
+
inc('__alloc_hdr', '__ptr_offset')
|
|
907
|
+
const sid = tempI32('oes'), src = tempI32('oesrc'), n = tempI32('oen')
|
|
908
|
+
const base = tempI32('oebase'), out = tempI32('oeo'), i = tempI32('oei'), pair = tempI32('oep')
|
|
909
|
+
const id = ctx.func.uniq++
|
|
910
|
+
return ['block', ['result', 'f64'],
|
|
911
|
+
['local.set', `$${sid}`, ['i32.wrap_i64', ['i64.and',
|
|
912
|
+
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const', LAYOUT.AUX_SHIFT]],
|
|
913
|
+
['i64.const', LAYOUT.AUX_MASK]]]],
|
|
914
|
+
['local.set', `$${src}`, ['i32.wrap_i64', ['i64.and',
|
|
915
|
+
['i64.load', ['i32.add', ['global.get', '$__schema_tbl'], ['i32.shl', ['local.get', `$${sid}`], ['i32.const', 3]]]],
|
|
916
|
+
['i64.const', LAYOUT.OFFSET_MASK]]]],
|
|
917
|
+
['local.set', `$${n}`, ['i32.load', ['i32.sub', ['local.get', `$${src}`], ['i32.const', 8]]]],
|
|
918
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
919
|
+
['local.set', `$${out}`, ['call', '$__alloc_hdr',
|
|
920
|
+
['local.get', `$${n}`], ['local.get', `$${n}`]]],
|
|
921
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
922
|
+
['block', `$oebrk${id}`, ['loop', `$oeloop${id}`,
|
|
923
|
+
['br_if', `$oebrk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${n}`]]],
|
|
924
|
+
['local.set', `$${pair}`, ['call', '$__alloc_hdr', ['i32.const', 2], ['i32.const', 2]]],
|
|
925
|
+
['i64.store',
|
|
926
|
+
['local.get', `$${pair}`],
|
|
927
|
+
['i64.load',
|
|
928
|
+
['i32.add', ['local.get', `$${src}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]],
|
|
929
|
+
['f64.store',
|
|
930
|
+
['i32.add', ['local.get', `$${pair}`], ['i32.const', 8]],
|
|
931
|
+
['f64.load',
|
|
932
|
+
['i32.add', ['local.get', `$${base}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]],
|
|
933
|
+
['f64.store',
|
|
934
|
+
['i32.add', ['local.get', `$${out}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]],
|
|
935
|
+
mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${pair}`])],
|
|
936
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
937
|
+
['br', `$oeloop${id}`]]],
|
|
938
|
+
mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${out}`])]
|
|
939
|
+
}
|