jz 0.6.0 → 0.7.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 +66 -43
- package/bench/README.md +128 -78
- package/bench/bench.svg +32 -42
- package/cli.js +73 -7
- package/dist/interop.js +1 -0
- package/dist/jz.js +6024 -0
- package/index.d.ts +126 -0
- package/index.js +190 -34
- package/interop.js +71 -27
- package/layout.js +5 -0
- package/module/array.js +71 -39
- package/module/collection.js +41 -5
- package/module/core.js +2 -4
- package/module/math.js +138 -2
- package/module/number.js +21 -0
- package/module/object.js +26 -0
- package/module/simd.js +37 -5
- package/module/string.js +41 -12
- package/module/typedarray.js +415 -26
- package/package.json +21 -6
- package/src/autoload.js +3 -0
- package/src/compile/analyze-scans.js +174 -11
- package/src/compile/analyze.js +38 -3
- package/src/compile/emit-assign.js +9 -6
- package/src/compile/emit.js +347 -36
- package/src/compile/index.js +307 -29
- package/src/compile/infer.js +36 -1
- package/src/compile/loop-divmod.js +155 -0
- package/src/compile/narrow.js +108 -11
- package/src/compile/peel-stencil.js +261 -0
- package/src/compile/plan/advise.js +55 -1
- package/src/compile/plan/index.js +4 -0
- package/src/compile/plan/inline.js +5 -2
- package/src/compile/plan/literals.js +220 -5
- package/src/compile/plan/scope.js +115 -39
- package/src/compile/program-facts.js +21 -2
- package/src/ctx.js +45 -7
- package/src/ir.js +55 -7
- package/src/kind-traits.js +27 -0
- package/src/kind.js +65 -3
- package/src/optimize/index.js +344 -45
- package/src/optimize/vectorize.js +2968 -182
- package/src/prepare/index.js +64 -7
- package/src/prepare/lift-iife.js +149 -0
- package/src/reps.js +3 -2
- package/src/static.js +9 -0
- package/src/type.js +10 -6
- package/src/wat/assemble.js +195 -13
- package/src/wat/optimize.js +363 -185
package/module/string.js
CHANGED
|
@@ -114,8 +114,8 @@ export default (ctx) => {
|
|
|
114
114
|
__str_copy: [],
|
|
115
115
|
__str_slice: ['__str_byteLen', '__alloc'],
|
|
116
116
|
__str_slice_view: ['__str_byteLen', '__mkptr', '__str_slice'],
|
|
117
|
-
__str_indexof: ['__str_byteLen'
|
|
118
|
-
__str_lastindexof: ['__str_byteLen'
|
|
117
|
+
__str_indexof: ['__str_byteLen'],
|
|
118
|
+
__str_lastindexof: ['__str_byteLen'],
|
|
119
119
|
__wrap1: ['__alloc', '__mkptr'],
|
|
120
120
|
__str_substring: ['__str_slice'],
|
|
121
121
|
__str_startswith: ['__str_byteLen'],
|
|
@@ -145,9 +145,21 @@ export default (ctx) => {
|
|
|
145
145
|
__str_byteLen: ['__ptr_type', '__ptr_aux', '__str_len'],
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
+
// String *runtime* construction (concat/slice/split/`String(n)`/shared-memory
|
|
149
|
+
// pools) allocates and boxes pointers, so the module eagerly declares its core
|
|
150
|
+
// heap deps — same as object.js / typedarray.js / array.js. Several such helpers
|
|
151
|
+
// (`__str_slice`, `__str_split`, `__str_case`, …) call `$__mkptr`/`$__alloc` only
|
|
152
|
+
// through nested template thunks (sliceSsoPackWat/internProbeWat); resolveIncludes'
|
|
153
|
+
// auto-derivation realizes those thunks to recover the dep, but that re-realization
|
|
154
|
+
// is not reliable under self-host (the jz.wasm kernel re-runs the thunk late, with
|
|
155
|
+
// intern/heap state the native run never reaches), so a runtime string op would
|
|
156
|
+
// emit `call $__mkptr` with no definition. Declaring the deps explicitly here makes
|
|
157
|
+
// inclusion independent of thunk re-realization. Reachability pruning still drops
|
|
158
|
+
// both for a literals-only module (no `__mkptr`/`__alloc` call site → unreachable),
|
|
159
|
+
// so a heap-free program stays heap-free — no allocator, no memory, no exports.
|
|
148
160
|
inc('__mkptr', '__alloc')
|
|
149
161
|
|
|
150
|
-
// === String literal: "abc" → SSO if ≤4 ASCII, else
|
|
162
|
+
// === String literal: "abc" → SSO if ≤4 ASCII, else static data ===
|
|
151
163
|
|
|
152
164
|
bind('str', (str) => {
|
|
153
165
|
const MAX_SSO = 4
|
|
@@ -684,8 +696,8 @@ export default (ctx) => {
|
|
|
684
696
|
(local $hlen i32) (local $nlen i32) (local $i i32) (local $j i32) (local $match i32)
|
|
685
697
|
(local $hoff i32) (local $noff i32)
|
|
686
698
|
(local $hsso i32) (local $nsso i32) (local $hb i32) (local $nb i32) (local $k i32)
|
|
687
|
-
;; ToString the
|
|
688
|
-
|
|
699
|
+
;; The search value is ToString'd at the call site (searchArg) per 21.1.3.9 step 4 —
|
|
700
|
+
;; a known-string needle passes raw, so this helper carries no float-pulling __to_str.
|
|
689
701
|
(local.set $hlen (call $__str_byteLen (local.get $hay)))
|
|
690
702
|
(local.set $nlen (call $__str_byteLen (local.get $ndl)))
|
|
691
703
|
(if (i32.eqz (local.get $nlen)) (then (return (local.get $from))))
|
|
@@ -729,8 +741,7 @@ export default (ctx) => {
|
|
|
729
741
|
(local $hlen i32) (local $nlen i32) (local $i i32) (local $j i32) (local $match i32)
|
|
730
742
|
(local $hoff i32) (local $noff i32)
|
|
731
743
|
(local $hsso i32) (local $nsso i32) (local $hb i32) (local $nb i32) (local $k i32)
|
|
732
|
-
;; ToString the
|
|
733
|
-
(local.set $ndl (call $__to_str (local.get $ndl)))
|
|
744
|
+
;; The search value is ToString'd at the call site (searchArg) per 21.1.3.10 step 4.
|
|
734
745
|
(local.set $hlen (call $__str_byteLen (local.get $hay)))
|
|
735
746
|
(local.set $nlen (call $__str_byteLen (local.get $ndl)))
|
|
736
747
|
;; Empty needle always matches at the clamp(from,0,hlen) position
|
|
@@ -1430,9 +1441,22 @@ export default (ctx) => {
|
|
|
1430
1441
|
// covers string/number/null/undefined needles, but two cases need help here:
|
|
1431
1442
|
// a BOOL rides the 0/1 carrier (→ "0"/"1" not "true"/"false"), and an OBJECT
|
|
1432
1443
|
// needs compile-time ToPrimitive(string) (__to_str can't invoke user toString).
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1444
|
+
// Coerce the search operand to a string AT THE CALL SITE (21.1.3.x ToString step),
|
|
1445
|
+
// so the `__str_indexof` family carries no embedded `__to_str`. A known-STRING arg
|
|
1446
|
+
// (the overwhelmingly common `s.indexOf("x")` / `s.indexOf(t)` shape) passes raw —
|
|
1447
|
+
// dropping the whole ToString → float-formatter dep tree (~4 KB) that an internal,
|
|
1448
|
+
// unconditional coercion forced into every search-method program. Mirrors
|
|
1449
|
+
// `stringSearchMethod` (startsWith/endsWith), which has always coerced here.
|
|
1450
|
+
const searchArg = (search) => {
|
|
1451
|
+
const vt = valTypeOf(search)
|
|
1452
|
+
if (vt === VAL.STRING) return asI64(emit(search))
|
|
1453
|
+
if (vt === VAL.BOOL) return asI64(bool(search))
|
|
1454
|
+
if (vt === VAL.OBJECT) return toStrI64(search, emit(search))
|
|
1455
|
+
inc('__to_str')
|
|
1456
|
+
return ['call', '$__to_str', asI64(emit(search))]
|
|
1457
|
+
}
|
|
1458
|
+
// Replacement operand of .replace/.replaceAll — same call-site ToString as searchArg.
|
|
1459
|
+
const strReplArg = searchArg
|
|
1436
1460
|
|
|
1437
1461
|
bind('.string:indexOf', (str, search, from) => {
|
|
1438
1462
|
inc('__str_indexof')
|
|
@@ -1573,9 +1597,14 @@ export default (ctx) => {
|
|
|
1573
1597
|
asI64(tail)], 'f64')]]], 'f64')
|
|
1574
1598
|
}
|
|
1575
1599
|
inc('__str_replace')
|
|
1576
|
-
|
|
1600
|
+
// search/repl ToString'd at the call site (searchArg) — __str_replace's __str_indexof
|
|
1601
|
+
// no longer coerces internally, so a non-string search must be stringified here.
|
|
1602
|
+
return typed(['call', '$__str_replace', asI64(emit(str)), searchArg(search), strReplArg(repl)], 'f64')
|
|
1603
|
+
})
|
|
1604
|
+
bind('.replaceAll', (str, search, repl) => {
|
|
1605
|
+
inc('__str_replaceall')
|
|
1606
|
+
return typed(['call', '$__str_replaceall', asI64(emit(str)), searchArg(search), strReplArg(repl)], 'f64')
|
|
1577
1607
|
})
|
|
1578
|
-
bind('.replaceAll', method('__str_replaceall', 'III'))
|
|
1579
1608
|
|
|
1580
1609
|
const caseMethod = (lo, hi, delta) => (str) => {
|
|
1581
1610
|
inc('__str_case')
|
package/module/typedarray.js
CHANGED
|
@@ -12,7 +12,7 @@ import { emit, idx, deps, call } from '../src/bridge.js'
|
|
|
12
12
|
import { valTypeOf } from '../src/kind.js'
|
|
13
13
|
import { VAL, lookupValType } from '../src/reps.js'
|
|
14
14
|
import { nanPrefixHex, TYPED_ELEM_NAMES, TYPED_ELEM_CODE, TYPED_ELEM_BIGINT_FLAG, encodeTypedElemAux } from '../layout.js'
|
|
15
|
-
import { inc, PTR, getter } from '../src/ctx.js'
|
|
15
|
+
import { inc, PTR, LAYOUT, getter } from '../src/ctx.js'
|
|
16
16
|
|
|
17
17
|
const _NAN_BITS = nanPrefixHex()
|
|
18
18
|
|
|
@@ -201,6 +201,11 @@ export default (ctx) => {
|
|
|
201
201
|
__byte_offset: ['__ptr_type', '__ptr_offset', '__ptr_aux'],
|
|
202
202
|
__to_buffer: ['__ptr_type', '__ptr_offset', '__ptr_aux', '__mkptr'],
|
|
203
203
|
__typed_set_idx: ['__ptr_aux', '__ptr_offset'],
|
|
204
|
+
__typed_get_idx: ['__ptr_aux', '__ptr_offset'],
|
|
205
|
+
__typed_fill: ['__len', '__typed_set_idx'],
|
|
206
|
+
__typed_reverse: ['__len', '__typed_get_idx', '__typed_set_idx'],
|
|
207
|
+
__typed_copyWithin: ['__len', '__typed_get_idx', '__typed_set_idx'],
|
|
208
|
+
__typed_sort: ['__len', '__typed_get_idx', '__typed_set_idx'],
|
|
204
209
|
// __str_join uses __typed_idx when typedarray is loaded (plain arrays promoted to
|
|
205
210
|
// Int32Array by promoteIntArrayLiterals can produce PTR.TYPED results via .map()).
|
|
206
211
|
__str_join: [...(ctx.core.stdlibDeps.__str_join ?? []), '__typed_idx'],
|
|
@@ -842,18 +847,21 @@ export default (ctx) => {
|
|
|
842
847
|
* at the chain output (the result is always an owned typed array). */
|
|
843
848
|
const TYPED_CHAIN_METHODS = new Set(['map', 'filter', 'slice'])
|
|
844
849
|
const resolveElem = (arr) => {
|
|
845
|
-
let receiver = arr, chainOutput = false
|
|
850
|
+
let receiver = arr, chainOutput = false, viewOutput = false
|
|
846
851
|
// Walk method-call chain inward. `arr.method(...)` parses as
|
|
847
|
-
// ['()', ['.', recv, 'method'], ...args] — peel until we hit a name.
|
|
852
|
+
// ['()', ['.', recv, 'method'], ...args] — peel until we hit a name. The OUTERMOST
|
|
853
|
+
// op decides view-ness: `.subarray(...)` yields a zero-copy VIEW (reads must indirect
|
|
854
|
+
// through the descriptor), whereas `.map`/`.slice`/… yield a fresh non-view copy.
|
|
848
855
|
while (Array.isArray(receiver) && receiver[0] === '()' &&
|
|
849
856
|
Array.isArray(receiver[1]) && receiver[1][0] === '.' &&
|
|
850
|
-
TYPED_CHAIN_METHODS.has(receiver[1][2])) {
|
|
857
|
+
(TYPED_CHAIN_METHODS.has(receiver[1][2]) || receiver[1][2] === 'subarray')) {
|
|
858
|
+
if (!chainOutput) viewOutput = receiver[1][2] === 'subarray'
|
|
851
859
|
receiver = receiver[1][1]
|
|
852
860
|
chainOutput = true
|
|
853
861
|
}
|
|
854
862
|
const ctor = typeof receiver === 'string' && ctx.types.typedElem?.get(receiver)
|
|
855
863
|
if (!ctor) return null
|
|
856
|
-
const isView = !chainOutput && ctor.endsWith('.view')
|
|
864
|
+
const isView = viewOutput || (!chainOutput && ctor.endsWith('.view'))
|
|
857
865
|
const name = ctor.endsWith('.view') ? ctor.slice(4, -5) : ctor.slice(4)
|
|
858
866
|
const et = TYPED_ELEM_CODE[name]
|
|
859
867
|
return et == null ? null : { et, isView, isBigInt: name === 'BigInt64Array' || name === 'BigUint64Array' }
|
|
@@ -863,9 +871,19 @@ export default (ctx) => {
|
|
|
863
871
|
* Owned: low 32 bits of the NaN-box (or the unboxed local directly).
|
|
864
872
|
* View: load descriptor[4]. Uses ptrOffsetIR so unboxed-TYPED locals pass through
|
|
865
873
|
* without a rebox-then-unbox round trip, and globals fold to inline bit-extract. */
|
|
874
|
+
// A typed array (Float64Array/Int32Array/…) is a FIXED-SIZE allocation — it has no
|
|
875
|
+
// grow op, so it can never relocate, so its base needs no realloc-forwarding follow.
|
|
876
|
+
// An already-unboxed pointer is the offset itself; a boxed f64 pointer extracts its
|
|
877
|
+
// low-32 offset directly — no __ptr_offset call. (The general ptrOffsetIR keeps the
|
|
878
|
+
// forwarding follow because ARRAY/HASH/SET/MAP relocate and an *inferred* OBJECT can
|
|
879
|
+
// alias a relocated ARRAY — but VAL.TYPED is a narrow type that can only be a real
|
|
880
|
+
// typed array, so the follow is provably dead here.)
|
|
881
|
+
const typedBase = (objIR) => objIR.ptrKind != null && objIR.ptrKind !== VAL.ARRAY
|
|
882
|
+
? objIR
|
|
883
|
+
: ['i32.wrap_i64', ['i64.and', ['i64.reinterpret_f64', asF64(objIR)], ['i64.const', LAYOUT.OFFSET_MASK]]]
|
|
866
884
|
const typedDataAddr = (objIR, isView) => isView
|
|
867
|
-
? ['i32.load', ['i32.add',
|
|
868
|
-
:
|
|
885
|
+
? ['i32.load', ['i32.add', typedBase(objIR), ['i32.const', 4]]]
|
|
886
|
+
: typedBase(objIR)
|
|
869
887
|
|
|
870
888
|
// Runtime-dispatch typed index: checks ptr_type + aux to load with correct stride.
|
|
871
889
|
// For TYPED views (aux bit 3), $off indirects through descriptor[4] to real data.
|
|
@@ -956,6 +974,219 @@ export default (ctx) => {
|
|
|
956
974
|
(else (i32.store8 (i32.add (local.get $off) (local.get $i)) (local.get $bits))))))))))))
|
|
957
975
|
(local.get $v))`
|
|
958
976
|
|
|
977
|
+
// .fill(value, start?, end?) for typed arrays. The plain-array __arr_fill gates
|
|
978
|
+
// on PTR.ARRAY and silently no-ops a typed receiver (the storage layout and
|
|
979
|
+
// element width differ); this loops the element-width-aware __typed_set_idx
|
|
980
|
+
// over the clamped range so every element kind (u8…f64, BigInt) fills correctly.
|
|
981
|
+
// start/end default 0/length, accept negatives, and clamp to [0, length].
|
|
982
|
+
ctx.core.stdlib['__typed_fill'] = `(func $__typed_fill (param $ptr i64) (param $val f64) (param $start i32) (param $end i32) (result f64)
|
|
983
|
+
(local $len i32) (local $i i32)
|
|
984
|
+
(local.set $len (call $__len (local.get $ptr)))
|
|
985
|
+
(if (i32.lt_s (local.get $start) (i32.const 0)) (then (local.set $start (i32.add (local.get $len) (local.get $start)))))
|
|
986
|
+
(if (i32.lt_s (local.get $start) (i32.const 0)) (then (local.set $start (i32.const 0))))
|
|
987
|
+
(if (i32.gt_s (local.get $start) (local.get $len)) (then (local.set $start (local.get $len))))
|
|
988
|
+
(if (i32.lt_s (local.get $end) (i32.const 0)) (then (local.set $end (i32.add (local.get $len) (local.get $end)))))
|
|
989
|
+
(if (i32.lt_s (local.get $end) (i32.const 0)) (then (local.set $end (i32.const 0))))
|
|
990
|
+
(if (i32.gt_s (local.get $end) (local.get $len)) (then (local.set $end (local.get $len))))
|
|
991
|
+
(local.set $i (local.get $start))
|
|
992
|
+
(block $done (loop $fill
|
|
993
|
+
(br_if $done (i32.ge_s (local.get $i) (local.get $end)))
|
|
994
|
+
(drop (call $__typed_set_idx (local.get $ptr) (local.get $i) (local.get $val)))
|
|
995
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
996
|
+
(br $fill)))
|
|
997
|
+
(f64.reinterpret_i64 (local.get $ptr)))`
|
|
998
|
+
|
|
999
|
+
// Element-width/-kind-aware read: arr[i] → f64, the read mirror of __typed_set_idx.
|
|
1000
|
+
// Integers convert by signedness (et&1 ⇒ unsigned); BigInt returns the raw i64 bits
|
|
1001
|
+
// reinterpreted as f64 so a get→set roundtrip is bit-exact (set_idx stores the bits
|
|
1002
|
+
// back unchanged). Used by the in-place algorithms below for random-access reads.
|
|
1003
|
+
ctx.core.stdlib['__typed_get_idx'] = `(func $__typed_get_idx (param $ptr i64) (param $i i32) (result f64)
|
|
1004
|
+
(local $off i32) (local $aux i32) (local $et i32)
|
|
1005
|
+
(local.set $aux (call $__ptr_aux (local.get $ptr)))
|
|
1006
|
+
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
1007
|
+
(if (i32.and (local.get $aux) (i32.const 8))
|
|
1008
|
+
(then (local.set $off (i32.load (i32.add (local.get $off) (i32.const 4))))))
|
|
1009
|
+
(local.set $et (i32.and (local.get $aux) (i32.const 7)))
|
|
1010
|
+
(if (result f64) (i32.and (local.get $aux) (i32.const ${TYPED_ELEM_BIGINT_FLAG}))
|
|
1011
|
+
(then (f64.reinterpret_i64 (i64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))))))
|
|
1012
|
+
(else (if (result f64) (i32.eq (local.get $et) (i32.const 7))
|
|
1013
|
+
(then (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
1014
|
+
(else (if (result f64) (i32.eq (local.get $et) (i32.const 6))
|
|
1015
|
+
(then (f64.promote_f32 (f32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))
|
|
1016
|
+
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 4))
|
|
1017
|
+
(then (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
1018
|
+
(then (f64.convert_i32_u (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))
|
|
1019
|
+
(else (f64.convert_i32_s (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
|
|
1020
|
+
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 2))
|
|
1021
|
+
(then (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
1022
|
+
(then (f64.convert_i32_u (i32.load16_u (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))
|
|
1023
|
+
(else (f64.convert_i32_s (i32.load16_s (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))))
|
|
1024
|
+
(else (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
1025
|
+
(then (f64.convert_i32_u (i32.load8_u (i32.add (local.get $off) (local.get $i)))))
|
|
1026
|
+
(else (f64.convert_i32_s (i32.load8_s (i32.add (local.get $off) (local.get $i)))))))))))))))))`
|
|
1027
|
+
|
|
1028
|
+
// .reverse() — in-place, element-kind-agnostic via get/set (bit-exact for BigInt).
|
|
1029
|
+
ctx.core.stdlib['__typed_reverse'] = `(func $__typed_reverse (param $ptr i64) (result f64)
|
|
1030
|
+
(local $len i32) (local $i i32) (local $j i32) (local $t f64)
|
|
1031
|
+
(local.set $len (call $__len (local.get $ptr)))
|
|
1032
|
+
(local.set $i (i32.const 0))
|
|
1033
|
+
(local.set $j (i32.sub (local.get $len) (i32.const 1)))
|
|
1034
|
+
(block $done (loop $rev
|
|
1035
|
+
(br_if $done (i32.ge_s (local.get $i) (local.get $j)))
|
|
1036
|
+
(local.set $t (call $__typed_get_idx (local.get $ptr) (local.get $i)))
|
|
1037
|
+
(drop (call $__typed_set_idx (local.get $ptr) (local.get $i) (call $__typed_get_idx (local.get $ptr) (local.get $j))))
|
|
1038
|
+
(drop (call $__typed_set_idx (local.get $ptr) (local.get $j) (local.get $t)))
|
|
1039
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
1040
|
+
(local.set $j (i32.sub (local.get $j) (i32.const 1)))
|
|
1041
|
+
(br $rev)))
|
|
1042
|
+
(f64.reinterpret_i64 (local.get $ptr)))`
|
|
1043
|
+
|
|
1044
|
+
// .copyWithin(target, start, end) — in-place overlap-safe move. Indices accept
|
|
1045
|
+
// negatives (from end) and clamp to [0, len]; count = min(end-start, len-target).
|
|
1046
|
+
// Direction picked so overlapping ranges don't clobber unread source elements.
|
|
1047
|
+
ctx.core.stdlib['__typed_copyWithin'] = `(func $__typed_copyWithin (param $ptr i64) (param $target i32) (param $start i32) (param $end i32) (result f64)
|
|
1048
|
+
(local $len i32) (local $count i32) (local $k i32)
|
|
1049
|
+
(local.set $len (call $__len (local.get $ptr)))
|
|
1050
|
+
(if (i32.lt_s (local.get $target) (i32.const 0)) (then (local.set $target (i32.add (local.get $len) (local.get $target)))))
|
|
1051
|
+
(if (i32.lt_s (local.get $target) (i32.const 0)) (then (local.set $target (i32.const 0))))
|
|
1052
|
+
(if (i32.gt_s (local.get $target) (local.get $len)) (then (local.set $target (local.get $len))))
|
|
1053
|
+
(if (i32.lt_s (local.get $start) (i32.const 0)) (then (local.set $start (i32.add (local.get $len) (local.get $start)))))
|
|
1054
|
+
(if (i32.lt_s (local.get $start) (i32.const 0)) (then (local.set $start (i32.const 0))))
|
|
1055
|
+
(if (i32.gt_s (local.get $start) (local.get $len)) (then (local.set $start (local.get $len))))
|
|
1056
|
+
(if (i32.lt_s (local.get $end) (i32.const 0)) (then (local.set $end (i32.add (local.get $len) (local.get $end)))))
|
|
1057
|
+
(if (i32.lt_s (local.get $end) (i32.const 0)) (then (local.set $end (i32.const 0))))
|
|
1058
|
+
(if (i32.gt_s (local.get $end) (local.get $len)) (then (local.set $end (local.get $len))))
|
|
1059
|
+
(local.set $count (i32.sub (local.get $end) (local.get $start)))
|
|
1060
|
+
(if (i32.gt_s (local.get $count) (i32.sub (local.get $len) (local.get $target)))
|
|
1061
|
+
(then (local.set $count (i32.sub (local.get $len) (local.get $target)))))
|
|
1062
|
+
(if (i32.lt_s (local.get $target) (local.get $start))
|
|
1063
|
+
(then
|
|
1064
|
+
(local.set $k (i32.const 0))
|
|
1065
|
+
(block $fd (loop $fl
|
|
1066
|
+
(br_if $fd (i32.ge_s (local.get $k) (local.get $count)))
|
|
1067
|
+
(drop (call $__typed_set_idx (local.get $ptr) (i32.add (local.get $target) (local.get $k))
|
|
1068
|
+
(call $__typed_get_idx (local.get $ptr) (i32.add (local.get $start) (local.get $k)))))
|
|
1069
|
+
(local.set $k (i32.add (local.get $k) (i32.const 1)))
|
|
1070
|
+
(br $fl))))
|
|
1071
|
+
(else
|
|
1072
|
+
(local.set $k (local.get $count))
|
|
1073
|
+
(block $bd (loop $bl
|
|
1074
|
+
(br_if $bd (i32.le_s (local.get $k) (i32.const 0)))
|
|
1075
|
+
(local.set $k (i32.sub (local.get $k) (i32.const 1)))
|
|
1076
|
+
(drop (call $__typed_set_idx (local.get $ptr) (i32.add (local.get $target) (local.get $k))
|
|
1077
|
+
(call $__typed_get_idx (local.get $ptr) (i32.add (local.get $start) (local.get $k)))))
|
|
1078
|
+
(br $bl)))))
|
|
1079
|
+
(f64.reinterpret_i64 (local.get $ptr)))`
|
|
1080
|
+
|
|
1081
|
+
// .sort() — default numeric order (insertion sort, stable). NaN sorts to the end,
|
|
1082
|
+
// -0 before +0 (the equal-value tiebreak via signed-bit compare). BigInt arrays are
|
|
1083
|
+
// compared as signed i64 on their exact bits. A user comparator is handled inline by
|
|
1084
|
+
// the .typed:sort emitter (this helper is the no-argument numeric path).
|
|
1085
|
+
ctx.core.stdlib['__typed_sort'] = `(func $__typed_sort (param $ptr i64) (result f64)
|
|
1086
|
+
(local $isbig i32) (local $len i32) (local $i i32) (local $j i32) (local $saved f64) (local $nb f64)
|
|
1087
|
+
(local.set $isbig (i32.and (call $__ptr_aux (local.get $ptr)) (i32.const ${TYPED_ELEM_BIGINT_FLAG})))
|
|
1088
|
+
(local.set $len (call $__len (local.get $ptr)))
|
|
1089
|
+
(local.set $i (i32.const 1))
|
|
1090
|
+
(block $od (loop $ol
|
|
1091
|
+
(br_if $od (i32.ge_s (local.get $i) (local.get $len)))
|
|
1092
|
+
(local.set $saved (call $__typed_get_idx (local.get $ptr) (local.get $i)))
|
|
1093
|
+
(local.set $j (i32.sub (local.get $i) (i32.const 1)))
|
|
1094
|
+
(block $id (loop $il
|
|
1095
|
+
(br_if $id (i32.lt_s (local.get $j) (i32.const 0)))
|
|
1096
|
+
(local.set $nb (call $__typed_get_idx (local.get $ptr) (local.get $j)))
|
|
1097
|
+
(br_if $id (i32.eqz
|
|
1098
|
+
(if (result i32) (local.get $isbig)
|
|
1099
|
+
(then (i64.gt_s (i64.reinterpret_f64 (local.get $nb)) (i64.reinterpret_f64 (local.get $saved))))
|
|
1100
|
+
(else (if (result i32) (f64.ne (local.get $nb) (local.get $nb))
|
|
1101
|
+
(then (i32.eqz (f64.ne (local.get $saved) (local.get $saved))))
|
|
1102
|
+
(else (if (result i32) (f64.ne (local.get $saved) (local.get $saved))
|
|
1103
|
+
(then (i32.const 0))
|
|
1104
|
+
(else (if (result i32) (f64.gt (local.get $nb) (local.get $saved))
|
|
1105
|
+
(then (i32.const 1))
|
|
1106
|
+
(else (if (result i32) (f64.lt (local.get $nb) (local.get $saved))
|
|
1107
|
+
(then (i32.const 0))
|
|
1108
|
+
(else (i64.gt_s (i64.reinterpret_f64 (local.get $nb)) (i64.reinterpret_f64 (local.get $saved)))))))))))))))
|
|
1109
|
+
(drop (call $__typed_set_idx (local.get $ptr) (i32.add (local.get $j) (i32.const 1)) (local.get $nb)))
|
|
1110
|
+
(local.set $j (i32.sub (local.get $j) (i32.const 1)))
|
|
1111
|
+
(br $il)))
|
|
1112
|
+
(drop (call $__typed_set_idx (local.get $ptr) (i32.add (local.get $j) (i32.const 1)) (local.get $saved)))
|
|
1113
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
1114
|
+
(br $ol)))
|
|
1115
|
+
(f64.reinterpret_i64 (local.get $ptr)))`
|
|
1116
|
+
|
|
1117
|
+
ctx.core.emit['.typed:fill'] = (arr, val, start, end) => {
|
|
1118
|
+
inc('__typed_fill')
|
|
1119
|
+
return typed(['call', '$__typed_fill',
|
|
1120
|
+
asI64(emit(arr)),
|
|
1121
|
+
val == null ? undefExpr() : asF64(emit(val)),
|
|
1122
|
+
start == null ? ['i32.const', 0] : asI32(emit(start)),
|
|
1123
|
+
end == null ? ['i32.const', 0x7FFFFFFF] : asI32(emit(end))], 'f64')
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// .reverse() / .copyWithin(...) for typed arrays. The plain-array helpers gate on
|
|
1127
|
+
// PTR.ARRAY and silently no-op a typed receiver; these go through the element-kind-
|
|
1128
|
+
// aware get/set helpers so every width and signedness reverses/moves correctly.
|
|
1129
|
+
ctx.core.emit['.typed:reverse'] = (arr) => {
|
|
1130
|
+
inc('__typed_reverse')
|
|
1131
|
+
return typed(['call', '$__typed_reverse', asI64(emit(arr))], 'f64')
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
ctx.core.emit['.typed:copyWithin'] = (arr, target, start, end) => {
|
|
1135
|
+
inc('__typed_copyWithin')
|
|
1136
|
+
return typed(['call', '$__typed_copyWithin',
|
|
1137
|
+
asI64(emit(arr)),
|
|
1138
|
+
target == null ? ['i32.const', 0] : asI32(emit(target)),
|
|
1139
|
+
start == null ? ['i32.const', 0] : asI32(emit(start)),
|
|
1140
|
+
end == null ? ['i32.const', 0x7FFFFFFF] : asI32(emit(end))], 'f64')
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// .sort(compareFn?) for typed arrays. No argument → the numeric __typed_sort helper
|
|
1144
|
+
// (typed-array default is NUMERIC, unlike Array.sort's lexicographic default — so it
|
|
1145
|
+
// must NOT route through the plain-array string comparator). With a comparator, an
|
|
1146
|
+
// insertion sort is emitted inline, calling the closure per neighbor compare; a
|
|
1147
|
+
// positive return shifts (same convention as Array.prototype.sort).
|
|
1148
|
+
// Sort the typed array VALUE `arrValIR` in place and return it. Factored out so
|
|
1149
|
+
// .typed:sort sorts the receiver and .typed:toSorted sorts a fresh copy with one body.
|
|
1150
|
+
const emitTypedSort = (arrValIR, fn) => {
|
|
1151
|
+
if (fn == null) {
|
|
1152
|
+
inc('__typed_sort')
|
|
1153
|
+
return typed(['call', '$__typed_sort', asI64(arrValIR)], 'f64')
|
|
1154
|
+
}
|
|
1155
|
+
inc('__len', '__typed_get_idx', '__typed_set_idx')
|
|
1156
|
+
const arrL = temp('tsa'), cbL = temp('tsf')
|
|
1157
|
+
const len = tempI32('tsn'), i = tempI32('tsi'), j = tempI32('tsj')
|
|
1158
|
+
const cur = temp('tsc'), nb = temp('tsb')
|
|
1159
|
+
const id = ctx.func.uniq++
|
|
1160
|
+
const oE = `$tsoe${id}`, oL = `$tsol${id}`, iE = `$tsie${id}`, iL = `$tsil${id}`
|
|
1161
|
+
const ptr = () => ['i64.reinterpret_f64', ['local.get', `$${arrL}`]]
|
|
1162
|
+
const jp1 = ['i32.add', ['local.get', `$${j}`], ['i32.const', 1]]
|
|
1163
|
+
return typed(['block', ['result', 'f64'],
|
|
1164
|
+
['local.set', `$${arrL}`, asF64(arrValIR)],
|
|
1165
|
+
['local.set', `$${cbL}`, asF64(emit(fn))],
|
|
1166
|
+
['local.set', `$${len}`, ['call', '$__len', ptr()]],
|
|
1167
|
+
['local.set', `$${i}`, ['i32.const', 1]],
|
|
1168
|
+
['block', oE, ['loop', oL,
|
|
1169
|
+
['br_if', oE, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
1170
|
+
['local.set', `$${cur}`, ['call', '$__typed_get_idx', ptr(), ['local.get', `$${i}`]]],
|
|
1171
|
+
['local.set', `$${j}`, ['i32.sub', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
1172
|
+
['block', iE, ['loop', iL,
|
|
1173
|
+
['br_if', iE, ['i32.lt_s', ['local.get', `$${j}`], ['i32.const', 0]]],
|
|
1174
|
+
['local.set', `$${nb}`, ['call', '$__typed_get_idx', ptr(), ['local.get', `$${j}`]]],
|
|
1175
|
+
// Break unless cmp(neighbor, cur) > 0. f64.gt is false for NaN (spec NaN-as-0).
|
|
1176
|
+
['br_if', iE, ['i32.eqz', ['f64.gt',
|
|
1177
|
+
asF64(ctx.closure.call(typed(['local.get', `$${cbL}`], 'f64'),
|
|
1178
|
+
[typed(['local.get', `$${nb}`], 'f64'), typed(['local.get', `$${cur}`], 'f64')])),
|
|
1179
|
+
['f64.const', 0]]]],
|
|
1180
|
+
['drop', ['call', '$__typed_set_idx', ptr(), jp1, ['local.get', `$${nb}`]]],
|
|
1181
|
+
['local.set', `$${j}`, ['i32.sub', ['local.get', `$${j}`], ['i32.const', 1]]],
|
|
1182
|
+
['br', iL]]],
|
|
1183
|
+
['drop', ['call', '$__typed_set_idx', ptr(), jp1, ['local.get', `$${cur}`]]],
|
|
1184
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
1185
|
+
['br', oL]]],
|
|
1186
|
+
['local.get', `$${arrL}`]], 'f64')
|
|
1187
|
+
}
|
|
1188
|
+
ctx.core.emit['.typed:sort'] = (arr, fn) => emitTypedSort(emit(arr), fn)
|
|
1189
|
+
|
|
959
1190
|
// Type-aware TypedArray read: arr[i]
|
|
960
1191
|
ctx.core.emit['.typed:[]'] = (arr, i) => {
|
|
961
1192
|
const r = resolveElem(arr)
|
|
@@ -1257,18 +1488,54 @@ export default (ctx) => {
|
|
|
1257
1488
|
|
|
1258
1489
|
// .indexOf: scalar value-equality search. Returns -1 on miss. Compare on f64
|
|
1259
1490
|
// — Array.prototype.indexOf uses strict equality (NaN ≠ NaN).
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1491
|
+
// Effective start index for a fromIndex arg: negative counts from the end. The match
|
|
1492
|
+
// guard is `i >= start` and i ≥ 0, so a start below 0 needs no clamp (always passes).
|
|
1493
|
+
const fromStart = (fiL, len) => ['if', ['result', 'i32'],
|
|
1494
|
+
['i32.lt_s', ['local.get', `$${fiL}`], ['i32.const', 0]],
|
|
1495
|
+
['then', ['i32.add', ['local.get', `$${fiL}`], ['local.get', `$${len}`]]],
|
|
1496
|
+
['else', ['local.get', `$${fiL}`]]]
|
|
1497
|
+
|
|
1498
|
+
ctx.core.emit['.typed:indexOf'] = (arr, val, fromIndex) => {
|
|
1499
|
+
const found = tempI32('tif'), needle = temp('tin'), fiL = tempI32('tifx')
|
|
1500
|
+
const loop = typedLoop(arr, (load, i, len, _ptr, exit) => {
|
|
1501
|
+
const matched = ['f64.eq', load(), ['local.get', `$${needle}`]]
|
|
1502
|
+
const cond = fromIndex == null ? matched
|
|
1503
|
+
: ['i32.and', ['i32.ge_s', ['local.get', `$${i}`], fromStart(fiL, len)], matched]
|
|
1504
|
+
return [['if', cond, ['then',
|
|
1505
|
+
['local.set', `$${found}`, ['local.get', `$${i}`]], ['br', exit]]]]
|
|
1506
|
+
})
|
|
1268
1507
|
if (!loop) return null
|
|
1269
1508
|
return typed(['block', ['result', 'f64'],
|
|
1270
1509
|
['local.set', `$${needle}`, asF64(emit(val))],
|
|
1271
1510
|
['local.set', `$${found}`, ['i32.const', -1]],
|
|
1511
|
+
...(fromIndex == null ? [] : [['local.set', `$${fiL}`, asI32(emit(fromIndex))]]),
|
|
1512
|
+
...loop.setup,
|
|
1513
|
+
['f64.convert_i32_s', ['local.get', `$${found}`]]], 'f64')
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// .lastIndexOf(val): the last index whose element strictly-equals val, else -1.
|
|
1517
|
+
// Was unimplemented for typed arrays (threw). A forward scan that keeps the latest
|
|
1518
|
+
// match needs no reverse iteration — equivalent for the no-fromIndex form, which is
|
|
1519
|
+
// the common case. With a fromIndex the matcher additionally bounds i ≤ fromIndex
|
|
1520
|
+
// (negative counts from the end, resolved against the typedLoop len). NaN never
|
|
1521
|
+
// strict-equals (f64.eq), matching JS.
|
|
1522
|
+
ctx.core.emit['.typed:lastIndexOf'] = (arr, val, fromIndex) => {
|
|
1523
|
+
const found = tempI32('tlf'), needle = temp('tln'), fiL = tempI32('tlfi')
|
|
1524
|
+
const loop = typedLoop(arr, (load, i, len) => {
|
|
1525
|
+
const matched = ['f64.eq', load(), ['local.get', `$${needle}`]]
|
|
1526
|
+
if (fromIndex == null)
|
|
1527
|
+
return [['if', matched, ['then', ['local.set', `$${found}`, ['local.get', `$${i}`]]]]]
|
|
1528
|
+
const lim = ['if', ['result', 'i32'], ['i32.lt_s', ['local.get', `$${fiL}`], ['i32.const', 0]],
|
|
1529
|
+
['then', ['i32.add', ['local.get', `$${fiL}`], ['local.get', `$${len}`]]],
|
|
1530
|
+
['else', ['local.get', `$${fiL}`]]]
|
|
1531
|
+
return [['if', ['i32.and', ['i32.le_s', ['local.get', `$${i}`], lim], matched],
|
|
1532
|
+
['then', ['local.set', `$${found}`, ['local.get', `$${i}`]]]]]
|
|
1533
|
+
})
|
|
1534
|
+
if (!loop) return null
|
|
1535
|
+
return typed(['block', ['result', 'f64'],
|
|
1536
|
+
['local.set', `$${needle}`, asF64(emit(val))],
|
|
1537
|
+
['local.set', `$${found}`, ['i32.const', -1]],
|
|
1538
|
+
...(fromIndex == null ? [] : [['local.set', `$${fiL}`, asI32(emit(fromIndex))]]),
|
|
1272
1539
|
...loop.setup,
|
|
1273
1540
|
['f64.convert_i32_s', ['local.get', `$${found}`]]], 'f64')
|
|
1274
1541
|
}
|
|
@@ -1276,21 +1543,23 @@ export default (ctx) => {
|
|
|
1276
1543
|
// .includes: like indexOf but NaN-equal-NaN (JS spec). Stash needle bits as
|
|
1277
1544
|
// i64 and compare via i64.eq so two NaNs with matching bit patterns match
|
|
1278
1545
|
// (f64.eq would say false).
|
|
1279
|
-
ctx.core.emit['.typed:includes'] = (arr, val) => {
|
|
1280
|
-
const found = tempI32('thf'), needle = temp('thn')
|
|
1281
|
-
const loop = typedLoop(arr, (load,
|
|
1282
|
-
['
|
|
1283
|
-
['
|
|
1284
|
-
|
|
1285
|
-
['i64.
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
['
|
|
1289
|
-
|
|
1546
|
+
ctx.core.emit['.typed:includes'] = (arr, val, fromIndex) => {
|
|
1547
|
+
const found = tempI32('thf'), needle = temp('thn'), fiL = tempI32('thx')
|
|
1548
|
+
const loop = typedLoop(arr, (load, i, len, _ptr, exit) => {
|
|
1549
|
+
const matched = ['i32.or',
|
|
1550
|
+
['f64.eq', load(), ['local.get', `$${needle}`]],
|
|
1551
|
+
['i64.eq',
|
|
1552
|
+
['i64.reinterpret_f64', load()],
|
|
1553
|
+
['i64.reinterpret_f64', ['local.get', `$${needle}`]]]]
|
|
1554
|
+
const cond = fromIndex == null ? matched
|
|
1555
|
+
: ['i32.and', ['i32.ge_s', ['local.get', `$${i}`], fromStart(fiL, len)], matched]
|
|
1556
|
+
return [['if', cond, ['then', ['local.set', `$${found}`, ['i32.const', 1]], ['br', exit]]]]
|
|
1557
|
+
})
|
|
1290
1558
|
if (!loop) return null
|
|
1291
1559
|
return typed(['block', ['result', 'f64'],
|
|
1292
1560
|
['local.set', `$${needle}`, asF64(emit(val))],
|
|
1293
1561
|
['local.set', `$${found}`, ['i32.const', 0]],
|
|
1562
|
+
...(fromIndex == null ? [] : [['local.set', `$${fiL}`, asI32(emit(fromIndex))]]),
|
|
1294
1563
|
...loop.setup,
|
|
1295
1564
|
['f64.convert_i32_s', ['local.get', `$${found}`]]], 'f64')
|
|
1296
1565
|
}
|
|
@@ -1328,6 +1597,40 @@ export default (ctx) => {
|
|
|
1328
1597
|
ctx.core.emit['.typed:find'] = (arr, fn) => findCommon(arr, fn, false)
|
|
1329
1598
|
ctx.core.emit['.typed:findIndex'] = (arr, fn) => findCommon(arr, fn, true)
|
|
1330
1599
|
|
|
1600
|
+
// .findLast / .findLastIndex — like find/findIndex but keep the LAST match instead of
|
|
1601
|
+
// the first, so no early break (a forward scan that overwrites on each hit). Without a
|
|
1602
|
+
// typed handler these routed through the plain-array versions, which read elements as
|
|
1603
|
+
// raw f64 and returned garbage for non-f64 typed arrays.
|
|
1604
|
+
const findLastCommon = (arr, fn, returnIndex) => {
|
|
1605
|
+
const cbLoc = temp('tLc'), result = temp('tLr'), foundIdx = tempI32('tLi')
|
|
1606
|
+
const loop = typedLoop(arr, (load, i) => {
|
|
1607
|
+
const itemLoc = temp('tLit')
|
|
1608
|
+
return [
|
|
1609
|
+
['local.set', `$${itemLoc}`, load()],
|
|
1610
|
+
['if', truthyIR(ctx.closure.call(
|
|
1611
|
+
typed(['local.get', `$${cbLoc}`], 'f64'),
|
|
1612
|
+
[typed(['local.get', `$${itemLoc}`], 'f64'),
|
|
1613
|
+
typed(['f64.convert_i32_s', ['local.get', `$${i}`]], 'f64')])),
|
|
1614
|
+
['then',
|
|
1615
|
+
returnIndex
|
|
1616
|
+
? ['local.set', `$${foundIdx}`, ['local.get', `$${i}`]]
|
|
1617
|
+
: ['local.set', `$${result}`, typed(['local.get', `$${itemLoc}`], 'f64')]]]
|
|
1618
|
+
]
|
|
1619
|
+
})
|
|
1620
|
+
if (!loop) return null
|
|
1621
|
+
return typed(['block', ['result', 'f64'],
|
|
1622
|
+
['local.set', `$${cbLoc}`, asF64(emit(fn))],
|
|
1623
|
+
returnIndex
|
|
1624
|
+
? ['local.set', `$${foundIdx}`, ['i32.const', -1]]
|
|
1625
|
+
: ['local.set', `$${result}`, undefExpr()],
|
|
1626
|
+
...loop.setup,
|
|
1627
|
+
returnIndex
|
|
1628
|
+
? typed(['f64.convert_i32_s', ['local.get', `$${foundIdx}`]], 'f64')
|
|
1629
|
+
: typed(['local.get', `$${result}`], 'f64')], 'f64')
|
|
1630
|
+
}
|
|
1631
|
+
ctx.core.emit['.typed:findLast'] = (arr, fn) => findLastCommon(arr, fn, false)
|
|
1632
|
+
ctx.core.emit['.typed:findLastIndex'] = (arr, fn) => findLastCommon(arr, fn, true)
|
|
1633
|
+
|
|
1331
1634
|
// .some / .every: short-circuit boolean reduction. some=∃, every=∀.
|
|
1332
1635
|
const anyAllCommon = (arr, fn, isEvery) => {
|
|
1333
1636
|
const cbLoc = temp('tac'), result = tempI32('tar')
|
|
@@ -1460,4 +1763,90 @@ export default (ctx) => {
|
|
|
1460
1763
|
['i32.shl', ['local.get', `$${n}`], ['i32.const', SHIFT[et]]]],
|
|
1461
1764
|
dst.ptr], 'f64')
|
|
1462
1765
|
}
|
|
1766
|
+
|
|
1767
|
+
// .toReversed() — a reversed COPY (receiver unchanged): full slice-copy, then reverse
|
|
1768
|
+
// the copy in place. (slice bails on BigInt, so BigInt receivers fall back to a throw.)
|
|
1769
|
+
ctx.core.emit['.typed:toReversed'] = (arr) => {
|
|
1770
|
+
const copy = ctx.core.emit['.typed:slice'](arr)
|
|
1771
|
+
if (!copy) return null
|
|
1772
|
+
inc('__typed_reverse')
|
|
1773
|
+
const c = temp('ttv')
|
|
1774
|
+
return typed(['block', ['result', 'f64'],
|
|
1775
|
+
['local.set', `$${c}`, asF64(copy)],
|
|
1776
|
+
['call', '$__typed_reverse', ['i64.reinterpret_f64', ['local.get', `$${c}`]]]], 'f64')
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// .toSorted(fn?) — a sorted COPY (receiver unchanged): slice-copy, then sort in place.
|
|
1780
|
+
ctx.core.emit['.typed:toSorted'] = (arr, fn) => {
|
|
1781
|
+
const copy = ctx.core.emit['.typed:slice'](arr)
|
|
1782
|
+
return copy ? emitTypedSort(copy, fn) : null
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
// .with(index, value) — a COPY with one element replaced (receiver unchanged). Negative
|
|
1786
|
+
// index counts from the end; out of range throws RangeError ($__jz_err), per spec.
|
|
1787
|
+
ctx.core.emit['.typed:with'] = (arr, index, value) => {
|
|
1788
|
+
const copy = ctx.core.emit['.typed:slice'](arr)
|
|
1789
|
+
if (!copy) return null
|
|
1790
|
+
ctx.runtime.throws = true
|
|
1791
|
+
inc('__len', '__typed_set_idx')
|
|
1792
|
+
const c = temp('twc'), idx = tempI32('twi'), len = tempI32('twl')
|
|
1793
|
+
return typed(['block', ['result', 'f64'],
|
|
1794
|
+
['local.set', `$${c}`, asF64(copy)],
|
|
1795
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${c}`]]]],
|
|
1796
|
+
['local.set', `$${idx}`, asI32(emit(index))],
|
|
1797
|
+
['if', ['i32.lt_s', ['local.get', `$${idx}`], ['i32.const', 0]],
|
|
1798
|
+
['then', ['local.set', `$${idx}`, ['i32.add', ['local.get', `$${idx}`], ['local.get', `$${len}`]]]]],
|
|
1799
|
+
['if', ['i32.or',
|
|
1800
|
+
['i32.lt_s', ['local.get', `$${idx}`], ['i32.const', 0]],
|
|
1801
|
+
['i32.ge_s', ['local.get', `$${idx}`], ['local.get', `$${len}`]]],
|
|
1802
|
+
['then', ['throw', '$__jz_err', ['f64.const', 0]]]],
|
|
1803
|
+
['drop', ['call', '$__typed_set_idx', ['i64.reinterpret_f64', ['local.get', `$${c}`]],
|
|
1804
|
+
['local.get', `$${idx}`], asF64(emit(value))]],
|
|
1805
|
+
['local.get', `$${c}`]], 'f64')
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// .subarray(begin, end) — a zero-copy VIEW sharing the receiver's buffer (writes alias,
|
|
1809
|
+
// NOT a copy). Builds the 16-byte descriptor [byteLen][dataOff][parentOff] and tags the
|
|
1810
|
+
// TYPED ptr with aux|view, exactly like new TypedArray(buffer, byteOffset, length).
|
|
1811
|
+
ctx.core.emit['.typed:subarray'] = (arr, begin, end) => {
|
|
1812
|
+
const r = resolveElem(arr)
|
|
1813
|
+
if (!r) return null
|
|
1814
|
+
const { et, isView, isBigInt } = r
|
|
1815
|
+
const shift = SHIFT[et]
|
|
1816
|
+
const viewAux = et | 8 | (isBigInt ? 16 : 0)
|
|
1817
|
+
const arrL = temp('tua'), srcOff = tempI32('tuo'), data = tempI32('tud'), root = tempI32('tur')
|
|
1818
|
+
const len = tempI32('tul'), lo = tempI32('tulo'), hi = tempI32('tuhi'), n = tempI32('tun'), desc = tempI32('tude')
|
|
1819
|
+
inc('__len')
|
|
1820
|
+
const off4 = (o) => ['i32.load', ['i32.add', ['local.get', `$${srcOff}`], ['i32.const', o]]]
|
|
1821
|
+
const clamp = (boundExpr, dflt, name) => {
|
|
1822
|
+
if (boundExpr == null) return dflt
|
|
1823
|
+
const v = tempI32(name)
|
|
1824
|
+
return ['block', ['result', 'i32'],
|
|
1825
|
+
['local.set', `$${v}`, asI32(emit(boundExpr))],
|
|
1826
|
+
['if', ['i32.lt_s', ['local.get', `$${v}`], ['i32.const', 0]],
|
|
1827
|
+
['then', ['local.set', `$${v}`, ['i32.add', ['local.get', `$${v}`], ['local.get', `$${len}`]]]]],
|
|
1828
|
+
['if', ['i32.lt_s', ['local.get', `$${v}`], ['i32.const', 0]],
|
|
1829
|
+
['then', ['local.set', `$${v}`, ['i32.const', 0]]]],
|
|
1830
|
+
['if', ['i32.gt_s', ['local.get', `$${v}`], ['local.get', `$${len}`]],
|
|
1831
|
+
['then', ['local.set', `$${v}`, ['local.get', `$${len}`]]]],
|
|
1832
|
+
['local.get', `$${v}`]]
|
|
1833
|
+
}
|
|
1834
|
+
return typed(['block', ['result', 'f64'],
|
|
1835
|
+
['local.set', `$${arrL}`, asF64(emit(arr))],
|
|
1836
|
+
['local.set', `$${srcOff}`, typedBase(typed(['local.get', `$${arrL}`], 'f64'))],
|
|
1837
|
+
['local.set', `$${data}`, isView ? off4(4) : ['local.get', `$${srcOff}`]],
|
|
1838
|
+
['local.set', `$${root}`, isView ? off4(8) : ['local.get', `$${srcOff}`]],
|
|
1839
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${arrL}`]]]],
|
|
1840
|
+
['local.set', `$${lo}`, clamp(begin, ['i32.const', 0], 'tub')],
|
|
1841
|
+
['local.set', `$${hi}`, clamp(end, ['local.get', `$${len}`], 'tue')],
|
|
1842
|
+
['local.set', `$${n}`, ['select',
|
|
1843
|
+
['i32.sub', ['local.get', `$${hi}`], ['local.get', `$${lo}`]], ['i32.const', 0],
|
|
1844
|
+
['i32.gt_s', ['local.get', `$${hi}`], ['local.get', `$${lo}`]]]],
|
|
1845
|
+
['local.set', `$${desc}`, ['call', '$__alloc', ['i32.const', 16]]],
|
|
1846
|
+
['i32.store', ['local.get', `$${desc}`], ['i32.shl', ['local.get', `$${n}`], ['i32.const', shift]]],
|
|
1847
|
+
['i32.store', ['i32.add', ['local.get', `$${desc}`], ['i32.const', 4]],
|
|
1848
|
+
['i32.add', ['local.get', `$${data}`], ['i32.shl', ['local.get', `$${lo}`], ['i32.const', shift]]]],
|
|
1849
|
+
['i32.store', ['i32.add', ['local.get', `$${desc}`], ['i32.const', 8]], ['local.get', `$${root}`]],
|
|
1850
|
+
mkPtrIR(PTR.TYPED, viewAux, ['local.get', `$${desc}`])], 'f64')
|
|
1851
|
+
}
|
|
1463
1852
|
}
|