jz 0.9.1 → 0.9.2
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 +19 -7
- package/bench/bench.svg +20 -20
- package/dist/interop.js +1 -1
- package/dist/jz.js +1642 -1045
- package/interop.js +73 -6
- package/jzify/index.js +7 -1
- package/jzify/transform.js +21 -2
- package/jzify/webrt.js +145 -0
- package/layout.js +13 -3
- package/module/array.js +49 -42
- package/module/collection.js +222 -111
- package/module/console.js +4 -0
- package/module/core.js +86 -7
- package/module/crypto.js +124 -0
- package/module/index.js +3 -1
- package/module/math.js +5 -0
- package/module/navigator.js +26 -0
- package/module/schema.js +11 -3
- package/module/string.js +433 -28
- package/module/timer.js +42 -1
- package/module/typedarray.js +370 -60
- package/package.json +3 -3
- package/src/abi/array.js +24 -16
- package/src/abi/index.js +2 -2
- package/src/abi/object.js +17 -0
- package/src/ast.js +53 -0
- package/src/autoload.js +19 -3
- package/src/compile/analyze.js +190 -21
- package/src/compile/emit-assign.js +111 -7
- package/src/compile/emit.js +84 -20
- package/src/compile/index.js +37 -4
- package/src/compile/inplace-store.js +10 -9
- package/src/compile/narrow.js +71 -1
- package/src/compile/plan/index.js +5 -0
- package/src/compile/plan/literals.js +11 -2
- package/src/ctx.js +14 -0
- package/src/ir.js +26 -1
- package/src/optimize/index.js +1 -0
- package/src/optimize/vectorize.js +80 -6
- package/src/prepare/index.js +6 -0
- package/src/static.js +31 -0
- package/src/type.js +356 -47
package/module/console.js
CHANGED
|
@@ -194,6 +194,8 @@ const setupWasi = (ctx) => {
|
|
|
194
194
|
makeConsole('log', 1)
|
|
195
195
|
makeConsole('warn', 2)
|
|
196
196
|
makeConsole('error', 2)
|
|
197
|
+
makeConsole('info', 1) // spec/Node: info & debug are log-level -> stdout
|
|
198
|
+
makeConsole('debug', 1)
|
|
197
199
|
|
|
198
200
|
const needClock = () => hostImport('wasi_snapshot_preview1', 'clock_time_get',
|
|
199
201
|
['func', '$__clock_time_get', ['param', 'i32'], ['param', 'i64'], ['param', 'i32'], ['result', 'i32']])
|
|
@@ -266,6 +268,8 @@ const setupJsHost = (ctx) => {
|
|
|
266
268
|
makeConsole('log', 1)
|
|
267
269
|
makeConsole('warn', 2)
|
|
268
270
|
makeConsole('error', 2)
|
|
271
|
+
makeConsole('info', 1) // spec/Node: info & debug are log-level -> stdout
|
|
272
|
+
makeConsole('debug', 1)
|
|
269
273
|
|
|
270
274
|
reg('Date.now', [], () => {
|
|
271
275
|
needNow()
|
package/module/core.js
CHANGED
|
@@ -15,6 +15,7 @@ import { reconstructArgsWithSpreads } from '../src/ir.js'
|
|
|
15
15
|
import { valTypeOf, shapeOf } from '../src/kind.js'
|
|
16
16
|
import { T } from '../src/ast.js'
|
|
17
17
|
import { inlineArraySid } from '../src/static.js'
|
|
18
|
+
import { packedI32, structInline } from '../src/abi/index.js'
|
|
18
19
|
import { VAL, lookupValType, lookupNotString, repOf, updateRep } from '../src/reps.js'
|
|
19
20
|
import { ctx, err, inc, PTR, LAYOUT, HEAP, FORWARDING_MASK, emitArity, followForwardingWat, declGlobal } from '../src/ctx.js'
|
|
20
21
|
import { ptrOffsetFwdWat, STR_INTERN_BIT } from '../layout.js'
|
|
@@ -31,6 +32,7 @@ export default (ctx) => {
|
|
|
31
32
|
__len: ['__typed_shift', '__ptr_offset', '__ptr_offset_fwd'],
|
|
32
33
|
__cap: ['__typed_shift', '__ptr_type', '__ptr_offset', '__ptr_aux'],
|
|
33
34
|
__typed_data: ['__ptr_offset', '__ptr_aux'],
|
|
35
|
+
__typed_idx: () => (ctx.features.f16 ? ['__f16_to_f64'] : []),
|
|
34
36
|
__ptr_offset: ['__ptr_offset_fwd'],
|
|
35
37
|
__ptr_offset_fwd: [],
|
|
36
38
|
__is_str_key: ['__ptr_type'],
|
|
@@ -233,9 +235,11 @@ export default (ctx) => {
|
|
|
233
235
|
(then (f64.convert_i32_u (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))
|
|
234
236
|
(else (f64.convert_i32_s (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
|
|
235
237
|
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 2))
|
|
236
|
-
(then (if (result f64) (i32.and (local.get $
|
|
238
|
+
(then ${ctx.features.f16 ? `(if (result f64) (i32.and (local.get $aux) (i32.const 32))
|
|
239
|
+
(then (call $__f16_to_f64 (i32.load16_u (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))
|
|
240
|
+
(else ` : ''}(if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
237
241
|
(then (f64.convert_i32_u (i32.load16_u (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))
|
|
238
|
-
(else (f64.convert_i32_s (i32.load16_s (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))))
|
|
242
|
+
(else (f64.convert_i32_s (i32.load16_s (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1)))))))${ctx.features.f16 ? '))' : ''})
|
|
239
243
|
(else (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
240
244
|
(then (f64.convert_i32_u (i32.load8_u (i32.add (local.get $off) (local.get $i)))))
|
|
241
245
|
(else (f64.convert_i32_s (i32.load8_s (i32.add (local.get $off) (local.get $i)))))))))))))
|
|
@@ -657,6 +661,74 @@ export default (ctx) => {
|
|
|
657
661
|
(then (i32.load (i32.add (local.get $off) (i32.const 4))))
|
|
658
662
|
(else (local.get $off))))`
|
|
659
663
|
|
|
664
|
+
// === binary16 ↔ f64 (Float16Array / DataView.getFloat16 / Math.f16round) ===
|
|
665
|
+
// Pure-integer, exactly rounded — f64 → f16 rounds DIRECTLY off the f64 bits
|
|
666
|
+
// (an f32 hop double-rounds at the overflow boundary: 65519.999… must round
|
|
667
|
+
// to 65504, not Inf). f16 → f64 is exact by construction (every half is a
|
|
668
|
+
// double). NaN canonicalizes (sign/payload dropped) so `v !== v` stays sound
|
|
669
|
+
// on jz's canonical-NaN model.
|
|
670
|
+
ctx.core.stdlib['__f16_to_f64'] = `(func $__f16_to_f64 (param $b i32) (result f64)
|
|
671
|
+
(local $h i32) (local $e i32) (local $m i32) (local $r f64)
|
|
672
|
+
(local.set $h (i32.and (local.get $b) (i32.const 0x7FFF)))
|
|
673
|
+
(local.set $e (i32.shr_u (local.get $h) (i32.const 10)))
|
|
674
|
+
(local.set $m (i32.and (local.get $h) (i32.const 0x3FF)))
|
|
675
|
+
(if (i32.eq (local.get $e) (i32.const 31))
|
|
676
|
+
(then
|
|
677
|
+
(if (local.get $m)
|
|
678
|
+
(then (return (f64.reinterpret_i64 (i64.const ${nanPrefixHex()}))))
|
|
679
|
+
(else (return (f64.reinterpret_i64 (i64.or (i64.const 0x7FF0000000000000)
|
|
680
|
+
(i64.shl (i64.extend_i32_u (i32.and (local.get $b) (i32.const 0x8000))) (i64.const 48)))))))))
|
|
681
|
+
(if (i32.eqz (local.get $e))
|
|
682
|
+
;; subnormal: mant · 2^-24 (exact — integer times a power of two)
|
|
683
|
+
(then (local.set $r (f64.mul (f64.convert_i32_u (local.get $m)) (f64.const 5.960464477539063e-8))))
|
|
684
|
+
(else (local.set $r (f64.reinterpret_i64 (i64.or
|
|
685
|
+
(i64.shl (i64.extend_i32_u (i32.add (local.get $e) (i32.const 1008))) (i64.const 52))
|
|
686
|
+
(i64.shl (i64.extend_i32_u (local.get $m)) (i64.const 42)))))))
|
|
687
|
+
(f64.reinterpret_i64 (i64.or (i64.reinterpret_f64 (local.get $r))
|
|
688
|
+
(i64.shl (i64.extend_i32_u (i32.and (local.get $b) (i32.const 0x8000))) (i64.const 48)))))`
|
|
689
|
+
|
|
690
|
+
ctx.core.stdlib['__f64_to_f16'] = `(func $__f64_to_f16 (param $v f64) (result i32)
|
|
691
|
+
(local $u i64) (local $sign i32) (local $ne i32) (local $half i32)
|
|
692
|
+
(local $m i64) (local $full i64) (local $sh i64) (local $rb i64) (local $hp i64)
|
|
693
|
+
(local.set $u (i64.reinterpret_f64 (local.get $v)))
|
|
694
|
+
(local.set $sign (i32.wrap_i64 (i64.and (i64.shr_u (local.get $u) (i64.const 48)) (i64.const 0x8000))))
|
|
695
|
+
(local.set $u (i64.and (local.get $u) (i64.const 0x7FFFFFFFFFFFFFFF)))
|
|
696
|
+
(if (i64.ge_u (local.get $u) (i64.const 0x7FF0000000000000))
|
|
697
|
+
(then (return (i32.or (local.get $sign)
|
|
698
|
+
(select (i32.const 0x7E00) (i32.const 0x7C00) (i64.gt_u (local.get $u) (i64.const 0x7FF0000000000000)))))))
|
|
699
|
+
(local.set $m (i64.and (local.get $u) (i64.const 0xFFFFFFFFFFFFF)))
|
|
700
|
+
(local.set $ne (i32.sub (i32.wrap_i64 (i64.shr_u (local.get $u) (i64.const 52))) (i32.const 1008)))
|
|
701
|
+
(if (i32.ge_s (local.get $ne) (i32.const 31))
|
|
702
|
+
(then (return (i32.or (local.get $sign) (i32.const 0x7C00)))))
|
|
703
|
+
(if (i32.ge_s (local.get $ne) (i32.const 1))
|
|
704
|
+
(then ;; normal: 42 dropped mantissa bits round ties-to-even; a mantissa
|
|
705
|
+
;; carry overflows into the exponent or into infinity — both exact
|
|
706
|
+
(local.set $half (i32.or (i32.shl (local.get $ne) (i32.const 10))
|
|
707
|
+
(i32.wrap_i64 (i64.shr_u (local.get $m) (i64.const 42)))))
|
|
708
|
+
(local.set $rb (i64.and (local.get $m) (i64.const 0x3FFFFFFFFFF)))
|
|
709
|
+
(if (i32.or (i64.gt_u (local.get $rb) (i64.const 0x20000000000))
|
|
710
|
+
(i32.and (i64.eq (local.get $rb) (i64.const 0x20000000000)) (i32.and (local.get $half) (i32.const 1))))
|
|
711
|
+
(then (local.set $half (i32.add (local.get $half) (i32.const 1)))))
|
|
712
|
+
(return (i32.or (local.get $sign) (local.get $half)))))
|
|
713
|
+
(if (i32.lt_s (local.get $ne) (i32.const -10))
|
|
714
|
+
(then (return (local.get $sign)))) ;; underflow → ±0
|
|
715
|
+
;; subnormal: shift the 53-bit significand by 43-ne (43…53), ties-to-even
|
|
716
|
+
(local.set $full (i64.or (local.get $m) (i64.const 0x10000000000000)))
|
|
717
|
+
(local.set $sh (i64.extend_i32_u (i32.sub (i32.const 43) (local.get $ne))))
|
|
718
|
+
(local.set $half (i32.wrap_i64 (i64.shr_u (local.get $full) (local.get $sh))))
|
|
719
|
+
(local.set $rb (i64.and (local.get $full) (i64.sub (i64.shl (i64.const 1) (local.get $sh)) (i64.const 1))))
|
|
720
|
+
(local.set $hp (i64.shl (i64.const 1) (i64.sub (local.get $sh) (i64.const 1))))
|
|
721
|
+
(if (i32.or (i64.gt_u (local.get $rb) (local.get $hp))
|
|
722
|
+
(i32.and (i64.eq (local.get $rb) (local.get $hp)) (i32.and (local.get $half) (i32.const 1))))
|
|
723
|
+
(then (local.set $half (i32.add (local.get $half) (i32.const 1)))))
|
|
724
|
+
(i32.or (local.get $sign) (local.get $half)))`
|
|
725
|
+
|
|
726
|
+
// ToUint8Clamp (Uint8ClampedArray stores): NaN → 0, clamp [0,255],
|
|
727
|
+
// round-half-to-even — f64.nearest IS ties-to-even.
|
|
728
|
+
ctx.core.stdlib['__u8_clamp'] = `(func $__u8_clamp (param $v f64) (result i32)
|
|
729
|
+
(if (f64.ne (local.get $v) (local.get $v)) (then (return (i32.const 0))))
|
|
730
|
+
(i32.trunc_sat_f64_u (f64.nearest (f64.min (f64.max (local.get $v) (f64.const 0)) (f64.const 255)))))`
|
|
731
|
+
|
|
660
732
|
// Hot (~85M calls in watr self-host). Type/offset extraction inlined; forwarding
|
|
661
733
|
// loop only entered for ARRAY. ARRAY fast path dominates (nodes?.length, out.length …).
|
|
662
734
|
ctx.core.stdlib['__len'] = `(func $__len (param $ptr i64) (result i32)
|
|
@@ -857,11 +929,13 @@ export default (ctx) => {
|
|
|
857
929
|
(then
|
|
858
930
|
(local.set $cap (call $__cap (local.get $bits)))
|
|
859
931
|
(local.set $src (call $__ptr_offset (local.get $bits)))
|
|
860
|
-
|
|
932
|
+
;; 28 = MAP_ENTRY + the probe hash lane (collection.js) — the wholesale
|
|
933
|
+
;; copy must carry the lane or the clone's probes see stale zeros
|
|
934
|
+
(local.set $dst (call $__alloc_hdr_n (i32.const 0) (local.get $cap) (i32.const 28)))
|
|
861
935
|
(memory.copy
|
|
862
936
|
(i32.sub (local.get $dst) (i32.const 16))
|
|
863
937
|
(i32.sub (local.get $src) (i32.const 16))
|
|
864
|
-
(i32.add (i32.const 16) (i32.mul (local.get $cap) (i32.const
|
|
938
|
+
(i32.add (i32.const 16) (i32.mul (local.get $cap) (i32.const 28))))
|
|
865
939
|
(return (call $__mkptr (i32.const ${PTR.HASH}) (i32.const 0) (local.get $dst)))))
|
|
866
940
|
(local.get $v))`
|
|
867
941
|
|
|
@@ -951,6 +1025,9 @@ export default (ctx) => {
|
|
|
951
1025
|
const base = (baseExpr?.ptrKind != null && baseExpr.ptrKind !== VAL.ARRAY)
|
|
952
1026
|
? baseExpr
|
|
953
1027
|
: (baseExpr?.type === 'f64' ? baseExpr : asF64(baseExpr))
|
|
1028
|
+
// Packed i32 cells (structInline + inlineCellI32, flag rides the cursor
|
|
1029
|
+
// node): the field IS a raw i32 at +idx*4 — one i32.load, no trunc_sat.
|
|
1030
|
+
if (baseExpr?.cellI32) return typed(packedI32.ops.load(ptrOffsetIR(base, VAL.OBJECT), idx), 'i32')
|
|
954
1031
|
const load = ctx.abi.object.ops.load(ptrOffsetIR(base, VAL.OBJECT), idx)
|
|
955
1032
|
// Strict-int32 slot (ctx.schema.slotI32CertainAt — every censused write is
|
|
956
1033
|
// exactly-int32, never -0): land the value directly in i32. trunc_sat of
|
|
@@ -1306,13 +1383,15 @@ export default (ctx) => {
|
|
|
1306
1383
|
if (Array.isArray(obj) && (obj[0] === 'str' || obj[0] == null) && typeof obj[1] === 'string') {
|
|
1307
1384
|
return typed(['f64.const', new TextEncoder().encode(obj[1]).length], 'f64')
|
|
1308
1385
|
}
|
|
1309
|
-
// structInline Array<S>: the header `len` counts physical
|
|
1310
|
-
// per element), so the JS array length is
|
|
1386
|
+
// structInline Array<S>: the header `len` counts physical 8-byte cells
|
|
1387
|
+
// (K per element, ⌈K/2⌉ when packed i32), so the JS array length is
|
|
1388
|
+
// `physicalLen / cellsPerElem`.
|
|
1311
1389
|
const inlSid = inlineArraySid(obj)
|
|
1312
1390
|
if (inlSid != null) {
|
|
1313
1391
|
const K = ctx.schema.list[inlSid].length
|
|
1392
|
+
const cpe = structInline(K, ctx.schema.inlineCellI32?.has(inlSid)).cpe
|
|
1314
1393
|
const physLen = ['i32.load', ['i32.sub', ptrOffsetIR(asF64(emit(obj)), VAL.ARRAY), ['i32.const', 8]]]
|
|
1315
|
-
return typed(['f64.convert_i32_s',
|
|
1394
|
+
return typed(['f64.convert_i32_s', cpe > 1 ? ['i32.div_s', physLen, ['i32.const', cpe]] : physLen], 'f64')
|
|
1316
1395
|
}
|
|
1317
1396
|
const rep = typeof obj === 'string' ? repOf(obj) : null
|
|
1318
1397
|
const vt = rep ? rep.val : valTypeOf(obj)
|
package/module/crypto.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* crypto — Web Crypto's compute slice: getRandomValues + randomUUID.
|
|
3
|
+
*
|
|
4
|
+
* Entropy rides the host's CSPRNG through the same channel Math.random seeds
|
|
5
|
+
* from: WASI `random_get` under host:'wasi', an `env.random` byte-fill (wired
|
|
6
|
+
* by interop from globalThis.crypto.getRandomValues) under host:'js'. A
|
|
7
|
+
* numeric `randomSeed` option switches BOTH into a deterministic xorshift32
|
|
8
|
+
* stream — reproducible runs for sims/tests, explicitly NOT cryptographic
|
|
9
|
+
* (documented divergence; the default entropy mode IS the host CSPRNG).
|
|
10
|
+
*
|
|
11
|
+
* getRandomValues keeps the spec's guards (integer typed arrays only, ≤ 64 KiB
|
|
12
|
+
* per call) and returns its argument. randomUUID mints a lowercase v4 UUID.
|
|
13
|
+
* crypto.subtle stays out by doctrine — security crypto is out of scope.
|
|
14
|
+
*
|
|
15
|
+
* @module crypto
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { typed, asI64 } from '../src/ir.js'
|
|
19
|
+
import { emit, wat, hostImport, bind } from '../src/bridge.js'
|
|
20
|
+
import { inc, declGlobal, PTR } from '../src/ctx.js'
|
|
21
|
+
|
|
22
|
+
export default (ctx) => {
|
|
23
|
+
const seeded = typeof ctx.transform.randomSeed === 'number'
|
|
24
|
+
const seedConst = seeded ? ((ctx.transform.randomSeed >>> 0) || 1) : 0 // xorshift dies on 0
|
|
25
|
+
const wasi = ctx.transform.host === 'wasi'
|
|
26
|
+
|
|
27
|
+
const needEntropy = () => wasi
|
|
28
|
+
? hostImport('wasi_snapshot_preview1', 'random_get',
|
|
29
|
+
['func', '$__random_get', ['param', 'i32'], ['param', 'i32'], ['result', 'i32']])
|
|
30
|
+
: hostImport('env', 'random',
|
|
31
|
+
['func', '$__env_random', ['param', 'i32'], ['param', 'i32']])
|
|
32
|
+
|
|
33
|
+
if (seeded) {
|
|
34
|
+
declGlobal('crypto.state', 'i32', seedConst)
|
|
35
|
+
wat('__crypto_fill', `(func $__crypto_fill (param $dst i32) (param $len i32)
|
|
36
|
+
(local $s i32) (local $i i32)
|
|
37
|
+
(local.set $s (global.get $crypto.state))
|
|
38
|
+
(block $d (loop $l
|
|
39
|
+
(br_if $d (i32.ge_s (local.get $i) (local.get $len)))
|
|
40
|
+
(local.set $s (i32.xor (local.get $s) (i32.shl (local.get $s) (i32.const 13))))
|
|
41
|
+
(local.set $s (i32.xor (local.get $s) (i32.shr_u (local.get $s) (i32.const 17))))
|
|
42
|
+
(local.set $s (i32.xor (local.get $s) (i32.shl (local.get $s) (i32.const 5))))
|
|
43
|
+
(i32.store8 (i32.add (local.get $dst) (local.get $i)) (local.get $s))
|
|
44
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
45
|
+
(br $l)))
|
|
46
|
+
(global.set $crypto.state (local.get $s)))`)
|
|
47
|
+
} else {
|
|
48
|
+
wat('__crypto_fill', wasi
|
|
49
|
+
? `(func $__crypto_fill (param $dst i32) (param $len i32)
|
|
50
|
+
(drop (call $__random_get (local.get $dst) (local.get $len))))`
|
|
51
|
+
: `(func $__crypto_fill (param $dst i32) (param $len i32)
|
|
52
|
+
(call $__env_random (local.get $dst) (local.get $len)))`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Spec guards (WebCryptoAPI §10.1.1): receiver must be an integer typed
|
|
56
|
+
// array (floats → TypeMismatchError), byteLength ≤ 65536 (QuotaExceeded).
|
|
57
|
+
// Element codes 0–5 are the int family; code 7 + the bigint flag is
|
|
58
|
+
// BigInt64/BigUint64 (allowed). Returns the receiver, filled.
|
|
59
|
+
wat('__get_random_values', `(func $__get_random_values (param $p i64) (result f64)
|
|
60
|
+
(local $aux i32) (local $et i32) (local $bl i32)
|
|
61
|
+
(if (i32.ne (call $__ptr_type (local.get $p)) (i32.const ${PTR.TYPED}))
|
|
62
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
63
|
+
(local.set $aux (call $__ptr_aux (local.get $p)))
|
|
64
|
+
(local.set $et (i32.and (local.get $aux) (i32.const 7)))
|
|
65
|
+
(if (i32.and
|
|
66
|
+
(i32.gt_u (local.get $et) (i32.const 5))
|
|
67
|
+
(i32.eqz (i32.and (local.get $aux) (i32.const 16))))
|
|
68
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
69
|
+
(local.set $bl (i32.shl (call $__len (local.get $p)) (call $__typed_shift (local.get $et))))
|
|
70
|
+
(if (i32.gt_u (local.get $bl) (i32.const 65536))
|
|
71
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
72
|
+
(call $__crypto_fill (call $__typed_data (local.get $p)) (local.get $bl))
|
|
73
|
+
(f64.reinterpret_i64 (local.get $p)))`,
|
|
74
|
+
['__crypto_fill', '__ptr_type', '__ptr_aux', '__len', '__typed_shift', '__typed_data'])
|
|
75
|
+
|
|
76
|
+
// Lowercase v4 UUID: 16 entropy bytes, version/variant bits, hex + dashes.
|
|
77
|
+
wat('__random_uuid', `(func $__random_uuid (result f64)
|
|
78
|
+
(local $buf i32) (local $base i32) (local $out i32)
|
|
79
|
+
(local $i i32) (local $j i32) (local $b i32) (local $n i32)
|
|
80
|
+
(local.set $buf (call $__alloc (i32.const 16)))
|
|
81
|
+
(call $__crypto_fill (local.get $buf) (i32.const 16))
|
|
82
|
+
(i32.store8 (i32.add (local.get $buf) (i32.const 6))
|
|
83
|
+
(i32.or (i32.and (i32.load8_u (i32.add (local.get $buf) (i32.const 6))) (i32.const 15)) (i32.const 64)))
|
|
84
|
+
(i32.store8 (i32.add (local.get $buf) (i32.const 8))
|
|
85
|
+
(i32.or (i32.and (i32.load8_u (i32.add (local.get $buf) (i32.const 8))) (i32.const 63)) (i32.const 128)))
|
|
86
|
+
(local.set $base (call $__alloc (i32.const 40)))
|
|
87
|
+
(i32.store (local.get $base) (i32.const 36))
|
|
88
|
+
(local.set $out (i32.add (local.get $base) (i32.const 4)))
|
|
89
|
+
(block $d (loop $l
|
|
90
|
+
(br_if $d (i32.ge_u (local.get $i) (i32.const 16)))
|
|
91
|
+
(local.set $b (i32.load8_u (i32.add (local.get $buf) (local.get $i))))
|
|
92
|
+
(local.set $n (i32.shr_u (local.get $b) (i32.const 4)))
|
|
93
|
+
(i32.store8 (i32.add (local.get $out) (local.get $j))
|
|
94
|
+
(select (i32.add (local.get $n) (i32.const 87)) (i32.add (local.get $n) (i32.const 48)) (i32.gt_u (local.get $n) (i32.const 9))))
|
|
95
|
+
(local.set $n (i32.and (local.get $b) (i32.const 15)))
|
|
96
|
+
(i32.store8 (i32.add (i32.add (local.get $out) (local.get $j)) (i32.const 1))
|
|
97
|
+
(select (i32.add (local.get $n) (i32.const 87)) (i32.add (local.get $n) (i32.const 48)) (i32.gt_u (local.get $n) (i32.const 9))))
|
|
98
|
+
(local.set $j (i32.add (local.get $j) (i32.const 2)))
|
|
99
|
+
;; dash after bytes 3, 5, 7, 9 → 8-4-4-4-12
|
|
100
|
+
(if (i32.or
|
|
101
|
+
(i32.or (i32.eq (local.get $i) (i32.const 3)) (i32.eq (local.get $i) (i32.const 5)))
|
|
102
|
+
(i32.or (i32.eq (local.get $i) (i32.const 7)) (i32.eq (local.get $i) (i32.const 9))))
|
|
103
|
+
(then
|
|
104
|
+
(i32.store8 (i32.add (local.get $out) (local.get $j)) (i32.const 45))
|
|
105
|
+
(local.set $j (i32.add (local.get $j) (i32.const 1)))))
|
|
106
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
107
|
+
(br $l)))
|
|
108
|
+
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $out)))`,
|
|
109
|
+
['__crypto_fill', '__alloc', '__mkptr'])
|
|
110
|
+
|
|
111
|
+
bind('crypto.getRandomValues', (arr) => {
|
|
112
|
+
if (!seeded) needEntropy()
|
|
113
|
+
ctx.runtime.throws = true
|
|
114
|
+
ctx.features.typedarray = true
|
|
115
|
+
inc('__get_random_values')
|
|
116
|
+
return typed(['call', '$__get_random_values', asI64(emit(arr))], 'f64')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
bind('crypto.randomUUID', () => {
|
|
120
|
+
if (!seeded) needEntropy()
|
|
121
|
+
inc('__random_uuid')
|
|
122
|
+
return typed(['call', '$__random_uuid'], 'f64')
|
|
123
|
+
})
|
|
124
|
+
}
|
package/module/index.js
CHANGED
|
@@ -17,4 +17,6 @@ import date from './date.js'
|
|
|
17
17
|
import simd from './simd.js'
|
|
18
18
|
import fs from './fs.js'
|
|
19
19
|
import web from './web.js'
|
|
20
|
-
|
|
20
|
+
import crypto from './crypto.js'
|
|
21
|
+
import navigator from './navigator.js'
|
|
22
|
+
export { math, core, array, object, string, number, fn, typedarray, collection, symbol, console, json, regex, timer, date, simd , atomics, fs, web, crypto, navigator }
|
package/module/math.js
CHANGED
|
@@ -262,6 +262,11 @@ export default (ctx) => {
|
|
|
262
262
|
], 'f64')
|
|
263
263
|
}
|
|
264
264
|
ctx.core.emit['math.fround'] = a => typed(['f64.promote_f32', ['f32.demote_f64', toNumF64(a, emit(a))]], 'f64')
|
|
265
|
+
// ES2025 Math.f16round — round to binary16 and back (kernels in core.js)
|
|
266
|
+
ctx.core.emit['math.f16round'] = a => {
|
|
267
|
+
inc('__f64_to_f16', '__f16_to_f64')
|
|
268
|
+
return typed(['call', '$__f16_to_f64', ['call', '$__f64_to_f16', toNumF64(a, emit(a))]], 'f64')
|
|
269
|
+
}
|
|
265
270
|
// ES2025 Math.f16round — no wasm f16 ops, so round in software (exactly).
|
|
266
271
|
reg('math.f16round', ['math.f16round'], a => fn('math.f16round', a))
|
|
267
272
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* navigator — the one member with compute meaning: hardwareConcurrency.
|
|
3
|
+
* Pairs with jz.pool/SPMD workers for in-module tile sizing.
|
|
4
|
+
*
|
|
5
|
+
* host:'js' — reads through an `env.hardwareConcurrency` service (wired by
|
|
6
|
+
* interop from globalThis.navigator, present in browsers and Node ≥ 21).
|
|
7
|
+
* host:'wasi' — no host to ask: warns and folds to 1 (a loud, documented
|
|
8
|
+
* degradation; WASI p1 has no concurrency introspection).
|
|
9
|
+
*
|
|
10
|
+
* @module navigator
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { typed } from '../src/ir.js'
|
|
14
|
+
import { hostImport } from '../src/bridge.js'
|
|
15
|
+
import { warn } from '../src/ctx.js'
|
|
16
|
+
|
|
17
|
+
export default (ctx) => {
|
|
18
|
+
ctx.core.emit['navigator.hardwareConcurrency'] = () => {
|
|
19
|
+
if (ctx.transform.host === 'wasi') {
|
|
20
|
+
warn('host-global', `\`navigator.hardwareConcurrency\` has no WASI source — folded to 1; size worker pools from the embedder instead`, {})
|
|
21
|
+
return typed(['f64.const', 1], 'f64')
|
|
22
|
+
}
|
|
23
|
+
hostImport('env', 'hardwareConcurrency', ['func', '$__hw_concurrency', ['result', 'f64']])
|
|
24
|
+
return typed(['call', '$__hw_concurrency'], 'f64')
|
|
25
|
+
}
|
|
26
|
+
}
|
package/module/schema.js
CHANGED
|
@@ -97,12 +97,20 @@ export function initSchema(ctx) {
|
|
|
97
97
|
if (ctx.schema.poisoned?.has(varName)) return -1
|
|
98
98
|
const vt = lookupValType(varName)
|
|
99
99
|
if (vt !== VAL.OBJECT) return -1
|
|
100
|
-
// Structural subtyping
|
|
101
|
-
//
|
|
100
|
+
// Structural subtyping — sound only under the FULL closed-world condition:
|
|
101
|
+
// the prop lives at the SAME slot in EVERY registered schema, so whatever
|
|
102
|
+
// sid this OBJECT receiver carries, the slot is right. Checking only the
|
|
103
|
+
// schemas that CONTAIN the prop (the old form) bet that no other schema's
|
|
104
|
+
// instance could flow here — wrong the moment two shapes share a binding:
|
|
105
|
+
// `p.x` on a {z,w,q} receiver read z (slot 0 of a foreign schema), and the
|
|
106
|
+
// write sibling `p.x = v` CORRUPTED z. Receivers with a genuinely unique
|
|
107
|
+
// prop still devirtualize via guardedSlotOf's runtime-guarded path.
|
|
102
108
|
const bucket = byProp.get(prop)
|
|
103
|
-
if (!bucket) return -1
|
|
109
|
+
if (!bucket || bucket.length !== ctx.schema.list.length) return -1
|
|
104
110
|
const slot = bucket[0].slot
|
|
105
111
|
for (let i = 1; i < bucket.length; i++) if (bucket[i].slot !== slot) return -1
|
|
112
|
+
if (typeof process !== 'undefined' && process.env?.JZ_DBG_SLOTOF)
|
|
113
|
+
console.error('[slotof-structural]', varName, '.', prop, 'slot', slot, 'bucket', bucket.length, 'of', ctx.schema.list.length, 'schemas')
|
|
106
114
|
return slot
|
|
107
115
|
}
|
|
108
116
|
|