jz 0.4.0 → 0.5.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 +275 -255
- package/cli.js +8 -51
- package/index.js +166 -31
- package/{src/host.js → interop.js} +291 -88
- package/module/array.js +181 -71
- package/module/collection.js +222 -8
- package/module/console.js +1 -1
- package/module/core.js +277 -118
- package/module/date.js +9 -5
- package/module/function.js +11 -1
- package/module/json.js +361 -55
- package/module/math.js +601 -130
- package/module/number.js +390 -227
- package/module/object.js +252 -34
- package/module/regex.js +123 -16
- package/module/schema.js +15 -1
- package/module/string.js +545 -96
- package/module/timer.js +1 -1
- package/module/typedarray.js +674 -57
- package/package.json +10 -7
- 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 +1870 -485
- package/src/assemble.js +27 -12
- package/src/autoload.js +13 -1
- package/src/compile.js +446 -179
- package/src/ctx.js +85 -18
- package/src/emit.js +705 -196
- package/src/infer.js +548 -0
- package/src/ir.js +291 -23
- package/src/jzify.js +851 -132
- package/src/narrow.js +433 -251
- package/src/optimize.js +126 -122
- package/src/plan.js +703 -34
- package/src/prepare.js +833 -153
- 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/src/fuse.js +0 -159
package/module/collection.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { typed, asF64, asI64, asI32, NULL_NAN, UNDEF_NAN, temp, tempI32, tempI64, allocPtr, undefExpr } from '../src/ir.js'
|
|
12
12
|
import { emit, emitFlat } from '../src/emit.js'
|
|
13
13
|
import { valTypeOf, lookupValType, VAL } from '../src/analyze.js'
|
|
14
|
-
import { hasOwnContinue } from '../src/
|
|
14
|
+
import { hasOwnContinue } from '../src/analyze.js'
|
|
15
15
|
import { inc, PTR, LAYOUT } from '../src/ctx.js'
|
|
16
16
|
|
|
17
17
|
const SET_ENTRY = 16 // hash + key
|
|
@@ -94,19 +94,20 @@ function genUpsert(name, entrySize, hashFn, eqExpr, expectedType, hasVal, hasExt
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/** Generate lookup probe function.
|
|
97
|
-
* wantValue=true: return slot value, missing =>
|
|
97
|
+
* wantValue=true: return slot value, missing => `undefined` (UNDEF_NAN) — a
|
|
98
|
+
* missing Map entry / object property reads as `undefined` in JS, never null.
|
|
98
99
|
* wantValue=false: return i32 0/1 existence flag.
|
|
99
100
|
* hasExt: emit EXTERNAL fallthrough (delegate to __ext_prop/__ext_has). */
|
|
100
101
|
function genLookup(name, entrySize, hashFn, eqExpr, expectedType, wantValue, hasExt) {
|
|
101
102
|
const rt = wantValue ? 'i64' : 'i32'
|
|
102
103
|
const onEmpty = wantValue
|
|
103
|
-
? `(return (i64.const ${
|
|
104
|
+
? `(return (i64.const ${UNDEF_NAN}))`
|
|
104
105
|
: '(return (i32.const 0))'
|
|
105
106
|
const onFound = wantValue
|
|
106
107
|
? '(return (i64.load (i32.add (local.get $slot) (i32.const 16))))'
|
|
107
108
|
: '(return (i32.const 1))'
|
|
108
109
|
const notFound = wantValue
|
|
109
|
-
? `(i64.const ${
|
|
110
|
+
? `(i64.const ${UNDEF_NAN})`
|
|
110
111
|
: '(i32.const 0)'
|
|
111
112
|
const tExpr = `(i32.wrap_i64 (i64.and (i64.shr_u (local.get $coll) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK})))`
|
|
112
113
|
const typeGuard = hasExt
|
|
@@ -354,9 +355,15 @@ export default (ctx) => {
|
|
|
354
355
|
__dyn_get_any_t: () => ctx.features.external
|
|
355
356
|
? ['__dyn_get_t', '__hash_get_local', '__ext_prop']
|
|
356
357
|
: ['__dyn_get_t', '__hash_get_local'],
|
|
358
|
+
__dyn_get_any_t_h: () => ctx.features.external
|
|
359
|
+
? ['__dyn_get_t_h', '__hash_get_local_h', '__ext_prop']
|
|
360
|
+
: ['__dyn_get_t_h', '__hash_get_local_h'],
|
|
357
361
|
__dyn_get_or: ['__dyn_get'],
|
|
358
362
|
__dyn_set: ['__hash_new', '__hash_new_small', '__ihash_get_local', '__ihash_set_local', '__hash_set_local', '__ptr_offset', '__is_nullish', '__str_eq'],
|
|
359
363
|
__dyn_move: ['__ihash_get_local', '__ihash_set_local', '__is_nullish'],
|
|
364
|
+
__hash_del_local: ['__str_hash', '__str_eq', '__ptr_type'],
|
|
365
|
+
__dyn_del: ['__hash_del_local', '__ihash_get_local', '__is_nullish'],
|
|
366
|
+
__coll_clear: ['__ptr_type', '__ptr_offset'],
|
|
360
367
|
})
|
|
361
368
|
|
|
362
369
|
inc('__ptr_offset', '__cap')
|
|
@@ -442,10 +449,53 @@ export default (ctx) => {
|
|
|
442
449
|
|
|
443
450
|
// === Set ===
|
|
444
451
|
|
|
445
|
-
ctx.core.emit['new.Set'] = () => {
|
|
452
|
+
ctx.core.emit['new.Set'] = (iterExpr) => {
|
|
446
453
|
ctx.features.set = true
|
|
447
|
-
|
|
448
|
-
|
|
454
|
+
if (iterExpr == null) {
|
|
455
|
+
const out = allocPtr({ type: PTR.SET, len: 0, cap: INIT_CAP, stride: SET_ENTRY, tag: 'set' })
|
|
456
|
+
return typed(['block', ['result', 'f64'], out.init, out.ptr], 'f64')
|
|
457
|
+
}
|
|
458
|
+
// new Set(iterable): seed from an array argument's elements. __set_add does
|
|
459
|
+
// SameValueZero dedup + −0 normalization. A non-array argument leaves the set
|
|
460
|
+
// empty — the ptr_type guard zeroes the length so the loop is skipped.
|
|
461
|
+
//
|
|
462
|
+
// __set_add is a FIXED-capacity probe (genUpsert never grows the table); a
|
|
463
|
+
// full table makes its probe loop spin forever. So the table is pre-sized to
|
|
464
|
+
// the smallest power of two greater than 2*len: distinct entries ≤ len, so the
|
|
465
|
+
// table stays ≤50% full and every insert finds a free slot. cap = 1 <<
|
|
466
|
+
// (32 − clz(m−1)) with m = 2*len + INIT_CAP rounds 2*len up to a power of two
|
|
467
|
+
// and floors at INIT_CAP for the empty/short case.
|
|
468
|
+
inc('__set_add', '__ptr_type', '__len', '__typed_idx')
|
|
469
|
+
const setL = temp('nss'), arrL = temp('nsa')
|
|
470
|
+
const iL = tempI32('nsi'), lenL = tempI32('nsl')
|
|
471
|
+
const capExpr = ['i32.shl', ['i32.const', 1],
|
|
472
|
+
['i32.sub', ['i32.const', 32], ['i32.clz',
|
|
473
|
+
['i32.sub',
|
|
474
|
+
['i32.add', ['i32.shl', ['local.get', `$${lenL}`], ['i32.const', 1]], ['i32.const', INIT_CAP]],
|
|
475
|
+
['i32.const', 1]]]]]
|
|
476
|
+
const out = allocPtr({ type: PTR.SET, len: 0, cap: capExpr, stride: SET_ENTRY, tag: 'set' })
|
|
477
|
+
return typed(['block', ['result', 'f64'],
|
|
478
|
+
['local.set', `$${arrL}`, asF64(emit(iterExpr))],
|
|
479
|
+
['local.set', `$${lenL}`, ['i32.const', 0]],
|
|
480
|
+
['if', ['i32.eq',
|
|
481
|
+
['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${arrL}`]]],
|
|
482
|
+
['i32.const', PTR.ARRAY]],
|
|
483
|
+
['then', ['local.set', `$${lenL}`,
|
|
484
|
+
['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${arrL}`]]]]]],
|
|
485
|
+
out.init,
|
|
486
|
+
['local.set', `$${setL}`, out.ptr],
|
|
487
|
+
['local.set', `$${iL}`, ['i32.const', 0]],
|
|
488
|
+
['block', `$d_${iL}`, ['loop', `$l_${iL}`,
|
|
489
|
+
['br_if', `$d_${iL}`, ['i32.ge_s', ['local.get', `$${iL}`], ['local.get', `$${lenL}`]]],
|
|
490
|
+
['local.set', `$${setL}`, ['f64.reinterpret_i64',
|
|
491
|
+
['call', '$__set_add',
|
|
492
|
+
['i64.reinterpret_f64', ['local.get', `$${setL}`]],
|
|
493
|
+
['i64.reinterpret_f64', ['call', '$__typed_idx',
|
|
494
|
+
['i64.reinterpret_f64', ['local.get', `$${arrL}`]],
|
|
495
|
+
['local.get', `$${iL}`]]]]]],
|
|
496
|
+
['local.set', `$${iL}`, ['i32.add', ['local.get', `$${iL}`], ['i32.const', 1]]],
|
|
497
|
+
['br', `$l_${iL}`]]],
|
|
498
|
+
['local.get', `$${setL}`]], 'f64')
|
|
449
499
|
}
|
|
450
500
|
|
|
451
501
|
ctx.core.emit['.add'] = (setExpr, val) => {
|
|
@@ -463,10 +513,55 @@ export default (ctx) => {
|
|
|
463
513
|
return typed(['f64.convert_i32_s', ['call', '$__set_delete', asI64(emit(setExpr)), asI64(emit(val))]], 'f64')
|
|
464
514
|
}
|
|
465
515
|
|
|
516
|
+
// Map.prototype.clear / Set.prototype.clear — drop every entry. `.clear` only
|
|
517
|
+
// exists on Map/Set in JS, so a single generic emitter is unambiguous; the
|
|
518
|
+
// stdlib reads the ptr tag to pick the entry stride.
|
|
519
|
+
ctx.core.emit['.clear'] = (collExpr) => {
|
|
520
|
+
inc('__coll_clear')
|
|
521
|
+
return typed(['call', '$__coll_clear', asI64(emit(collExpr))], 'f64')
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Zero every slot (so probes see empty) and reset the length header. Entry
|
|
525
|
+
// stride is 16 for a SET, 24 for a MAP — `(t == MAP) << 3` adds the 8-byte
|
|
526
|
+
// value column. Returns undefined; a non-collation arg is a guarded no-op.
|
|
527
|
+
ctx.core.stdlib['__coll_clear'] = `(func $__coll_clear (param $coll i64) (result f64)
|
|
528
|
+
(local $off i32) (local $cap i32) (local $t i32)
|
|
529
|
+
(local.set $t (call $__ptr_type (local.get $coll)))
|
|
530
|
+
(if (i32.or (i32.eq (local.get $t) (i32.const ${PTR.SET})) (i32.eq (local.get $t) (i32.const ${PTR.MAP})))
|
|
531
|
+
(then
|
|
532
|
+
(local.set $off (call $__ptr_offset (local.get $coll)))
|
|
533
|
+
(local.set $cap (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
534
|
+
(memory.fill (local.get $off) (i32.const 0)
|
|
535
|
+
(i32.mul (local.get $cap)
|
|
536
|
+
(i32.add (i32.const ${SET_ENTRY})
|
|
537
|
+
(i32.shl (i32.eq (local.get $t) (i32.const ${PTR.MAP})) (i32.const 3)))))
|
|
538
|
+
(i32.store (i32.sub (local.get $off) (i32.const 8)) (i32.const 0))))
|
|
539
|
+
(f64.reinterpret_i64 (i64.const ${UNDEF_NAN})))`
|
|
540
|
+
|
|
466
541
|
ctx.core.emit['.size'] = (expr) => {
|
|
467
542
|
return typed(['f64.convert_i32_s', ['call', '$__len', ['i64.reinterpret_f64', asF64(emit(expr))]]], 'f64')
|
|
468
543
|
}
|
|
469
544
|
|
|
545
|
+
// x instanceof Map / Set — typed-pointer predicates emitted by jzify. NaN-check
|
|
546
|
+
// first (non-pointer numbers must report false), then compare __ptr_type tag.
|
|
547
|
+
// Mirrors module/array.js's Array.isArray inline form. Result is i32 (boolean).
|
|
548
|
+
ctx.core.emit['__is_map'] = (x) => {
|
|
549
|
+
inc('__ptr_type')
|
|
550
|
+
const v = asF64(emit(x))
|
|
551
|
+
const t = temp('imap')
|
|
552
|
+
return typed(['i32.and',
|
|
553
|
+
['f64.ne', ['local.tee', `$${t}`, v], ['local.get', `$${t}`]],
|
|
554
|
+
['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${t}`]]], ['i32.const', PTR.MAP]]], 'i32')
|
|
555
|
+
}
|
|
556
|
+
ctx.core.emit['__is_set'] = (x) => {
|
|
557
|
+
inc('__ptr_type')
|
|
558
|
+
const v = asF64(emit(x))
|
|
559
|
+
const t = temp('iset')
|
|
560
|
+
return typed(['i32.and',
|
|
561
|
+
['f64.ne', ['local.tee', `$${t}`, v], ['local.get', `$${t}`]],
|
|
562
|
+
['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${t}`]]], ['i32.const', PTR.SET]]], 'i32')
|
|
563
|
+
}
|
|
564
|
+
|
|
470
565
|
// Generated Set probe functions
|
|
471
566
|
ctx.core.stdlib['__set_add'] = () => genUpsert('__set_add', SET_ENTRY, '$__map_hash', sameValueZeroEq, PTR.SET, false, ctx.features.external)
|
|
472
567
|
ctx.core.stdlib['__set_has'] = () => genLookup('__set_has', SET_ENTRY, '$__map_hash', sameValueZeroEq, PTR.SET, false, ctx.features.external)
|
|
@@ -513,7 +608,7 @@ export default (ctx) => {
|
|
|
513
608
|
// Generated Map probe functions
|
|
514
609
|
ctx.core.stdlib['__map_set'] = () => genUpsert('__map_set', MAP_ENTRY, '$__map_hash', sameValueZeroEq, PTR.MAP, true, ctx.features.external)
|
|
515
610
|
ctx.core.stdlib['__map_get'] = () => genLookup('__map_get', MAP_ENTRY, '$__map_hash', sameValueZeroEq, PTR.MAP, true, ctx.features.external)
|
|
516
|
-
ctx.core.stdlib['__map_get_h'] = () => genLookupStrictPrehashed('__map_get_h', MAP_ENTRY, sameValueZeroEq, PTR.MAP,
|
|
611
|
+
ctx.core.stdlib['__map_get_h'] = () => genLookupStrictPrehashed('__map_get_h', MAP_ENTRY, sameValueZeroEq, PTR.MAP, UNDEF_NAN, ctx.features.external)
|
|
517
612
|
ctx.core.stdlib['__map_has'] = () => genLookup('__map_has', MAP_ENTRY, '$__map_hash', sameValueZeroEq, PTR.MAP, false, ctx.features.external)
|
|
518
613
|
ctx.core.stdlib['__map_delete'] = genDelete('__map_delete', MAP_ENTRY, '$__map_hash', sameValueZeroEq, PTR.MAP)
|
|
519
614
|
|
|
@@ -584,6 +679,9 @@ export default (ctx) => {
|
|
|
584
679
|
ctx.core.stdlib['__hash_get_local_h'] = genLookupStrictPrehashed('__hash_get_local_h', MAP_ENTRY, strEq, PTR.HASH)
|
|
585
680
|
ctx.core.stdlib['__hash_set_local_h'] = genUpsertStrictPrehashed('__hash_set_local_h', MAP_ENTRY, strEq, PTR.HASH)
|
|
586
681
|
ctx.core.stdlib['__hash_set_local'] = genUpsertGrow('__hash_set_local', MAP_ENTRY, '$__str_hash', strEq, PTR.HASH, true)
|
|
682
|
+
// Tombstones an entry in a HASH (string keys). Returns 1 if found+deleted, 0 otherwise.
|
|
683
|
+
// Used as the bucket-level primitive for __dyn_del.
|
|
684
|
+
ctx.core.stdlib['__hash_del_local'] = genDelete('__hash_del_local', MAP_ENTRY, '$__str_hash', strEq, PTR.HASH)
|
|
587
685
|
// Outer __dyn_props hash: keyed by object offset (i32 as f64 bits), value is per-object props hash.
|
|
588
686
|
// Uses bit-hash + i64.eq — no string allocation for the unique integer key.
|
|
589
687
|
ctx.core.stdlib['__ihash_get_local'] = genLookupStrict('__ihash_get_local', MAP_ENTRY, '$__map_hash', '(i64.eq (i64.load (i32.add (local.get $slot) (i32.const 8))) (local.get $key))', PTR.HASH)
|
|
@@ -838,6 +936,28 @@ export default (ctx) => {
|
|
|
838
936
|
(else ${extArm})))))`
|
|
839
937
|
}
|
|
840
938
|
|
|
939
|
+
// Prehashed variant of __dyn_get_any_t for constant string keys: the FNV hash
|
|
940
|
+
// is folded at compile time (strHashLiteral), so no __str_hash call at runtime.
|
|
941
|
+
// Hot for the layered-parser pattern — `parse.step`/`parse.space`/… reads a
|
|
942
|
+
// function-object property with a literal key on every parser step.
|
|
943
|
+
ctx.core.stdlib['__dyn_get_any_t_h'] = () => {
|
|
944
|
+
const extArm = ctx.features.external
|
|
945
|
+
? `(if (result i64) (i32.eq (local.get $t) (i32.const ${PTR.EXTERNAL}))
|
|
946
|
+
(then (call $__ext_prop (local.get $obj) (local.get $key)))
|
|
947
|
+
(else (i64.const ${NULL_NAN})))`
|
|
948
|
+
: `(i64.const ${NULL_NAN})`
|
|
949
|
+
return `(func $__dyn_get_any_t_h (param $obj i64) (param $key i64) (param $t i32) (param $h i32) (result i64)
|
|
950
|
+
(local $val i64)
|
|
951
|
+
(if (result i64) (i32.eq (local.get $t) (i32.const ${PTR.HASH}))
|
|
952
|
+
(then (call $__hash_get_local_h (local.get $obj) (local.get $key) (local.get $h)))
|
|
953
|
+
(else
|
|
954
|
+
(local.set $val (call $__dyn_get_t_h (local.get $obj) (local.get $key) (local.get $t) (local.get $h)))
|
|
955
|
+
(if (result i64)
|
|
956
|
+
(i64.ne (local.get $val) (i64.const ${UNDEF_NAN}))
|
|
957
|
+
(then (local.get $val))
|
|
958
|
+
(else ${extArm})))))`
|
|
959
|
+
}
|
|
960
|
+
|
|
841
961
|
// Hot for `node.loc = pos` patterns (e.g. watr's parser tags every nested level).
|
|
842
962
|
// Defer the root insert to the end and gate it on props-ptr change: most calls hit
|
|
843
963
|
// the no-grow case where the ptr is unchanged and the root slot already points to it.
|
|
@@ -934,6 +1054,91 @@ export default (ctx) => {
|
|
|
934
1054
|
(then (global.set $__dyn_get_cache_props (f64.reinterpret_i64 (local.get $props)))))))
|
|
935
1055
|
(local.get $val))`
|
|
936
1056
|
|
|
1057
|
+
// Tag-dispatched delete (mirrors __dyn_set's dispatch). Returns 1 if a slot was
|
|
1058
|
+
// found+tombstoned, 0 otherwise. Header types (ARRAY non-shifted, OBJECT heap-only,
|
|
1059
|
+
// TYPED/HASH/SET/MAP) carry propsPtr at off-16; others fall back to the global
|
|
1060
|
+
// __dyn_props hash keyed by offset.
|
|
1061
|
+
// Schema-aware delete arm: when the receiver is an OBJECT with a known schema and
|
|
1062
|
+
// the key matches a static slot, overwrite that slot with UNDEF_NAN so subsequent
|
|
1063
|
+
// reads see "absent" (matches `delete obj.a; obj.a → undefined`). Without this,
|
|
1064
|
+
// the shadow-store delete alone would leave the structural slot intact and a later
|
|
1065
|
+
// ctx[k] read would re-surface the original value.
|
|
1066
|
+
const buildObjectSchemaDelArm = () => (ctx.schema.list.length > 0 || ctx.core.includes.has('__jp_obj')) ? `
|
|
1067
|
+
(if (i32.and (i32.eq (local.get $type) (i32.const ${PTR.OBJECT}))
|
|
1068
|
+
(i32.ne (global.get $__schema_tbl) (i32.const 0)))
|
|
1069
|
+
(then
|
|
1070
|
+
(local.set $sid (i32.wrap_i64 (i64.and (i64.shr_u
|
|
1071
|
+
(local.get $obj) (i64.const ${LAYOUT.AUX_SHIFT})) (i64.const ${LAYOUT.AUX_MASK}))))
|
|
1072
|
+
(local.set $kbits
|
|
1073
|
+
(i64.load (i32.add (global.get $__schema_tbl) (i32.shl (local.get $sid) (i32.const 3)))))
|
|
1074
|
+
(local.set $koff (i32.wrap_i64 (i64.and (local.get $kbits) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
1075
|
+
(local.set $nkeys (i32.load (i32.sub (local.get $koff) (i32.const 8))))
|
|
1076
|
+
(local.set $idx (i32.const 0))
|
|
1077
|
+
(block $schemaDelDone (loop $schemaDelLoop
|
|
1078
|
+
(br_if $schemaDelDone (i32.ge_s (local.get $idx) (local.get $nkeys)))
|
|
1079
|
+
(if (call $__str_eq
|
|
1080
|
+
(i64.load (i32.add (local.get $koff) (i32.shl (local.get $idx) (i32.const 3))))
|
|
1081
|
+
(local.get $key))
|
|
1082
|
+
(then
|
|
1083
|
+
(i64.store (i32.add (local.get $off) (i32.shl (local.get $idx) (i32.const 3))) (i64.const ${UNDEF_NAN}))
|
|
1084
|
+
(local.set $hit (i32.const 1))
|
|
1085
|
+
(br $schemaDelDone)))
|
|
1086
|
+
(local.set $idx (i32.add (local.get $idx) (i32.const 1)))
|
|
1087
|
+
(br $schemaDelLoop)))))` : ''
|
|
1088
|
+
|
|
1089
|
+
ctx.core.stdlib['__dyn_del'] = () => `(func $__dyn_del (param $obj i64) (param $key i64) (result i32)
|
|
1090
|
+
(local $root i64) (local $props i64) (local $oldProps i64)
|
|
1091
|
+
(local $off i32) (local $type i32) (local $hit i32) ${buildObjectSchemaSetLocals()}
|
|
1092
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $obj) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
1093
|
+
(local.set $type (i32.wrap_i64 (i64.and (i64.shr_u (local.get $obj) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
1094
|
+
${buildObjectSchemaDelArm()}
|
|
1095
|
+
;; CLOSURE with no env: rekey to function table index (parallels __dyn_set / __dyn_get_t_h).
|
|
1096
|
+
(if (i32.and (i32.eq (local.get $type) (i32.const ${PTR.CLOSURE})) (i32.eqz (local.get $off)))
|
|
1097
|
+
(then (local.set $off (i32.sub (i32.const -1)
|
|
1098
|
+
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $obj) (i64.const ${LAYOUT.AUX_SHIFT})) (i64.const ${LAYOUT.AUX_MASK})))))))
|
|
1099
|
+
;; ARRAY: follow forwarding chain to landed base.
|
|
1100
|
+
(if (i32.eq (local.get $type) (i32.const ${PTR.ARRAY}))
|
|
1101
|
+
(then
|
|
1102
|
+
(block $done
|
|
1103
|
+
(loop $follow
|
|
1104
|
+
(br_if $done (i32.lt_u (local.get $off) (i32.const 16)))
|
|
1105
|
+
(br_if $done (i32.gt_u (local.get $off) (i32.shl (memory.size) (i32.const 16))))
|
|
1106
|
+
(br_if $done (i32.ne (i32.load (i32.sub (local.get $off) (i32.const 4))) (i32.const -1)))
|
|
1107
|
+
(local.set $off (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
1108
|
+
(br $follow)))))
|
|
1109
|
+
;; ARRAY landed propsPtr (HASH-tagged means real sidecar; else fall through to global).
|
|
1110
|
+
(if (i32.and (i32.eq (local.get $type) (i32.const ${PTR.ARRAY}))
|
|
1111
|
+
(i32.ge_u (local.get $off) (i32.const 16)))
|
|
1112
|
+
(then
|
|
1113
|
+
(local.set $oldProps (i64.load (i32.sub (local.get $off) (i32.const 16))))
|
|
1114
|
+
(if (i32.eq
|
|
1115
|
+
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $oldProps) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK})))
|
|
1116
|
+
(i32.const ${PTR.HASH}))
|
|
1117
|
+
(then (return (i32.or (local.get $hit) (call $__hash_del_local (local.get $oldProps) (local.get $key))))))))
|
|
1118
|
+
;; OBJECT heap: propsPtr directly at off-16.
|
|
1119
|
+
(if (i32.and (i32.eq (local.get $type) (i32.const ${PTR.OBJECT}))
|
|
1120
|
+
(i32.ge_u (local.get $off) (global.get $__heap_start)))
|
|
1121
|
+
(then
|
|
1122
|
+
(local.set $oldProps (i64.load (i32.sub (local.get $off) (i32.const 16))))
|
|
1123
|
+
(if (i64.eqz (local.get $oldProps)) (then (return (local.get $hit))))
|
|
1124
|
+
(return (i32.or (local.get $hit) (call $__hash_del_local (local.get $oldProps) (local.get $key))))))
|
|
1125
|
+
;; Other header types (TYPED/HASH/SET/MAP).
|
|
1126
|
+
(if (i32.and (i32.ge_u (local.get $off) (i32.const 16))
|
|
1127
|
+
(i32.or (i32.eq (local.get $type) (i32.const ${PTR.TYPED}))
|
|
1128
|
+
(i32.or (i32.eq (local.get $type) (i32.const ${PTR.HASH}))
|
|
1129
|
+
(i32.or (i32.eq (local.get $type) (i32.const ${PTR.SET}))
|
|
1130
|
+
(i32.eq (local.get $type) (i32.const ${PTR.MAP}))))))
|
|
1131
|
+
(then
|
|
1132
|
+
(local.set $oldProps (i64.load (i32.sub (local.get $off) (i32.const 16))))
|
|
1133
|
+
(if (i64.eqz (local.get $oldProps)) (then (return (local.get $hit))))
|
|
1134
|
+
(return (i32.or (local.get $hit) (call $__hash_del_local (local.get $oldProps) (local.get $key))))))
|
|
1135
|
+
;; Fallback: global __dyn_props keyed by offset.
|
|
1136
|
+
(local.set $root (i64.reinterpret_f64 (global.get $__dyn_props)))
|
|
1137
|
+
(if (i64.eqz (local.get $root)) (then (return (local.get $hit))))
|
|
1138
|
+
(local.set $props (call $__ihash_get_local (local.get $root) (i64.reinterpret_f64 (f64.convert_i32_s (local.get $off)))))
|
|
1139
|
+
(if (call $__is_nullish (local.get $props)) (then (return (local.get $hit))))
|
|
1140
|
+
(i32.or (local.get $hit) (call $__hash_del_local (local.get $props) (local.get $key))))`
|
|
1141
|
+
|
|
937
1142
|
ctx.core.stdlib['__dyn_move'] = `(func $__dyn_move (param $oldOff i32) (param $newOff i32)
|
|
938
1143
|
(local $props i64) (local $root i64)
|
|
939
1144
|
(if (f64.eq (global.get $__dyn_props) (f64.const 0)) (then (return)))
|
|
@@ -947,6 +1152,15 @@ export default (ctx) => {
|
|
|
947
1152
|
ctx.core.stdlib['__hash_get'] = () => genLookup('__hash_get', MAP_ENTRY, '$__str_hash', strEq, PTR.HASH, true, ctx.features.external)
|
|
948
1153
|
ctx.core.stdlib['__hash_has'] = () => genLookup('__hash_has', MAP_ENTRY, '$__str_hash', strEq, PTR.HASH, false, ctx.features.external)
|
|
949
1154
|
|
|
1155
|
+
// === `delete obj[k]`: lift from prepare for computed-key removal ===
|
|
1156
|
+
// Static-key `delete obj.x` / `delete obj["x"]` is rejected in prepare (fixed schema);
|
|
1157
|
+
// only the runtime-dispatched form reaches here. JS returns `true` on success — we
|
|
1158
|
+
// surface the actual found/not-found bit as i32 (`true`/`false` ↔ 1/0 in jz NaN-box).
|
|
1159
|
+
ctx.core.emit['delete'] = (obj, key) => {
|
|
1160
|
+
inc('__dyn_del')
|
|
1161
|
+
return typed(['call', '$__dyn_del', asI64(emit(obj)), asI64(emit(key))], 'i32')
|
|
1162
|
+
}
|
|
1163
|
+
|
|
950
1164
|
// === `in` operator: key in obj → HASH key existence check ===
|
|
951
1165
|
ctx.core.emit['in'] = (key, obj) => {
|
|
952
1166
|
const objType = typeof obj === 'string' ? lookupValType(obj) : valTypeOf(obj)
|
package/module/console.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Console + clocks module — two host-mode lowerings.
|
|
3
3
|
*
|
|
4
4
|
* `host: 'js'` (default): emit `env.print(val: i64, fd: i32, sep: i32)` and
|
|
5
|
-
* `env.now(clock: i32) -> f64`. The JS host (
|
|
5
|
+
* `env.now(clock: i32) -> f64`. The JS host (interop.js) wires both
|
|
6
6
|
* automatically — `print` reads the NaN-boxed value via `mem.read`, so
|
|
7
7
|
* stringification happens host-side (no __ftoa / __write_str / __write_val
|
|
8
8
|
* stdlib in the binary). `sep`: 10=newline, 32=space, 0=no separator.
|