jz 0.7.0 → 0.8.1
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 +37 -33
- package/bench/README.md +176 -73
- package/bench/bench.svg +58 -71
- package/cli.js +12 -5
- package/dist/interop.js +1 -1
- package/dist/jz.js +1366 -1101
- package/index.js +40 -9
- package/interop.js +193 -138
- package/layout.js +29 -18
- package/module/array.js +49 -73
- package/module/collection.js +83 -25
- package/module/console.js +1 -1
- package/module/core.js +161 -15
- package/module/json.js +3 -3
- package/module/math.js +167 -117
- package/module/number.js +247 -13
- package/module/object.js +11 -5
- package/module/regex.js +8 -7
- package/module/string.js +295 -171
- package/module/typedarray.js +169 -105
- package/package.json +7 -3
- package/src/abi/string.js +40 -35
- package/src/ast.js +19 -2
- package/src/compile/analyze.js +64 -2
- package/src/compile/cse-load.js +200 -0
- package/src/compile/emit-assign.js +73 -14
- package/src/compile/emit.js +324 -34
- package/src/compile/index.js +204 -61
- package/src/compile/infer.js +8 -1
- package/src/compile/loop-divmod.js +12 -58
- package/src/compile/loop-model.js +91 -0
- package/src/compile/loop-recurrence.js +167 -0
- package/src/compile/loop-square.js +102 -0
- package/src/compile/narrow.js +180 -34
- package/src/compile/peel-stencil.js +18 -64
- package/src/compile/plan/common.js +29 -0
- package/src/compile/plan/index.js +4 -1
- package/src/compile/plan/inline.js +176 -21
- package/src/compile/plan/literals.js +93 -19
- package/src/ctx.js +51 -12
- package/src/helper-counters.js +137 -0
- package/src/ir.js +102 -13
- package/src/kind-traits.js +7 -3
- package/src/kind.js +14 -1
- package/src/op-policy.js +5 -2
- package/src/ops.js +119 -0
- package/src/optimize/index.js +1125 -136
- package/src/optimize/recurse.js +182 -0
- package/src/optimize/vectorize.js +1302 -144
- package/src/prepare/index.js +29 -12
- package/src/reps.js +4 -1
- package/src/type.js +53 -45
- package/src/wat/assemble.js +92 -9
- package/src/widen.js +21 -0
- package/src/wat/optimize.js +0 -3938
package/module/core.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { typed, asF64, asI32, asI64, NULL_NAN, UNDEF_NAN, FALSE_NAN, TRUE_NAN, temp, usesDynProps, ptrOffsetIR, isNullish, valKindToPtr, sidecarOverride, undefExpr } from '../src/ir.js'
|
|
13
|
-
import { emit, spread, deps } from '../src/bridge.js'
|
|
13
|
+
import { emit, spread, deps, wat } from '../src/bridge.js'
|
|
14
14
|
import { reconstructArgsWithSpreads } from '../src/ir.js'
|
|
15
15
|
import { valTypeOf, shapeOf } from '../src/kind.js'
|
|
16
16
|
import { T } from '../src/ast.js'
|
|
@@ -36,11 +36,12 @@ export default (ctx) => {
|
|
|
36
36
|
__is_str_key: ['__ptr_type'],
|
|
37
37
|
__str_len: ['__ptr_type', '__ptr_offset', '__ptr_aux'],
|
|
38
38
|
__set_len: ['__ptr_offset_fwd'],
|
|
39
|
-
__length: ['__ptr_type', '
|
|
39
|
+
__length: ['__ptr_type', '__str_len', '__len'],
|
|
40
40
|
__alloc: ['__memgrow'],
|
|
41
41
|
__alloc_hdr: ['__alloc'],
|
|
42
42
|
__alloc_hdr_n: ['__alloc'],
|
|
43
43
|
__coll_order: ['__alloc'],
|
|
44
|
+
__obj_clone: ['__ptr_type', '__ptr_aux', '__ptr_offset', '__len', '__cap', '__alloc_hdr', '__alloc_hdr_n', '__mkptr'],
|
|
44
45
|
})
|
|
45
46
|
|
|
46
47
|
ctx.core.stdlib['__is_nullish'] = `(func $__is_nullish (param $v i64) (result i32)
|
|
@@ -143,6 +144,79 @@ export default (ctx) => {
|
|
|
143
144
|
(i64.shl (i64.and (i64.extend_i32_u (local.get $aux)) (i64.const ${LAYOUT.AUX_MASK})) (i64.const ${LAYOUT.AUX_SHIFT}))
|
|
144
145
|
(i64.and (i64.extend_i32_u (local.get $offset)) (i64.const ${LAYOUT.OFFSET_MASK})))))))`
|
|
145
146
|
|
|
147
|
+
// Relative-index clamp to `[0, len]` — the JS `RelativeIndex`/`ToIntegerOrInfinity`
|
|
148
|
+
// bounds dance shared by slice/subarray/fill/copyWithin (string + typed + array).
|
|
149
|
+
// Single shared body so N method bodies don't each inline the same six branches.
|
|
150
|
+
wat('__clamp_idx', `(func $__clamp_idx (param $v i32) (param $len i32) (result i32)
|
|
151
|
+
(if (i32.lt_s (local.get $v) (i32.const 0)) (then (local.set $v (i32.add (local.get $v) (local.get $len)))))
|
|
152
|
+
(if (i32.lt_s (local.get $v) (i32.const 0)) (then (local.set $v (i32.const 0))))
|
|
153
|
+
(if (i32.gt_s (local.get $v) (local.get $len)) (then (local.set $v (local.get $len))))
|
|
154
|
+
(local.get $v))`)
|
|
155
|
+
|
|
156
|
+
// Polymorphic element read for any heap-indexable (ARRAY or TYPED). The one
|
|
157
|
+
// home for `arr[i]` lowering: ARRAY and typed reads both route here, plain-array
|
|
158
|
+
// programs get the ARRAY-only collapse, typed programs the full elem dispatch.
|
|
159
|
+
ctx.core.stdlib['__typed_idx'] = () => {
|
|
160
|
+
if (!ctx.features.typedarray && !ctx.features.external) {
|
|
161
|
+
return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
|
|
162
|
+
(local $len i32)
|
|
163
|
+
(local.set $len (call $__len (local.get $ptr)))
|
|
164
|
+
(if (result f64)
|
|
165
|
+
(i32.or
|
|
166
|
+
(i32.lt_s (local.get $i) (i32.const 0))
|
|
167
|
+
(i32.ge_u (local.get $i) (local.get $len)))
|
|
168
|
+
(then (f64.const nan:${UNDEF_NAN}))
|
|
169
|
+
(else (f64.load (i32.add (call $__ptr_offset (local.get $ptr)) (i32.shl (local.get $i) (i32.const 3)))))))`
|
|
170
|
+
}
|
|
171
|
+
// Hot (~37M calls in watr self-host). Type/aux/offset extracted once from $ptr.
|
|
172
|
+
return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
|
|
173
|
+
(local $t i32) (local $off i32) (local $et i32) (local $len i32) (local $aux i32)
|
|
174
|
+
(local.set $t (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
175
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
176
|
+
;; ARRAY fast path: follow forwarding inline, bounds-check against header len, f64.load — no $__len call.
|
|
177
|
+
(if (i32.and (i32.eq (local.get $t) (i32.const ${PTR.ARRAY})) (i32.ge_u (local.get $off) (i32.const 8)))
|
|
178
|
+
(then
|
|
179
|
+
${followForwardingWat('$off', { lowGuard: false })}
|
|
180
|
+
(return (if (result f64)
|
|
181
|
+
(i32.and (i32.ge_s (local.get $i) (i32.const 0)) (i32.lt_u (local.get $i) (i32.load (i32.sub (local.get $off) (i32.const 8)))))
|
|
182
|
+
(then (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
183
|
+
(else (f64.const nan:${UNDEF_NAN}))))))
|
|
184
|
+
(local.set $aux (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})) (i64.const ${LAYOUT.AUX_MASK}))))
|
|
185
|
+
(if
|
|
186
|
+
(i32.and
|
|
187
|
+
(i32.eq (local.get $t) (i32.const ${PTR.TYPED}))
|
|
188
|
+
(i32.ne (i32.and (local.get $aux) (i32.const 8)) (i32.const 0)))
|
|
189
|
+
(then (local.set $off (i32.load (i32.add (local.get $off) (i32.const 4))))))
|
|
190
|
+
(local.set $len (call $__len (local.get $ptr)))
|
|
191
|
+
(if (result f64)
|
|
192
|
+
(i32.or
|
|
193
|
+
(i32.lt_s (local.get $i) (i32.const 0))
|
|
194
|
+
(i32.ge_u (local.get $i) (local.get $len)))
|
|
195
|
+
(then (f64.const nan:${UNDEF_NAN}))
|
|
196
|
+
(else
|
|
197
|
+
(if (result f64) (i32.eq (local.get $t) (i32.const ${PTR.TYPED}))
|
|
198
|
+
(then
|
|
199
|
+
(local.set $et (i32.and (local.get $aux) (i32.const 7)))
|
|
200
|
+
(if (result f64) (i32.ge_u (local.get $et) (i32.const 6))
|
|
201
|
+
(then (if (result f64) (i32.eq (local.get $et) (i32.const 7))
|
|
202
|
+
(then (if (result f64) (i32.and (local.get $aux) (i32.const 16))
|
|
203
|
+
(then (f64.reinterpret_i64 (i64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))))))
|
|
204
|
+
(else (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))))
|
|
205
|
+
(else (f64.promote_f32 (f32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
|
|
206
|
+
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 4))
|
|
207
|
+
(then (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
208
|
+
(then (f64.convert_i32_u (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))
|
|
209
|
+
(else (f64.convert_i32_s (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
|
|
210
|
+
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 2))
|
|
211
|
+
(then (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
212
|
+
(then (f64.convert_i32_u (i32.load16_u (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))
|
|
213
|
+
(else (f64.convert_i32_s (i32.load16_s (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))))
|
|
214
|
+
(else (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
215
|
+
(then (f64.convert_i32_u (i32.load8_u (i32.add (local.get $off) (local.get $i)))))
|
|
216
|
+
(else (f64.convert_i32_s (i32.load8_s (i32.add (local.get $off) (local.get $i)))))))))))))
|
|
217
|
+
(else (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))))))`
|
|
218
|
+
}
|
|
219
|
+
|
|
146
220
|
ctx.core.stdlib['__ptr_offset_fwd'] = ptrOffsetFwdWat()
|
|
147
221
|
|
|
148
222
|
ctx.core.stdlib['__ptr_offset'] = `(func $__ptr_offset (param $ptr i64) (result i32)
|
|
@@ -255,12 +329,29 @@ export default (ctx) => {
|
|
|
255
329
|
(call $__memgrow (local.get $next))
|
|
256
330
|
(i32.store (i32.const ${HEAP.PTR_ADDR}) (local.get $next))
|
|
257
331
|
(local.get $ptr))`
|
|
332
|
+
// NOTE: shared memory rewinds to the raw HEAP.START, NOT a post-init high-water
|
|
333
|
+
// mark — so a shared module whose `__start` heap-allocates (strPool memory.init,
|
|
334
|
+
// module-init state) loses that state on `_clear`. Pre-existing; unlike the owned
|
|
335
|
+
// path below it has no `__heap_reset` analogue because the rewind target would need
|
|
336
|
+
// a reserved low-memory cell (the [0,HEAP.START) region is already spoken for —
|
|
337
|
+
// clock at 0, heap ptr at HEAP.PTR_ADDR). Owned memory (the self-host + default
|
|
338
|
+
// case) is the one fixed below; revisit shared if a thread-pooled reset hits it.
|
|
258
339
|
ctx.core.stdlib['__clear'] = `(func $__clear
|
|
259
340
|
(i32.store (i32.const ${HEAP.PTR_ADDR}) (i32.const ${HEAP.START})))`
|
|
260
341
|
} else {
|
|
261
342
|
// Own memory: heap offset in a global, exported so the JS-side adapter
|
|
262
343
|
// (alloc:false, no `_alloc` export) shares the pointer.
|
|
263
344
|
declGlobal('__heap', 'i32', HEAP.START, { export: '__heap' })
|
|
345
|
+
// `__clear` rewinds to the *post-module-init* high-water mark, not the static
|
|
346
|
+
// data end: a module whose top-level code heap-allocates (e.g. the self-host
|
|
347
|
+
// compiler building its GLOBALS/atom tables in `__start`) leaves live state
|
|
348
|
+
// above the data segment that a reset must preserve. `__heap_reset` is seeded
|
|
349
|
+
// to the data end (assemble.js heapBase patch) and overwritten by `__start`'s
|
|
350
|
+
// tail with the heap top after init runs (buildStartFn) — so for a module with
|
|
351
|
+
// no init allocations it equals the data end, and for self-host it spares the
|
|
352
|
+
// compiler's init state. (Distinct from `__heap_start`, the propsPtr watermark,
|
|
353
|
+
// which must stay at the data end or init-time heap objects misread as static.)
|
|
354
|
+
declGlobal('__heap_reset', 'i32', HEAP.START)
|
|
264
355
|
ctx.core.stdlib['__alloc'] = `(func $__alloc (param $bytes i32) (result i32)
|
|
265
356
|
(local $ptr i32) (local $next i32)
|
|
266
357
|
(local.set $ptr (global.get $__heap))
|
|
@@ -269,7 +360,7 @@ export default (ctx) => {
|
|
|
269
360
|
(global.set $__heap (local.get $next))
|
|
270
361
|
(local.get $ptr))`
|
|
271
362
|
ctx.core.stdlib['__clear'] = `(func $__clear
|
|
272
|
-
(global.set $__heap (
|
|
363
|
+
(global.set $__heap (global.get $__heap_reset)))`
|
|
273
364
|
}
|
|
274
365
|
|
|
275
366
|
// Build an insertion-ordered list of live slot offsets for a Set/Map/HASH
|
|
@@ -407,14 +498,15 @@ export default (ctx) => {
|
|
|
407
498
|
(else (i32.load (i32.sub (local.get $off) (i32.const 4))))))
|
|
408
499
|
(else (i32.const 0))))`
|
|
409
500
|
|
|
410
|
-
// String length (UTF-8 byte count). Heap: [-4:len(i32)][chars...]; SSO
|
|
501
|
+
// String length (UTF-8 byte count). Heap: [-4:len(i32)][chars...]; SSO (7-bit codec):
|
|
502
|
+
// len at aux bits 10-12 (= payload bits 42-44). See module/string.js codec.
|
|
411
503
|
ctx.core.stdlib['__str_len'] = `(func $__str_len (param $ptr i64) (result i32)
|
|
412
504
|
(local $off i32) (local $aux i32)
|
|
413
505
|
(if (i32.ne (call $__ptr_type (local.get $ptr)) (i32.const ${PTR.STRING}))
|
|
414
506
|
(then (return (i32.const 0))))
|
|
415
507
|
(local.set $aux (call $__ptr_aux (local.get $ptr)))
|
|
416
508
|
(if (i32.and (local.get $aux) (i32.const ${LAYOUT.SSO_BIT}))
|
|
417
|
-
(then (return (i32.and (local.get $aux) (i32.const 7)))))
|
|
509
|
+
(then (return (i32.and (i32.shr_u (local.get $aux) (i32.const 10)) (i32.const 7)))))
|
|
418
510
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
419
511
|
(if (result i32) (i32.ge_u (local.get $off) (i32.const 4))
|
|
420
512
|
(then (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
@@ -475,6 +567,56 @@ export default (ctx) => {
|
|
|
475
567
|
(memory.fill (i32.add (local.get $ptr) (i32.const 16)) (i32.const 0) (i32.mul (local.get $cap) (local.get $stride)))
|
|
476
568
|
(i32.add (local.get $ptr) (i32.const 16)))`
|
|
477
569
|
|
|
570
|
+
// Shallow clone of an OBJECT or HASH, preserving its runtime type — the copy
|
|
571
|
+
// semantics of a single unknown spread `{ ...src }` (module/object.js). Without
|
|
572
|
+
// this, `{ ...src }` aliases src, so any later write to the result mutates the
|
|
573
|
+
// source (a real bug: jz's own narrow.js had to route around it). Per JS spread,
|
|
574
|
+
// the clone is SHALLOW: scalar slots are copied by value; nested object/string
|
|
575
|
+
// pointers are shared (immutable strings; nested objects are aliased as in V8).
|
|
576
|
+
//
|
|
577
|
+
// - OBJECT: alloc a fresh header'd object with the same schemaId and copy its N
|
|
578
|
+
// schema slots (N = key count of __schema_tbl[sid], robust to static-segment
|
|
579
|
+
// sources that carry no len/cap header). Then deep-copy the per-instance
|
|
580
|
+
// dyn-props HASH (base-16) so `o[k]=v` keys added before the spread carry over
|
|
581
|
+
// independently — heap objects only; static-segment objects have no header.
|
|
582
|
+
// - HASH: copy header + every probe slot wholesale (entries hold immutable
|
|
583
|
+
// string keys + scalar/pointer values — a byte copy is an independent dict).
|
|
584
|
+
// - anything else (primitive): nothing to clone, return as-is.
|
|
585
|
+
ctx.core.stdlib['__obj_clone'] = `(func $__obj_clone (param $v f64) (result f64)
|
|
586
|
+
(local $bits i64) (local $t i32) (local $sid i32) (local $n i32) (local $cap i32)
|
|
587
|
+
(local $src i32) (local $dst i32) (local $props i64)
|
|
588
|
+
(local.set $bits (i64.reinterpret_f64 (local.get $v)))
|
|
589
|
+
(local.set $t (call $__ptr_type (local.get $bits)))
|
|
590
|
+
(if (i32.eq (local.get $t) (i32.const ${PTR.OBJECT}))
|
|
591
|
+
(then
|
|
592
|
+
(local.set $sid (call $__ptr_aux (local.get $bits)))
|
|
593
|
+
(local.set $src (call $__ptr_offset (local.get $bits)))
|
|
594
|
+
(local.set $n (i32.const 0))
|
|
595
|
+
(if (i32.ne (global.get $__schema_tbl) (i32.const 0))
|
|
596
|
+
(then (local.set $n (call $__len
|
|
597
|
+
(i64.load (i32.add (global.get $__schema_tbl) (i32.shl (local.get $sid) (i32.const 3))))))))
|
|
598
|
+
(local.set $cap (i32.add (local.get $n) (i32.eqz (local.get $n))))
|
|
599
|
+
(local.set $dst (call $__alloc_hdr (i32.const 0) (local.get $cap)))
|
|
600
|
+
(memory.copy (local.get $dst) (local.get $src) (i32.shl (local.get $n) (i32.const 3)))
|
|
601
|
+
(if (i32.ge_u (local.get $src) (global.get $__heap_start))
|
|
602
|
+
(then
|
|
603
|
+
(local.set $props (i64.load (i32.sub (local.get $src) (i32.const 16))))
|
|
604
|
+
(if (i32.eq (call $__ptr_type (local.get $props)) (i32.const ${PTR.HASH}))
|
|
605
|
+
(then (i64.store (i32.sub (local.get $dst) (i32.const 16))
|
|
606
|
+
(i64.reinterpret_f64 (call $__obj_clone (f64.reinterpret_i64 (local.get $props)))))))))
|
|
607
|
+
(return (call $__mkptr (i32.const ${PTR.OBJECT}) (local.get $sid) (local.get $dst)))))
|
|
608
|
+
(if (i32.eq (local.get $t) (i32.const ${PTR.HASH}))
|
|
609
|
+
(then
|
|
610
|
+
(local.set $cap (call $__cap (local.get $bits)))
|
|
611
|
+
(local.set $src (call $__ptr_offset (local.get $bits)))
|
|
612
|
+
(local.set $dst (call $__alloc_hdr_n (i32.const 0) (local.get $cap) (i32.const 24)))
|
|
613
|
+
(memory.copy
|
|
614
|
+
(i32.sub (local.get $dst) (i32.const 16))
|
|
615
|
+
(i32.sub (local.get $src) (i32.const 16))
|
|
616
|
+
(i32.add (i32.const 16) (i32.mul (local.get $cap) (i32.const 24))))
|
|
617
|
+
(return (call $__mkptr (i32.const ${PTR.HASH}) (i32.const 0) (local.get $dst)))))
|
|
618
|
+
(local.get $v))`
|
|
619
|
+
|
|
478
620
|
// Allocator + exports are deferred: only included when memory is actually needed.
|
|
479
621
|
// Any module using allocPtr/inc('__alloc') pulls these in via ctx.core.stdlibDeps.
|
|
480
622
|
// compile.js emits _alloc/_clear exports + memory section only when __alloc is in includes.
|
|
@@ -730,12 +872,14 @@ export default (ctx) => {
|
|
|
730
872
|
const eqT = (n) => `(i32.eq (local.get $t) (i32.const ${n}))`
|
|
731
873
|
let disj = eqT(types[0])
|
|
732
874
|
for (let i = 1; i < types.length; i++) disj = `(i32.or ${disj} ${eqT(types[i])})`
|
|
733
|
-
const lenArm = `(
|
|
875
|
+
const lenArm = `(block (result f64)
|
|
876
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $v) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
877
|
+
(if (result f64) ${disj}
|
|
734
878
|
(then
|
|
735
879
|
(if (result f64) (i32.ge_u (local.get $off) (i32.const 8))
|
|
736
880
|
(then (f64.convert_i32_s (call $__len (local.get $v))))
|
|
737
881
|
(else (f64.const nan:${UNDEF_NAN}))))
|
|
738
|
-
(else (f64.const nan:${UNDEF_NAN})))`
|
|
882
|
+
(else (f64.const nan:${UNDEF_NAN}))))`
|
|
739
883
|
const stringArm = `(if (result f64) (i32.eq (local.get $t) (i32.const ${PTR.STRING}))
|
|
740
884
|
(then (f64.convert_i32_s (call $__str_len (local.get $v))))
|
|
741
885
|
(else ${lenArm}))`
|
|
@@ -746,7 +890,6 @@ export default (ctx) => {
|
|
|
746
890
|
(then (f64.const nan:${UNDEF_NAN}))
|
|
747
891
|
(else
|
|
748
892
|
(local.set $t (call $__ptr_type (local.get $v)))
|
|
749
|
-
(local.set $off (call $__ptr_offset (local.get $v)))
|
|
750
893
|
${stringArm})))`
|
|
751
894
|
}
|
|
752
895
|
|
|
@@ -823,8 +966,9 @@ export default (ctx) => {
|
|
|
823
966
|
const ptRep = typeof obj === 'string' ? repOf(obj) : null
|
|
824
967
|
const ptVt = ptRep ? ptRep.val : valTypeOf(obj)
|
|
825
968
|
if (ptVt) {
|
|
826
|
-
const
|
|
827
|
-
|
|
969
|
+
const tpKey = `.${ptVt}:${prop}`
|
|
970
|
+
const tpEmitter = ctx.core.emit[tpKey]
|
|
971
|
+
if (tpEmitter && ctx.core.getters.has(tpKey)) return tpEmitter(obj)
|
|
828
972
|
}
|
|
829
973
|
|
|
830
974
|
// valueOf/toString are ToPrimitive hooks (ES2024 7.1.1) that an own data
|
|
@@ -851,7 +995,7 @@ export default (ctx) => {
|
|
|
851
995
|
// fall through to a real property read — `m.values` reads the "values" field.
|
|
852
996
|
const propKey = `.${prop}`
|
|
853
997
|
const propEmitter = ctx.core.emit[propKey]
|
|
854
|
-
if (propEmitter
|
|
998
|
+
if (propEmitter && ctx.core.getters.has(propKey)) return propEmitter(obj)
|
|
855
999
|
|
|
856
1000
|
return emitPropAccess(emit(obj), obj, prop)
|
|
857
1001
|
}
|
|
@@ -904,11 +1048,13 @@ export default (ctx) => {
|
|
|
904
1048
|
// re-emitting `obj`. Without this `s?.size` fell straight to emitPropAccess (a
|
|
905
1049
|
// plain field read) and returned undefined — a Set/Map size getter never ran.
|
|
906
1050
|
if (vt) {
|
|
907
|
-
const
|
|
908
|
-
|
|
1051
|
+
const tgKey = `.${vt}:${prop}`
|
|
1052
|
+
const tg = ctx.core.emit[tgKey]
|
|
1053
|
+
if (tg && ctx.core.getters.has(tgKey)) return tg(t)
|
|
909
1054
|
}
|
|
910
|
-
const
|
|
911
|
-
|
|
1055
|
+
const gKey = `.${prop}`
|
|
1056
|
+
const g = ctx.core.emit[gKey]
|
|
1057
|
+
if (g && ctx.core.getters.has(gKey)) return g(t)
|
|
912
1058
|
return emitPropAccess(typed(['local.get', `$${t}`], 'f64'), obj, prop, true)
|
|
913
1059
|
})
|
|
914
1060
|
|
package/module/json.js
CHANGED
|
@@ -711,12 +711,12 @@ export default (ctx) => {
|
|
|
711
711
|
(local.set $len (i32.add (local.get $len) (i32.const 1)))
|
|
712
712
|
${ADV(2)})
|
|
713
713
|
(else
|
|
714
|
-
;; Pack first 4 bytes into SSO slot (used only when len ≤ 4).
|
|
714
|
+
;; Pack first 4 bytes into SSO slot (used only when len ≤ 4): 7-bit ASCII, char at bit len*7.
|
|
715
715
|
(if (i32.lt_u (local.get $len) (i32.const 4))
|
|
716
716
|
(then (local.set $sso
|
|
717
717
|
(i32.or (local.get $sso)
|
|
718
718
|
(i32.shl (i32.and (local.get $ch) (i32.const 0xFF))
|
|
719
|
-
(i32.
|
|
719
|
+
(i32.mul (local.get $len) (i32.const 7)))))))
|
|
720
720
|
(local.set $h (i32.mul (i32.xor (local.get $h) (i32.and (local.get $ch) (i32.const 0xFF))) (i32.const 0x01000193)))
|
|
721
721
|
(local.set $len (i32.add (local.get $len) (i32.const 1)))
|
|
722
722
|
${ADV(1)}))
|
|
@@ -735,7 +735,7 @@ export default (ctx) => {
|
|
|
735
735
|
;; SSO fast path: ≤4 ASCII chars, no escapes — bytes already packed inline.
|
|
736
736
|
(if (i32.and (local.get $simple) (i32.le_u (local.get $len) (i32.const 4)))
|
|
737
737
|
(then
|
|
738
|
-
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.or (i32.const ${LAYOUT.SSO_BIT}) (local.get $len)) (local.get $sso)))))
|
|
738
|
+
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.or (i32.const ${LAYOUT.SSO_BIT}) (i32.shl (local.get $len) (i32.const 10))) (local.get $sso)))))
|
|
739
739
|
;; Simple STRING fast path: no escapes, len > 4 — bulk memcpy from parse buffer,
|
|
740
740
|
;; skip rewind + per-byte escape-decode loop. Hits 5+ char keys without escapes.
|
|
741
741
|
(if (local.get $simple)
|