jz 0.8.1 → 0.9.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 +56 -9
- package/bench/README.md +121 -50
- package/bench/bench.svg +27 -27
- package/cli.js +3 -1
- package/dist/interop.js +1 -1
- package/dist/jz.js +7541 -6271
- package/index.js +165 -74
- package/interop.js +189 -17
- package/jzify/arguments.js +32 -1
- package/jzify/async.js +409 -0
- package/jzify/classes.js +136 -18
- package/jzify/generators.js +639 -0
- package/jzify/hoist-vars.js +73 -1
- package/jzify/index.js +123 -4
- package/jzify/names.js +1 -0
- package/jzify/transform.js +239 -1
- package/layout.js +48 -3
- package/module/array.js +299 -44
- package/module/atomics.js +144 -0
- package/module/collection.js +1429 -155
- package/module/core.js +431 -40
- package/module/date.js +142 -120
- package/module/fs.js +144 -0
- package/module/function.js +6 -3
- package/module/index.js +4 -1
- package/module/json.js +270 -49
- package/module/math.js +892 -32
- package/module/number.js +532 -163
- package/module/object.js +353 -95
- package/module/regex.js +157 -7
- package/module/schema.js +100 -2
- package/module/string.js +301 -84
- package/module/typedarray.js +264 -72
- package/module/web.js +36 -0
- package/package.json +5 -5
- package/src/abi/string.js +71 -5
- package/src/ast.js +11 -5
- package/src/autoload.js +28 -1
- package/src/bridge.js +7 -2
- package/src/compile/analyze-scans.js +22 -7
- package/src/compile/analyze.js +136 -30
- package/src/compile/dyn-closure-tables.js +277 -0
- package/src/compile/emit-assign.js +281 -15
- package/src/compile/emit.js +979 -54
- package/src/compile/index.js +271 -29
- package/src/compile/infer.js +34 -3
- package/src/compile/inplace-store.js +329 -0
- package/src/compile/narrow.js +543 -15
- package/src/compile/peel-stencil.js +5 -0
- package/src/compile/plan/index.js +45 -3
- package/src/compile/plan/inline.js +8 -1
- package/src/compile/plan/literals.js +39 -0
- package/src/compile/plan/scope.js +430 -23
- package/src/compile/program-facts.js +732 -38
- package/src/ctx.js +64 -9
- package/src/helper-counters.js +7 -1
- package/src/ir.js +113 -5
- package/src/kind-traits.js +66 -3
- package/src/kind.js +84 -12
- package/src/op-policy.js +7 -4
- package/src/optimize/index.js +1060 -750
- package/src/optimize/recurse.js +2 -2
- package/src/optimize/vectorize.js +972 -67
- package/src/prepare/index.js +792 -63
- package/src/prepare/math-kernel.js +331 -0
- package/src/prepare/pre-eval.js +714 -0
- package/src/snapshot.js +194 -0
- package/src/type.js +1170 -56
- package/src/wat/assemble.js +403 -65
- package/src/wat/codegen.js +74 -14
- package/transform.js +113 -4
- package/wasi.js +3 -0
package/module/typedarray.js
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
* @module typed
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { typed, asF64, asI32, asI64, toNumF64, UNDEF_NAN, allocPtr, mkPtrIR, ptrOffsetIR, ptrTypeEq, temp, tempI32, tempI64, undefExpr, truthyIR } from '../src/ir.js'
|
|
10
|
+
import { typed, asF64, asI32, asI64, toNumF64, UNDEF_NAN, NULL_NAN, TRUE_NAN, FALSE_NAN, allocPtr, mkPtrIR, ptrOffsetIR, ptrTypeEq, temp, tempI32, tempI64, undefExpr, truthyIR } from '../src/ir.js'
|
|
11
11
|
import { emit, idx, deps, call } from '../src/bridge.js'
|
|
12
|
+
import { strHashLiteral } from './collection.js'
|
|
12
13
|
import { valTypeOf } from '../src/kind.js'
|
|
14
|
+
import { typedIdxProven } from '../src/type.js'
|
|
13
15
|
import { VAL, lookupValType } from '../src/reps.js'
|
|
14
16
|
import { nanPrefixHex, TYPED_ELEM_NAMES, TYPED_ELEM_CODE, TYPED_ELEM_BIGINT_FLAG, encodeTypedElemAux } from '../layout.js'
|
|
15
17
|
import { inc, PTR, LAYOUT, registerGetter } from '../src/ctx.js'
|
|
@@ -236,6 +238,40 @@ export default (ctx) => {
|
|
|
236
238
|
const blen = call('__byte_length', 'I', 'i32')
|
|
237
239
|
const boff = call('__byte_offset', 'I', 'i32')
|
|
238
240
|
|
|
241
|
+
// Unknown receiver for a buffer-family accessor: only BUFFER/TYPED own
|
|
242
|
+
// `.buffer`/`.byteLength`/`.byteOffset` in JS — on everything else the name
|
|
243
|
+
// is an ordinary own property (or undefined). Same dispatch shape as
|
|
244
|
+
// collection.js's `.size` (the dot-name hijack class, .work/todo.md
|
|
245
|
+
// 2026-07-11): a PROVEN BUFFER/TYPED receiver keeps the direct helper call;
|
|
246
|
+
// otherwise tag-dispatch at runtime — the NaN-check guards real numbers
|
|
247
|
+
// whose bit pattern could false-match the tag compare, and the prehashed
|
|
248
|
+
// dyn dispatcher covers OBJECT schema slots, HASH keys, sidecars, and
|
|
249
|
+
// primitives (→ undefined).
|
|
250
|
+
const bufAccessorDyn = (prop, helper, direct) => (obj) => {
|
|
251
|
+
const vt = valTypeOf(obj)
|
|
252
|
+
if (vt === VAL.BUFFER || vt === VAL.TYPED) return direct(obj)
|
|
253
|
+
inc('__ptr_type', '__dyn_get_expr_t_h', helper)
|
|
254
|
+
const o = temp('bad'), t = tempI32('badt')
|
|
255
|
+
const og = ['local.get', `$${o}`]
|
|
256
|
+
const helperCall = ['call', `$${helper}`, ['i64.reinterpret_f64', og]]
|
|
257
|
+
return typed(['block', ['result', 'f64'],
|
|
258
|
+
['local.set', `$${o}`, asF64(emit(obj))],
|
|
259
|
+
['local.set', `$${t}`, ['call', '$__ptr_type', ['i64.reinterpret_f64', og]]],
|
|
260
|
+
['if', ['result', 'f64'],
|
|
261
|
+
['i32.and',
|
|
262
|
+
['f64.ne', og, og],
|
|
263
|
+
['i32.or',
|
|
264
|
+
['i32.eq', ['local.get', `$${t}`], ['i32.const', PTR.BUFFER]],
|
|
265
|
+
['i32.eq', ['local.get', `$${t}`], ['i32.const', PTR.TYPED]]]],
|
|
266
|
+
['then', helper === '__to_buffer' ? typed(helperCall, 'f64') : ['f64.convert_i32_s', helperCall]],
|
|
267
|
+
['else', ['f64.reinterpret_i64', ['call', '$__dyn_get_expr_t_h',
|
|
268
|
+
['i64.reinterpret_f64', og], asI64(emit(['str', prop])), ['local.get', `$${t}`],
|
|
269
|
+
['i32.const', strHashLiteral(prop)]]]]]], 'f64')
|
|
270
|
+
}
|
|
271
|
+
const bufDyn = bufAccessorDyn('buffer', '__to_buffer', buf)
|
|
272
|
+
const blenDyn = bufAccessorDyn('byteLength', '__byte_length', blen)
|
|
273
|
+
const boffDyn = bufAccessorDyn('byteOffset', '__byte_offset', boff)
|
|
274
|
+
|
|
239
275
|
// === Runtime helpers: byte length, buffer coerce ===
|
|
240
276
|
// __typed_shift lives in core (needed by __len/__cap).
|
|
241
277
|
|
|
@@ -364,20 +400,51 @@ export default (ctx) => {
|
|
|
364
400
|
['local.get', `$${parentOff}`]],
|
|
365
401
|
mkPtrIR(PTR.TYPED, typedAux(name, true), ['local.get', `$${dst}`])], 'f64')
|
|
366
402
|
}
|
|
403
|
+
// TypedArray(typedArray) COPIES into fresh storage with element conversion —
|
|
404
|
+
// spec: only (buffer[, off, len]) constructs a view. Element reads go through
|
|
405
|
+
// __typed_idx (the source's elemType lives in its aux at runtime), stores
|
|
406
|
+
// convert to THIS view's elemType — `new Float64Array(int32Arr)` converts.
|
|
407
|
+
const copyFromTyped = (srcTemp) => {
|
|
408
|
+
inc('__typed_idx', '__len')
|
|
409
|
+
const cl = tempI32('tcl'), ci = tempI32('tci')
|
|
410
|
+
const out = allocPtr({ type: PTR.TYPED, aux,
|
|
411
|
+
len: ['i32.mul', ['local.get', `$${cl}`], ['i32.const', stride]], stride: 1, tag: 'tc' })
|
|
412
|
+
const conv = FROM_F64[elemType]
|
|
413
|
+
const srcElem = ['call', '$__typed_idx', ['i64.reinterpret_f64', ['local.get', `$${srcTemp}`]], ['local.get', `$${ci}`]]
|
|
414
|
+
const cid = ctx.func.uniq++
|
|
415
|
+
return ['block', ['result', 'f64'],
|
|
416
|
+
['local.set', `$${cl}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${srcTemp}`]]]],
|
|
417
|
+
out.init,
|
|
418
|
+
['local.set', `$${ci}`, ['i32.const', 0]],
|
|
419
|
+
['block', `$tcb${cid}`, ['loop', `$tclp${cid}`,
|
|
420
|
+
['br_if', `$tcb${cid}`, ['i32.ge_s', ['local.get', `$${ci}`], ['local.get', `$${cl}`]]],
|
|
421
|
+
[STORE[elemType],
|
|
422
|
+
['i32.add', ['local.get', `$${out.local}`], ['i32.mul', ['local.get', `$${ci}`], ['i32.const', stride]]],
|
|
423
|
+
conv ? [conv, srcElem] : srcElem],
|
|
424
|
+
['local.set', `$${ci}`, ['i32.add', ['local.get', `$${ci}`], ['i32.const', 1]]],
|
|
425
|
+
['br', `$tclp${cid}`]]],
|
|
426
|
+
out.ptr]
|
|
427
|
+
}
|
|
367
428
|
// Single arg array-like source: copy elements instead of treating the pointer as a length.
|
|
368
429
|
if (srcType === VAL.ARRAY && ctx.core.emit[`${name}.from`])
|
|
369
430
|
return ctx.core.emit[`${name}.from`](lenExpr)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
431
|
+
if (srcType === VAL.TYPED) {
|
|
432
|
+
const src = temp('ts')
|
|
433
|
+
return typed(['block', ['result', 'f64'],
|
|
434
|
+
['local.set', `$${src}`, asF64(emit(lenExpr))],
|
|
435
|
+
copyFromTyped(src)], 'f64')
|
|
436
|
+
}
|
|
437
|
+
// Reinterpret on a buffer: zero-copy view. TYPED retagged at the same offset —
|
|
438
|
+
// the byteLen header is shared with the parent. __len(view) = byteLen >> shift
|
|
439
|
+
// computes elemCount for this view's elemType.
|
|
440
|
+
if (srcType === VAL.BUFFER) {
|
|
374
441
|
ctx.features.typedView = true // zero-copy reinterpret aliases the source — SLP must not pack across it
|
|
375
442
|
return mkPtrIR(PTR.TYPED, aux, ['call', '$__ptr_offset', ['i64.reinterpret_f64', asF64(emit(lenExpr))]])
|
|
376
443
|
}
|
|
377
444
|
if (srcType == null && ctx.core.emit[`${name}.from`]) {
|
|
378
|
-
ctx.features.typedView = true // unknown arg: runtime may take the buffer
|
|
445
|
+
ctx.features.typedView = true // unknown arg: runtime may take the buffer zero-copy-view branch
|
|
379
446
|
|
|
380
|
-
// Runtime dispatch: number → allocate; array → copy elements; buffer
|
|
447
|
+
// Runtime dispatch: number → allocate; array/typed → copy elements; buffer → zero-copy view.
|
|
381
448
|
const src = temp('ts')
|
|
382
449
|
const len = tempI32('tl')
|
|
383
450
|
const shift = SHIFT[elemType]
|
|
@@ -392,12 +459,15 @@ export default (ctx) => {
|
|
|
392
459
|
['local.set', `$${len}`, ['i32.trunc_sat_f64_s', ['local.get', `$${src}`]]],
|
|
393
460
|
numAlloc.init,
|
|
394
461
|
numAlloc.ptr]],
|
|
395
|
-
// Pointer: array → copy
|
|
462
|
+
// Pointer: array → boxed-slot copy; typed → converted element copy; buffer → zero-copy view
|
|
396
463
|
['else', ['if', ['result', 'f64'],
|
|
397
464
|
ptrTypeEq(['local.get', `$${src}`], PTR.ARRAY),
|
|
398
465
|
['then', ctx.core.emit[`${name}.from`](src)],
|
|
399
|
-
['else',
|
|
400
|
-
['
|
|
466
|
+
['else', ['if', ['result', 'f64'],
|
|
467
|
+
ptrTypeEq(['local.get', `$${src}`], PTR.TYPED),
|
|
468
|
+
['then', copyFromTyped(src)],
|
|
469
|
+
['else', mkPtrIR(PTR.TYPED, aux,
|
|
470
|
+
['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${src}`]]])]]]]]]], 'f64')
|
|
401
471
|
}
|
|
402
472
|
// Normal: allocate fresh typed array (lenExpr is numeric size). Header stores byteLen.
|
|
403
473
|
const shift = SHIFT[elemType]
|
|
@@ -496,7 +566,7 @@ export default (ctx) => {
|
|
|
496
566
|
}
|
|
497
567
|
}
|
|
498
568
|
}
|
|
499
|
-
return
|
|
569
|
+
return bufDyn(obj)
|
|
500
570
|
})
|
|
501
571
|
|
|
502
572
|
// .byteLength — BUFFER: raw __len. Owned TYPED: elemCount * stride.
|
|
@@ -521,7 +591,7 @@ export default (ctx) => {
|
|
|
521
591
|
}
|
|
522
592
|
}
|
|
523
593
|
}
|
|
524
|
-
return
|
|
594
|
+
return blenDyn(obj)
|
|
525
595
|
})
|
|
526
596
|
|
|
527
597
|
// .byteOffset — owned: 0. View: descriptor[4] - descriptor[8].
|
|
@@ -539,7 +609,7 @@ export default (ctx) => {
|
|
|
539
609
|
}
|
|
540
610
|
if (ctor?.startsWith('new.') && TYPED_ELEM_CODE[ctor.slice(4)] != null) return typed(['f64.const', 0], 'f64')
|
|
541
611
|
}
|
|
542
|
-
return
|
|
612
|
+
return boffDyn(obj)
|
|
543
613
|
})
|
|
544
614
|
|
|
545
615
|
// Runtime fallback for .byteOffset when variable view-ness is unknown.
|
|
@@ -580,20 +650,22 @@ export default (ctx) => {
|
|
|
580
650
|
|
|
581
651
|
// buf.slice(begin?, end?) on a BUFFER → fresh BUFFER with the byte range copied.
|
|
582
652
|
// Only dispatches statically when obj is a tracked ArrayBuffer/DataView variable.
|
|
653
|
+
// Indices normalize through __clamp_idx (negative wraps from the end, then
|
|
654
|
+
// clamp to [0, byteLength]) — the same bounds dance as every other range op.
|
|
583
655
|
ctx.core.emit['.buf:slice'] = (obj, beginExpr, endExpr) => {
|
|
656
|
+
inc('__clamp_idx')
|
|
584
657
|
const src = temp('bss')
|
|
585
658
|
const beg = tempI32('bsb')
|
|
586
659
|
const end = tempI32('bse')
|
|
587
660
|
const bytes = tempI32('bsn')
|
|
588
661
|
const out = allocPtr({ type: PTR.BUFFER, len: ['local.get', `$${bytes}`], stride: 1, tag: 'bsd' })
|
|
662
|
+
const lenWat = ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${src}`]]]
|
|
589
663
|
const beginWat = beginExpr == null ? ['i32.const', 0] : asI32(emit(beginExpr))
|
|
590
|
-
const endWat = endExpr == null
|
|
591
|
-
? ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${src}`]]]
|
|
592
|
-
: asI32(emit(endExpr))
|
|
664
|
+
const endWat = endExpr == null ? lenWat : asI32(emit(endExpr))
|
|
593
665
|
return typed(['block', ['result', 'f64'],
|
|
594
666
|
['local.set', `$${src}`, asF64(emit(obj))],
|
|
595
|
-
['local.set', `$${beg}`, beginWat],
|
|
596
|
-
['local.set', `$${end}`, endWat],
|
|
667
|
+
['local.set', `$${beg}`, ['call', '$__clamp_idx', beginWat, lenWat]],
|
|
668
|
+
['local.set', `$${end}`, ['call', '$__clamp_idx', endWat, lenWat]],
|
|
597
669
|
['local.set', `$${bytes}`, ['i32.sub', ['local.get', `$${end}`], ['local.get', `$${beg}`]]],
|
|
598
670
|
['if',
|
|
599
671
|
['i32.lt_s', ['local.get', `$${bytes}`], ['i32.const', 0]],
|
|
@@ -909,6 +981,29 @@ export default (ctx) => {
|
|
|
909
981
|
const stride = STRIDE[elemType], store = STORE[elemType]
|
|
910
982
|
ctx.core.emit[`${name}.from`] = (src) => {
|
|
911
983
|
ctx.features.typedarray = true
|
|
984
|
+
// Bare array-literal source (`Int32Array.from([…])`, `new Int32Array([…])`): build the
|
|
985
|
+
// typed array directly — alloc + one native-typed store per element — instead of
|
|
986
|
+
// materializing an intermediate f64 ARRAY (every element a 9-byte f64.const) plus a
|
|
987
|
+
// per-element f64→elem copy loop. For an integer view each element then stores as an
|
|
988
|
+
// i32.const (2 B for small values), so a constant program / lookup table stops
|
|
989
|
+
// round-tripping through the boxed-f64 array. Fresh alloc per evaluation, so identity
|
|
990
|
+
// and mutation semantics are unchanged. BigInt views (elemType>7) keep the loop.
|
|
991
|
+
if (Array.isArray(src) && src[0] === '[' && elemType <= 7
|
|
992
|
+
&& !src.slice(1).some(e => Array.isArray(e) && e[0] === '...')) {
|
|
993
|
+
const elems = src.slice(1)
|
|
994
|
+
const out = allocPtr({ type: PTR.TYPED, aux, len: ['i32.const', elems.length * stride], stride: 1, tag: 'tf' })
|
|
995
|
+
const body = [out.init]
|
|
996
|
+
for (let k = 0; k < elems.length; k++) {
|
|
997
|
+
const addr = k === 0 ? ['local.get', `$${out.local}`]
|
|
998
|
+
: ['i32.add', ['local.get', `$${out.local}`], ['i32.const', k * stride]]
|
|
999
|
+
const v = elemType <= 5 ? asI32(emit(elems[k]))
|
|
1000
|
+
: elemType === 6 ? ['f32.demote_f64', asF64(emit(elems[k]))]
|
|
1001
|
+
: asF64(emit(elems[k]))
|
|
1002
|
+
body.push([store, addr, v])
|
|
1003
|
+
}
|
|
1004
|
+
body.push(out.ptr)
|
|
1005
|
+
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
1006
|
+
}
|
|
912
1007
|
const srcL = temp('tfs')
|
|
913
1008
|
const len = tempI32('tfl'), i = tempI32('tfi'), off = tempI32('tfo')
|
|
914
1009
|
const out = allocPtr({ type: PTR.TYPED, aux,
|
|
@@ -993,7 +1088,7 @@ export default (ctx) => {
|
|
|
993
1088
|
// Runtime-dispatch typed index: checks ptr_type + aux to load with correct stride.
|
|
994
1089
|
// For TYPED views (aux bit 3), $off indirects through descriptor[4] to real data.
|
|
995
1090
|
ctx.core.stdlib['__typed_set_idx'] = `(func $__typed_set_idx (param $ptr i64) (param $i i32) (param $v f64) (result f64)
|
|
996
|
-
(local $off i32) (local $aux i32) (local $et i32) (local $bits i32)
|
|
1091
|
+
(local $off i32) (local $aux i32) (local $et i32) (local $bits i32) (local $vb i64)
|
|
997
1092
|
(local.set $aux (call $__ptr_aux (local.get $ptr)))
|
|
998
1093
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
999
1094
|
(if (i32.ne (i32.and (local.get $aux) (i32.const 8)) (i32.const 0))
|
|
@@ -1002,6 +1097,19 @@ export default (ctx) => {
|
|
|
1002
1097
|
(if (i32.and (local.get $aux) (i32.const ${TYPED_ELEM_BIGINT_FLAG}))
|
|
1003
1098
|
(then (i64.store (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))) (i64.reinterpret_f64 (local.get $v))))
|
|
1004
1099
|
(else
|
|
1100
|
+
;; ToNumber for NaN-boxed values (spec: typed element writes coerce).
|
|
1101
|
+
;; true/false/null atoms store 1/0/0; other boxes (undefined, string,
|
|
1102
|
+
;; object) store canonical NaN. Skipped on the BigInt arm above — raw
|
|
1103
|
+
;; bigint i64 bits legitimately look like NaN through the f64 param.
|
|
1104
|
+
(if (f64.ne (local.get $v) (local.get $v))
|
|
1105
|
+
(then
|
|
1106
|
+
(local.set $vb (i64.reinterpret_f64 (local.get $v)))
|
|
1107
|
+
(local.set $v (f64.const nan))
|
|
1108
|
+
(if (i64.eq (local.get $vb) (i64.const ${TRUE_NAN}))
|
|
1109
|
+
(then (local.set $v (f64.const 1))))
|
|
1110
|
+
(if (i32.or (i64.eq (local.get $vb) (i64.const ${FALSE_NAN}))
|
|
1111
|
+
(i64.eq (local.get $vb) (i64.const ${NULL_NAN})))
|
|
1112
|
+
(then (local.set $v (f64.const 0))))))
|
|
1005
1113
|
(if (i32.eq (local.get $et) (i32.const 7))
|
|
1006
1114
|
(then (f64.store (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))) (local.get $v)))
|
|
1007
1115
|
(else
|
|
@@ -1223,55 +1331,107 @@ export default (ctx) => {
|
|
|
1223
1331
|
}
|
|
1224
1332
|
ctx.core.emit['.typed:sort'] = (arr, fn) => emitTypedSort(emit(arr), fn)
|
|
1225
1333
|
|
|
1226
|
-
// Type-aware TypedArray read: arr[i]
|
|
1334
|
+
// Type-aware TypedArray read: arr[i]. The DIRECT unchecked load is gated on the
|
|
1335
|
+
// structural in-bounds proof (inBoundsArrIdx — the same canonical `for (i=C≥0;
|
|
1336
|
+
// i<arr.length; i++)` scan the generic ARRAY read uses), which keeps every proven
|
|
1337
|
+
// hot loop byte-identical (the vectorizer's shapes). An UNPROVEN index takes the
|
|
1338
|
+
// checked form: JS reads `undefined` past the end — the unchecked load read
|
|
1339
|
+
// ADJACENT HEAP or trapped past memory (the Root F silent-corruption class).
|
|
1340
|
+
// `i32.lt_u` folds the negative case in (a negative i32 is a huge u32). The
|
|
1341
|
+
// checked result is number|undefined, so it carries NO valKind tag.
|
|
1227
1342
|
ctx.core.emit['.typed:[]'] = (arr, i) => {
|
|
1228
1343
|
const r = resolveElem(arr)
|
|
1229
1344
|
if (r == null) return null // unknown type, fallback to generic
|
|
1230
1345
|
const { et, isView, isBigInt } = r
|
|
1346
|
+
const proven = typedIdxProven(arr, i)
|
|
1347
|
+
const loadOf = (off) => isBigInt ? ['f64.reinterpret_i64', ['i64.load', off]]
|
|
1348
|
+
: et === 7 ? ['f64.load', off]
|
|
1349
|
+
: et === 6 ? ['f64.promote_f32', ['f32.load', off]]
|
|
1350
|
+
: [(et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', [LOAD[et], off]]
|
|
1351
|
+
if (!proven) {
|
|
1352
|
+
// BRANCHLESS checked read: `select(load(in ? idx : 0), undefined, in)`. The
|
|
1353
|
+
// address clamp makes the load unconditionally safe (index 0 of the data
|
|
1354
|
+
// region is mapped arena even for a 0-length array — the loaded value is
|
|
1355
|
+
// select-discarded), and the branch-free shape keeps checked reads inside
|
|
1356
|
+
// straight-line kernel bodies the SIMD recognizers can still lift.
|
|
1357
|
+
// len inlines to one header load for a RESOLVED elem type (no call — the
|
|
1358
|
+
// SIMD recognizers require call-free kernel bodies): owned byteLen at
|
|
1359
|
+
// base-8, view byteLen at descriptor[0]; elemCount = byteLen >> shift.
|
|
1360
|
+
const ti = tempI32('tbi'), tin = tempI32('tbn')
|
|
1361
|
+
const lenIR = ['i32.shr_u',
|
|
1362
|
+
['i32.load', isView ? typedBase(emit(arr)) : ['i32.sub', typedBase(emit(arr)), ['i32.const', 8]]],
|
|
1363
|
+
['i32.const', SHIFT[et]]]
|
|
1364
|
+
const off = ['i32.add', typedDataAddr(emit(arr), isView),
|
|
1365
|
+
['i32.shl', ['select', ['local.get', `$${ti}`], ['i32.const', 0], ['local.get', `$${tin}`]],
|
|
1366
|
+
['i32.const', SHIFT[et]]]]
|
|
1367
|
+
return typed(['block', ['result', 'f64'],
|
|
1368
|
+
['local.set', `$${ti}`, idx(i)],
|
|
1369
|
+
['local.set', `$${tin}`, ['i32.lt_u', ['local.get', `$${ti}`], lenIR]],
|
|
1370
|
+
['select', loadOf(off), undefExpr(), ['local.get', `$${tin}`]]], 'f64')
|
|
1371
|
+
}
|
|
1231
1372
|
const objIR = emit(arr), vi = idx(i)
|
|
1232
1373
|
const off = ['i32.add', typedDataAddr(objIR, isView), ['i32.shl', vi, ['i32.const', SHIFT[et]]]]
|
|
1233
|
-
if (isBigInt) return typed(
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
return typed([(et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', [LOAD[et], off]], 'f64')
|
|
1374
|
+
if (isBigInt) return typed(loadOf(off), 'f64')
|
|
1375
|
+
// Non-bigint typed elements are plain NUMBERS — tag the load so numeric-arm
|
|
1376
|
+
// predicates (isNumArm: `+` dispatch numSide, ?:/?? canon) skip box guards.
|
|
1377
|
+
const t = typed(loadOf(off), 'f64'); t.valKind = VAL.NUMBER; return t
|
|
1238
1378
|
}
|
|
1239
1379
|
|
|
1240
|
-
// Type-aware TypedArray write: arr[i] = val
|
|
1380
|
+
// Type-aware TypedArray write: arr[i] = val. Same proof gate as the read: proven
|
|
1381
|
+
// indexes keep the direct unchecked store byte-identical; unproven ones evaluate
|
|
1382
|
+
// the RHS (its effects and the assignment's value are unconditional per spec),
|
|
1383
|
+
// then store only when `i u< len` — JS silently IGNORES out-of-bounds typed
|
|
1384
|
+
// writes, where the unchecked store corrupted adjacent heap (Root F).
|
|
1241
1385
|
ctx.core.emit['.typed:[]='] = (arr, i, val, void_ = false) => {
|
|
1242
1386
|
const r = resolveElem(arr)
|
|
1243
1387
|
if (r == null) return null
|
|
1244
1388
|
const { et, isView, isBigInt } = r
|
|
1245
|
-
const
|
|
1389
|
+
const proven = typedIdxProven(arr, i)
|
|
1390
|
+
const pre = []
|
|
1391
|
+
let vi
|
|
1392
|
+
if (proven) vi = idx(i)
|
|
1393
|
+
else {
|
|
1394
|
+
inc('__len')
|
|
1395
|
+
const ti = tempI32('tbi')
|
|
1396
|
+
pre.push(['local.set', `$${ti}`, idx(i)])
|
|
1397
|
+
vi = ['local.get', `$${ti}`]
|
|
1398
|
+
}
|
|
1399
|
+
// Wrap a store statement in the bounds guard on the unproven path. The value
|
|
1400
|
+
// temp is set OUTSIDE the guard (spec: RHS evaluates regardless).
|
|
1401
|
+
const guard = (store) => proven ? store
|
|
1402
|
+
: ['if', ['i32.lt_u', vi, ['i32.shr_u',
|
|
1403
|
+
['i32.load', isView ? typedBase(emit(arr)) : ['i32.sub', typedBase(emit(arr)), ['i32.const', 8]]],
|
|
1404
|
+
['i32.const', SHIFT[et]]]], ['then', store]]
|
|
1405
|
+
const objIR = emit(arr), valIR = emit(val)
|
|
1246
1406
|
const off = ['i32.add', typedDataAddr(objIR, isView), ['i32.shl', vi, ['i32.const', SHIFT[et]]]]
|
|
1247
1407
|
if (isBigInt) {
|
|
1248
1408
|
const vt = temp('tw')
|
|
1249
|
-
return typed(void_ ? ['block',
|
|
1409
|
+
return typed(void_ ? ['block', ...pre,
|
|
1250
1410
|
['local.set', `$${vt}`, asF64(valIR)],
|
|
1251
|
-
['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]]]
|
|
1252
|
-
: ['block', ['result', 'f64'],
|
|
1411
|
+
guard(['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]])]
|
|
1412
|
+
: ['block', ['result', 'f64'], ...pre,
|
|
1253
1413
|
['local.set', `$${vt}`, asF64(valIR)],
|
|
1254
|
-
['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]],
|
|
1414
|
+
guard(['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]]),
|
|
1255
1415
|
['local.get', `$${vt}`]], void_ ? 'void' : 'f64')
|
|
1256
1416
|
}
|
|
1257
1417
|
if (et === 7) {
|
|
1258
1418
|
const vt = temp('tw')
|
|
1259
|
-
return typed(void_ ? ['block',
|
|
1419
|
+
return typed(void_ ? ['block', ...pre,
|
|
1260
1420
|
['local.set', `$${vt}`, asF64(valIR)],
|
|
1261
|
-
['f64.store', off, ['local.get', `$${vt}`]]]
|
|
1262
|
-
: ['block', ['result', 'f64'],
|
|
1421
|
+
guard(['f64.store', off, ['local.get', `$${vt}`]])]
|
|
1422
|
+
: ['block', ['result', 'f64'], ...pre,
|
|
1263
1423
|
['local.set', `$${vt}`, asF64(valIR)],
|
|
1264
|
-
['f64.store', off, ['local.get', `$${vt}`]],
|
|
1424
|
+
guard(['f64.store', off, ['local.get', `$${vt}`]]),
|
|
1265
1425
|
['local.get', `$${vt}`]], void_ ? 'void' : 'f64') // Float64Array
|
|
1266
1426
|
}
|
|
1267
1427
|
if (et === 6) {
|
|
1268
1428
|
const vt = temp('tw')
|
|
1269
|
-
return typed(void_ ? ['block',
|
|
1429
|
+
return typed(void_ ? ['block', ...pre,
|
|
1270
1430
|
['local.set', `$${vt}`, asF64(valIR)],
|
|
1271
|
-
['f32.store', off, ['f32.demote_f64', ['local.get', `$${vt}`]]]]
|
|
1272
|
-
: ['block', ['result', 'f64'],
|
|
1431
|
+
guard(['f32.store', off, ['f32.demote_f64', ['local.get', `$${vt}`]]])]
|
|
1432
|
+
: ['block', ['result', 'f64'], ...pre,
|
|
1273
1433
|
['local.set', `$${vt}`, asF64(valIR)],
|
|
1274
|
-
['f32.store', off, ['f32.demote_f64', ['local.get', `$${vt}`]]],
|
|
1434
|
+
guard(['f32.store', off, ['f32.demote_f64', ['local.get', `$${vt}`]]]),
|
|
1275
1435
|
['local.get', `$${vt}`]], void_ ? 'void' : 'f64') // Float32Array
|
|
1276
1436
|
}
|
|
1277
1437
|
// Integer store: when the source is already i32-typed (bitwise ops, |0, known-i32 var) —
|
|
@@ -1285,18 +1445,19 @@ export default (ctx) => {
|
|
|
1285
1445
|
const i32Backed = valIR.type === 'i32' ||
|
|
1286
1446
|
(Array.isArray(valIR) && (valIR[0] === 'f64.convert_i32_s' || valIR[0] === 'f64.convert_i32_u'))
|
|
1287
1447
|
if (i32Backed) {
|
|
1288
|
-
const
|
|
1289
|
-
const cheap = Array.isArray(
|
|
1290
|
-
((
|
|
1291
|
-
(
|
|
1292
|
-
if (void_ && cheap) return typed([STORE[et], off,
|
|
1448
|
+
const vi32 = asI32(valIR)
|
|
1449
|
+
const cheap = Array.isArray(vi32) &&
|
|
1450
|
+
((vi32[0] === 'local.get' && typeof vi32[1] === 'string') ||
|
|
1451
|
+
(vi32[0] === 'i32.const' && (typeof vi32[1] === 'number' || typeof vi32[1] === 'string')))
|
|
1452
|
+
if (void_ && cheap && proven) return typed([STORE[et], off, vi32], 'void')
|
|
1453
|
+
if (void_ && cheap) return typed(['block', ...pre, guard([STORE[et], off, vi32])], 'void')
|
|
1293
1454
|
const v32 = tempI32('tw')
|
|
1294
|
-
return typed(void_ ? ['block',
|
|
1295
|
-
['local.set', `$${v32}`,
|
|
1296
|
-
[STORE[et], off, ['local.get', `$${v32}`]]]
|
|
1297
|
-
: ['block', ['result', 'f64'],
|
|
1298
|
-
['local.set', `$${v32}`,
|
|
1299
|
-
[STORE[et], off, ['local.get', `$${v32}`]],
|
|
1455
|
+
return typed(void_ ? ['block', ...pre,
|
|
1456
|
+
['local.set', `$${v32}`, vi32],
|
|
1457
|
+
guard([STORE[et], off, ['local.get', `$${v32}`]])]
|
|
1458
|
+
: ['block', ['result', 'f64'], ...pre,
|
|
1459
|
+
['local.set', `$${v32}`, vi32],
|
|
1460
|
+
guard([STORE[et], off, ['local.get', `$${v32}`]]),
|
|
1300
1461
|
[(et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', ['local.get', `$${v32}`]]], void_ ? 'void' : 'f64')
|
|
1301
1462
|
}
|
|
1302
1463
|
const vt = temp('tw')
|
|
@@ -1304,12 +1465,12 @@ export default (ctx) => {
|
|
|
1304
1465
|
['if', ['result', 'i64'], ['f64.lt', ['local.get', `$${vt}`], ['f64.const', 0]],
|
|
1305
1466
|
['then', ['i64.trunc_sat_f64_s', ['local.get', `$${vt}`]]],
|
|
1306
1467
|
['else', ['i64.trunc_sat_f64_u', ['local.get', `$${vt}`]]]]]
|
|
1307
|
-
return typed(void_ ? ['block',
|
|
1468
|
+
return typed(void_ ? ['block', ...pre,
|
|
1308
1469
|
['local.set', `$${vt}`, asF64(valIR)],
|
|
1309
|
-
[STORE[et], off, i32val]]
|
|
1310
|
-
: ['block', ['result', 'f64'],
|
|
1470
|
+
guard([STORE[et], off, i32val])]
|
|
1471
|
+
: ['block', ['result', 'f64'], ...pre,
|
|
1311
1472
|
['local.set', `$${vt}`, asF64(valIR)],
|
|
1312
|
-
[STORE[et], off, i32val],
|
|
1473
|
+
guard([STORE[et], off, i32val]),
|
|
1313
1474
|
['local.get', `$${vt}`]], void_ ? 'void' : 'f64')
|
|
1314
1475
|
}
|
|
1315
1476
|
|
|
@@ -1436,9 +1597,7 @@ export default (ctx) => {
|
|
|
1436
1597
|
// typedLoop(arr, bodyFn, opts?) emits the common shape every typed iteration
|
|
1437
1598
|
// method needs: resolve elemType from the receiver's tracked ctor, set up
|
|
1438
1599
|
// (ptr, len, i) locals, walk i in [0, len), per iteration load arr[i] as f64
|
|
1439
|
-
// and pass it to bodyFn. Returns the IR setup statements.
|
|
1440
|
-
// element type can't be resolved — callers fall back to the generic array
|
|
1441
|
-
// emitter.
|
|
1600
|
+
// and pass it to bodyFn. Returns the IR setup statements.
|
|
1442
1601
|
//
|
|
1443
1602
|
// bodyFn(loadElem, i, len, ptr, exitLabel) returns IR statements. loadElem is
|
|
1444
1603
|
// a function (called lazily so it isn't materialized for unused-item paths)
|
|
@@ -1447,30 +1606,63 @@ export default (ctx) => {
|
|
|
1447
1606
|
// resolution). `i`/`len`/`ptr` are i32 local-name strings.
|
|
1448
1607
|
const typedLoop = (arr, bodyFn) => {
|
|
1449
1608
|
const r = resolveElem(arr)
|
|
1450
|
-
if (!r) return null
|
|
1451
|
-
const { et, isView, isBigInt } = r
|
|
1452
|
-
if (isBigInt) return null // BigInt: defer to generic .map (returns BigInts via f64-bits NaN-box)
|
|
1453
|
-
const va = emit(arr)
|
|
1454
|
-
const len = tempI32('tll'), ptr = tempI32('tlp'), i = tempI32('tli')
|
|
1455
1609
|
const id = ctx.func.uniq++
|
|
1456
1610
|
const exit = `$brk${id}`
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1611
|
+
const len = tempI32('tll'), i = tempI32('tli')
|
|
1612
|
+
// Static fast path: concrete element kind (and non-BigInt-ness) proven at
|
|
1613
|
+
// compile time — direct-typed load, no per-element dispatch.
|
|
1614
|
+
if (r && !r.isBigInt) {
|
|
1615
|
+
const { et, isView } = r
|
|
1616
|
+
const va = emit(arr)
|
|
1617
|
+
const ptr = tempI32('tlp')
|
|
1618
|
+
inc('__len')
|
|
1619
|
+
const loadElem = () => {
|
|
1620
|
+
const off = ['i32.add', ['local.get', `$${ptr}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', SHIFT[et]]]]
|
|
1621
|
+
if (et === 7) return typed(['f64.load', off], 'f64')
|
|
1622
|
+
if (et === 6) return typed(['f64.promote_f32', ['f32.load', off]], 'f64')
|
|
1623
|
+
return typed([(et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', [LOAD[et], off]], 'f64')
|
|
1624
|
+
}
|
|
1625
|
+
const setup = [
|
|
1626
|
+
['local.set', `$${ptr}`, typedDataAddr(asF64(va), isView)],
|
|
1627
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', asF64(va)]]],
|
|
1628
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
1629
|
+
['block', exit, ['loop', `$loop${id}`,
|
|
1630
|
+
['br_if', exit, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
1631
|
+
...bodyFn(loadElem, i, len, ptr, exit),
|
|
1632
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
1633
|
+
['br', `$loop${id}`]]]]
|
|
1634
|
+
return { setup, ptr, len, i, exit, et, isView, loadElem }
|
|
1463
1635
|
}
|
|
1636
|
+
// Dynamic fallback: concrete element kind (or BigInt-ness) isn't provable
|
|
1637
|
+
// statically — `resolveElem` only tracks a receiver traced back to a bare
|
|
1638
|
+
// `new XArray(...)` binding, so a TYPED value that instead flowed through
|
|
1639
|
+
// an object field, a return value, or any other opaque path (still proven
|
|
1640
|
+
// *some* typed array — that's how it dispatched here at all — just not
|
|
1641
|
+
// WHICH one) has no ctor to key off. Read every element through
|
|
1642
|
+
// __typed_get_idx: the SAME runtime aux-tag dispatch .reverse/.sort/.fill/
|
|
1643
|
+
// .copyWithin already use UNCONDITIONALLY (module/core.js), correct for
|
|
1644
|
+
// any concrete kind including BigInt, one indirect call slower per
|
|
1645
|
+
// element than the static path above. This used to `return null`, which
|
|
1646
|
+
// every caller propagated as "unsupported" up to emitMethodCall — for a
|
|
1647
|
+
// receiver already proven VAL.TYPED that either crashed downstream (null
|
|
1648
|
+
// IR reaching a consumer) or, if a caller "fell back to the generic array
|
|
1649
|
+
// emitter" instead, would misread the typed array's packed native bytes
|
|
1650
|
+
// as 8-byte f64 slots (module/array.js's arrayLoop is ARRAY-only) —
|
|
1651
|
+
// silent corruption, not a fallback. This is the sound one.
|
|
1652
|
+
inc('__typed_get_idx', '__len')
|
|
1653
|
+
const av = temp('tla')
|
|
1654
|
+
const loadElem = () => typed(['call', '$__typed_get_idx',
|
|
1655
|
+
['i64.reinterpret_f64', ['local.get', `$${av}`]], ['local.get', `$${i}`]], 'f64')
|
|
1464
1656
|
const setup = [
|
|
1465
|
-
['local.set', `$${
|
|
1466
|
-
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64',
|
|
1657
|
+
['local.set', `$${av}`, asF64(emit(arr))],
|
|
1658
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${av}`]]]],
|
|
1467
1659
|
['local.set', `$${i}`, ['i32.const', 0]],
|
|
1468
1660
|
['block', exit, ['loop', `$loop${id}`,
|
|
1469
1661
|
['br_if', exit, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
1470
|
-
...bodyFn(loadElem, i, len,
|
|
1662
|
+
...bodyFn(loadElem, i, len, null, exit),
|
|
1471
1663
|
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
1472
1664
|
['br', `$loop${id}`]]]]
|
|
1473
|
-
return { setup, ptr, len, i, exit, et, isView, loadElem }
|
|
1665
|
+
return { setup, ptr: null, len, i, exit, et: null, isView: false, loadElem }
|
|
1474
1666
|
}
|
|
1475
1667
|
|
|
1476
1668
|
// === Typed iteration emitters ===
|
package/module/web.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* web — callable host web globals (`fetch(...)`), bound automatically.
|
|
3
|
+
*
|
|
4
|
+
* Under host:'js' a bare `fetch(url, opts?)` lowers to an `env.fetch` import
|
|
5
|
+
* that interop wires from `globalThis.fetch` with full marshalling — the
|
|
6
|
+
* returned thenable adopts into a jz promise, so `await fetch(url)` works and
|
|
7
|
+
* the Response crosses as an external handle (`.text()`/`.json()` dispatch
|
|
8
|
+
* host-side and are awaitable in turn). No import statement needed.
|
|
9
|
+
*
|
|
10
|
+
* Under host:'wasi' there is no JS host to bind — a warning is emitted and
|
|
11
|
+
* the env import is still declared, so an embedder MAY wire `env.fetch`
|
|
12
|
+
* itself; an unwired module fails at instantiation, not silently.
|
|
13
|
+
*
|
|
14
|
+
* @module web
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { typed, asI64, UNDEF_NAN } from '../src/ir.js'
|
|
18
|
+
import { emit, hostImport } from '../src/bridge.js'
|
|
19
|
+
import { warn } from '../src/ctx.js'
|
|
20
|
+
|
|
21
|
+
const ARITY = { fetch: 2 }
|
|
22
|
+
|
|
23
|
+
export default (ctx) => {
|
|
24
|
+
for (const [name, arity] of Object.entries(ARITY)) {
|
|
25
|
+
ctx.core.emit[name] = (...args) => {
|
|
26
|
+
if (ctx.transform.host === 'wasi')
|
|
27
|
+
warn('host-global', `\`${name}\` binds from the JS host — under host:'wasi' wire env.${name} yourself or instantiation will fail`, {})
|
|
28
|
+
hostImport('env', name, ['func', `$__env_${name}`,
|
|
29
|
+
...Array.from({ length: arity }, () => ['param', 'i64']), ['result', 'i64']])
|
|
30
|
+
const ir = [`call`, `$__env_${name}`]
|
|
31
|
+
for (let i = 0; i < arity; i++)
|
|
32
|
+
ir.push(args[i] != null ? asI64(emit(args[i])) : ['i64.const', UNDEF_NAN])
|
|
33
|
+
return typed(['f64.reinterpret_i64', ir], 'f64')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jz",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Ahead-of-time compiler for the numeric core of JavaScript (DSP, audio, math, parsers) to lean GC-free WASM — valid jz is valid JS, no type annotations, no runtime.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"test:bench": "node test/bench.js",
|
|
47
47
|
"test:fuzz": "node test/fuzz.js --count=5000",
|
|
48
48
|
"test:ratchet": "node test/perf-ratchet.js",
|
|
49
|
-
"test:self": "node test/selfhost.js",
|
|
49
|
+
"test:self": "node test/selfhost.js && node test/selfhost-perf.js",
|
|
50
50
|
"test:262": "node test/test262.js",
|
|
51
51
|
"test:262:builtins": "node test/test262-builtins.js",
|
|
52
52
|
"test:all": "npm run test:matrix && npm run test:262 && npm run test:262:builtins && npm run test:bench && npm run test:self",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"audit:as": "node scripts/audit-assemblyscript.mjs",
|
|
61
61
|
"build": "node scripts/build-dist.mjs",
|
|
62
62
|
"build:examples": "node examples/build.mjs all",
|
|
63
|
-
"prepublishOnly": "npm test && npm run
|
|
63
|
+
"prepublishOnly": "npm test && npm run test:self",
|
|
64
64
|
"prepare": "npm run build",
|
|
65
65
|
"audit:fixpoint": "node scripts/audit-fixpoint.mjs"
|
|
66
66
|
},
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"author": "Dmitry Iv",
|
|
76
76
|
"license": "MIT",
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"subscript": "^10.
|
|
79
|
-
"watr": "^5.
|
|
78
|
+
"subscript": "^10.7.0",
|
|
79
|
+
"watr": "^5.4.2"
|
|
80
80
|
},
|
|
81
81
|
"keywords": [
|
|
82
82
|
"javascript",
|