jz 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -26
- package/cli.js +53 -8
- package/index.js +14 -1
- package/module/array.js +66 -9
- package/module/collection.js +68 -35
- package/module/core.js +65 -19
- package/module/date.js +5 -0
- package/module/function.js +2 -1
- package/module/json.js +66 -8
- package/module/number.js +127 -5
- package/module/object.js +88 -1
- package/module/string.js +3 -2
- package/module/typedarray.js +7 -1
- package/package.json +3 -3
- package/src/analyze.js +16 -4
- package/src/assemble.js +544 -0
- package/src/ast.js +160 -0
- package/src/auto-config.js +120 -0
- package/src/autoload.js +4 -1
- package/src/compile.js +22 -620
- package/src/ctx.js +33 -5
- package/src/emit.js +73 -165
- package/src/host.js +12 -0
- package/src/ir.js +13 -0
- package/src/jzify.js +348 -9
- package/src/narrow.js +2 -1
- package/src/optimize.js +298 -24
- package/src/plan.js +220 -34
- package/src/prepare.js +22 -2
- package/src/vectorize.js +3 -12
package/module/number.js
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { typed, asF64, asI32, asI64, toNumF64, NULL_NAN, UNDEF_NAN, temp, tempI32, tempI64 } from '../src/ir.js'
|
|
13
|
-
import { emit
|
|
13
|
+
import { emit } from '../src/emit.js'
|
|
14
|
+
import { isReassigned } from '../src/ast.js'
|
|
14
15
|
import { valTypeOf, VAL } from '../src/analyze.js'
|
|
15
16
|
import { inc, PTR } from '../src/ctx.js'
|
|
16
17
|
|
|
@@ -22,6 +23,7 @@ export default (ctx) => {
|
|
|
22
23
|
__to_num: ['__char_at', '__str_byteLen', '__pow10', '__to_str'],
|
|
23
24
|
__to_bigint: ['__char_at', '__str_byteLen'],
|
|
24
25
|
__parseInt: ['__char_at', '__str_byteLen'],
|
|
26
|
+
__parseFloat: ['__char_at', '__str_byteLen', '__pow10', '__to_str'],
|
|
25
27
|
})
|
|
26
28
|
|
|
27
29
|
|
|
@@ -346,7 +348,6 @@ export default (ctx) => {
|
|
|
346
348
|
['f64.eq', ['local.get', `$${t}`], ['f64.trunc', ['local.get', `$${t}`]]]], 'i32')
|
|
347
349
|
}
|
|
348
350
|
|
|
349
|
-
// parseInt(str, radix) — parse string to integer
|
|
350
351
|
ctx.core.stdlib['__parseInt'] = `(func $__parseInt (param $str i64) (param $radix i32) (result f64)
|
|
351
352
|
(local $off i32) (local $len i32) (local $i i32) (local $c i32) (local $neg i32)
|
|
352
353
|
(local $result f64) (local $digit i32) (local $seen i32) (local $f f64)
|
|
@@ -592,14 +593,135 @@ export default (ctx) => {
|
|
|
592
593
|
(then (i64.sub (i64.const 0) (local.get $result)))
|
|
593
594
|
(else (local.get $result)))))`
|
|
594
595
|
|
|
596
|
+
ctx.core.stdlib['__parseFloat'] = `(func $__parseFloat (param $v i64) (result f64)
|
|
597
|
+
(local $t i32) (local $len i32) (local $i i32) (local $c i32) (local $neg i32)
|
|
598
|
+
(local $seen i32) (local $exp i32) (local $expNeg i32) (local $expDigits i32)
|
|
599
|
+
(local $dot i32) (local $sigDigits i32) (local $decExp i32) (local $dropped i32) (local $round i32)
|
|
600
|
+
(local $result f64) (local $f f64)
|
|
601
|
+
(local.set $f (f64.reinterpret_i64 (local.get $v)))
|
|
602
|
+
(if (f64.eq (local.get $f) (local.get $f)) (then (return (local.get $f))))
|
|
603
|
+
(local.set $t (call $__ptr_type (local.get $v)))
|
|
604
|
+
;; parseFloat first applies ToString, then parses the longest decimal prefix.
|
|
605
|
+
;; Unlike Number(), empty strings and non-decimal prefixes produce NaN/0
|
|
606
|
+
;; according to the consumed prefix rather than whole-string numeric coercion.
|
|
607
|
+
(if (i32.ne (local.get $t) (i32.const ${PTR.STRING}))
|
|
608
|
+
(then
|
|
609
|
+
(local.set $v (call $__to_str (local.get $v)))
|
|
610
|
+
(local.set $t (call $__ptr_type (local.get $v)))
|
|
611
|
+
(if (i32.ne (local.get $t) (i32.const ${PTR.STRING}))
|
|
612
|
+
(then (return (f64.const nan))))))
|
|
613
|
+
(local.set $len (call $__str_byteLen (local.get $v)))
|
|
614
|
+
;; Skip leading whitespace.
|
|
615
|
+
(block $ws (loop $wsl
|
|
616
|
+
(br_if $ws (i32.ge_s (local.get $i) (local.get $len)))
|
|
617
|
+
(br_if $ws (i32.gt_s (call $__char_at (local.get $v) (local.get $i)) (i32.const 32)))
|
|
618
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
619
|
+
(br $wsl)))
|
|
620
|
+
;; Sign.
|
|
621
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
622
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 45)))
|
|
623
|
+
(then (local.set $neg (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
624
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
625
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 43)))
|
|
626
|
+
(then (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
627
|
+
;; Decimal significand. Keep 17 significant decimal digits, track the
|
|
628
|
+
;; base-10 exponent for skipped digits, and round once before pow10 scaling.
|
|
629
|
+
(block $numDone (loop $numLoop
|
|
630
|
+
(br_if $numDone (i32.ge_s (local.get $i) (local.get $len)))
|
|
631
|
+
(local.set $c (call $__char_at (local.get $v) (local.get $i)))
|
|
632
|
+
(if (i32.and (i32.eq (local.get $c) (i32.const 46)) (i32.eqz (local.get $dot)))
|
|
633
|
+
(then
|
|
634
|
+
(local.set $dot (i32.const 1))
|
|
635
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
636
|
+
(br $numLoop)))
|
|
637
|
+
(br_if $numDone
|
|
638
|
+
(i32.or
|
|
639
|
+
(i32.lt_s (local.get $c) (i32.const 48))
|
|
640
|
+
(i32.gt_s (local.get $c) (i32.const 57))))
|
|
641
|
+
(local.set $seen (i32.const 1))
|
|
642
|
+
(local.set $c (i32.sub (local.get $c) (i32.const 48)))
|
|
643
|
+
(if (i32.and (i32.eqz (local.get $sigDigits)) (i32.eqz (local.get $c)))
|
|
644
|
+
(then
|
|
645
|
+
(if (local.get $dot) (then (local.set $decExp (i32.sub (local.get $decExp) (i32.const 1)))))
|
|
646
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
647
|
+
(br $numLoop)))
|
|
648
|
+
(if (i32.lt_s (local.get $sigDigits)
|
|
649
|
+
(if (result i32) (local.get $dot) (then (i32.const 16)) (else (i32.const 17))))
|
|
650
|
+
(then
|
|
651
|
+
(local.set $result
|
|
652
|
+
(f64.add
|
|
653
|
+
(f64.mul (local.get $result) (f64.const 10))
|
|
654
|
+
(f64.convert_i32_s (local.get $c))))
|
|
655
|
+
(local.set $sigDigits (i32.add (local.get $sigDigits) (i32.const 1)))
|
|
656
|
+
(if (local.get $dot) (then (local.set $decExp (i32.sub (local.get $decExp) (i32.const 1))))))
|
|
657
|
+
(else
|
|
658
|
+
(if (i32.eqz (local.get $dropped))
|
|
659
|
+
(then (if (i32.ge_s (local.get $c) (i32.const 5)) (then (local.set $round (i32.const 1))))))
|
|
660
|
+
(local.set $dropped (i32.const 1))
|
|
661
|
+
(if (i32.eqz (local.get $dot)) (then (local.set $decExp (i32.add (local.get $decExp) (i32.const 1)))))))
|
|
662
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
663
|
+
(br $numLoop)))
|
|
664
|
+
(if (i32.eqz (local.get $seen)) (then (return (f64.const nan))))
|
|
665
|
+
(if (local.get $round) (then (local.set $result (f64.add (local.get $result) (f64.const 1)))))
|
|
666
|
+
;; Scientific notation.
|
|
667
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
668
|
+
(i32.or
|
|
669
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 101))
|
|
670
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 69))))
|
|
671
|
+
(then
|
|
672
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
673
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
674
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 45)))
|
|
675
|
+
(then (local.set $expNeg (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
676
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
677
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 43)))
|
|
678
|
+
(then (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
679
|
+
(block $expDone (loop $expLoop
|
|
680
|
+
(br_if $expDone (i32.ge_s (local.get $i) (local.get $len)))
|
|
681
|
+
(local.set $c (call $__char_at (local.get $v) (local.get $i)))
|
|
682
|
+
(br_if $expDone
|
|
683
|
+
(i32.or
|
|
684
|
+
(i32.lt_s (local.get $c) (i32.const 48))
|
|
685
|
+
(i32.gt_s (local.get $c) (i32.const 57))))
|
|
686
|
+
(local.set $exp
|
|
687
|
+
(i32.add
|
|
688
|
+
(i32.mul (local.get $exp) (i32.const 10))
|
|
689
|
+
(i32.sub (local.get $c) (i32.const 48))))
|
|
690
|
+
(local.set $expDigits (i32.add (local.get $expDigits) (i32.const 1)))
|
|
691
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
692
|
+
(br $expLoop)))
|
|
693
|
+
(if (local.get $expDigits)
|
|
694
|
+
(then
|
|
695
|
+
(if (local.get $expNeg)
|
|
696
|
+
(then (local.set $decExp (i32.sub (local.get $decExp) (local.get $exp))))
|
|
697
|
+
(else (local.set $decExp (i32.add (local.get $decExp) (local.get $exp)))))))))
|
|
698
|
+
(if (i32.gt_s (local.get $decExp) (i32.const 0))
|
|
699
|
+
(then (local.set $result (f64.mul (local.get $result) (call $__pow10 (local.get $decExp))))))
|
|
700
|
+
(if (i32.lt_s (local.get $decExp) (i32.const 0))
|
|
701
|
+
(then (local.set $result (f64.div (local.get $result) (call $__pow10 (i32.sub (i32.const 0) (local.get $decExp)))))))
|
|
702
|
+
(if (result f64) (local.get $neg) (then (f64.neg (local.get $result))) (else (local.get $result))))`
|
|
703
|
+
|
|
595
704
|
ctx.core.emit['Number.parseInt'] = (x, radix) => {
|
|
596
|
-
|
|
705
|
+
needParseInt()
|
|
597
706
|
return typed(['call', '$__parseInt', asI64(emit(x)), radix ? asI32(emit(radix)) : ['i32.const', 0]], 'f64')
|
|
598
707
|
}
|
|
599
708
|
ctx.core.emit['parseInt'] = ctx.core.emit['Number.parseInt']
|
|
709
|
+
const addImportOnce = (ctx, mod, name, fn) => {
|
|
710
|
+
if (ctx.module.imports.some(i => i[1] === `"${mod}"` && i[2] === `"${name}"`)) return
|
|
711
|
+
ctx.module.imports.push(['import', `"${mod}"`, `"${name}"`, fn])
|
|
712
|
+
}
|
|
713
|
+
const needParseFloat = () => addImportOnce(ctx, 'env', 'parseFloat',
|
|
714
|
+
['func', '$__parseFloat', ['param', 'i64'], ['result', 'f64']])
|
|
715
|
+
const needParseInt = () => addImportOnce(ctx, 'env', 'parseInt',
|
|
716
|
+
['func', '$__parseInt', ['param', 'i64'], ['param', 'i32'], ['result', 'f64']])
|
|
717
|
+
|
|
600
718
|
ctx.core.emit['Number.parseFloat'] = (x) => {
|
|
601
|
-
|
|
602
|
-
|
|
719
|
+
if (ctx.transform.host === 'wasi') {
|
|
720
|
+
inc('__parseFloat')
|
|
721
|
+
return typed(['call', '$__parseFloat', asI64(emit(x))], 'f64')
|
|
722
|
+
}
|
|
723
|
+
needParseFloat()
|
|
724
|
+
return typed(['call', '$__parseFloat', asI64(emit(x))], 'f64')
|
|
603
725
|
}
|
|
604
726
|
ctx.core.emit['parseFloat'] = ctx.core.emit['Number.parseFloat']
|
|
605
727
|
|
package/module/object.js
CHANGED
|
@@ -134,8 +134,9 @@ export default (ctx) => {
|
|
|
134
134
|
ctx.core.emit[`.${VAL.CLOSURE}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
135
135
|
|
|
136
136
|
ctx.core.emit['Object.values'] = (obj) => {
|
|
137
|
+
if (isHashTyped(obj)) return emitHashValues(obj)
|
|
137
138
|
const schema = resolveSchema(obj)
|
|
138
|
-
if (!schema)
|
|
139
|
+
if (!schema) return emitRuntimeValues(obj)
|
|
139
140
|
const va = asF64(emit(obj))
|
|
140
141
|
const n = schema.length
|
|
141
142
|
const t = temp('ov'), base = tempI32('vb')
|
|
@@ -225,6 +226,10 @@ export default (ctx) => {
|
|
|
225
226
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
226
227
|
}
|
|
227
228
|
|
|
229
|
+
ctx.core.emit['Object.defineProperty'] = () => {
|
|
230
|
+
err('Object.defineProperty descriptor semantics are outside jz scope; jzify only folds static bundler export helpers')
|
|
231
|
+
}
|
|
232
|
+
|
|
228
233
|
// Object.fromEntries(arr) → creates HASH from array of [key, value] pairs
|
|
229
234
|
ctx.core.emit['Object.fromEntries'] = (arr) => {
|
|
230
235
|
inc('__hash_new', '__hash_set')
|
|
@@ -450,6 +455,13 @@ function emitHashKeys(obj) {
|
|
|
450
455
|
hashKeysFromTemp(t)], 'f64')
|
|
451
456
|
}
|
|
452
457
|
|
|
458
|
+
function emitHashValues(obj) {
|
|
459
|
+
const t = temp('hv')
|
|
460
|
+
return typed(['block', ['result', 'f64'],
|
|
461
|
+
['local.set', `$${t}`, asF64(emit(obj))],
|
|
462
|
+
hashValuesFromTemp(t)], 'f64')
|
|
463
|
+
}
|
|
464
|
+
|
|
453
465
|
// Inline body of the HASH walk against an already-bound f64 local. Shared by
|
|
454
466
|
// the static-HASH path and the runtime-dispatch path so both produce the same
|
|
455
467
|
// IR shape from the same source — only difference is whether they enter from
|
|
@@ -481,6 +493,33 @@ function hashKeysFromTemp(t) {
|
|
|
481
493
|
out.ptr]
|
|
482
494
|
}
|
|
483
495
|
|
|
496
|
+
function hashValuesFromTemp(t) {
|
|
497
|
+
inc('__ptr_offset', '__cap', '__len')
|
|
498
|
+
const off = tempI32('hvo'), cap = tempI32('hvc'), n = tempI32('hvn')
|
|
499
|
+
const i = tempI32('hvi'), o = tempI32('hvj'), slot = tempI32('hvs')
|
|
500
|
+
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${n}`], tag: 'hva' })
|
|
501
|
+
const id = ctx.func.uniq++
|
|
502
|
+
return ['block', ['result', 'f64'],
|
|
503
|
+
['local.set', `$${n}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
504
|
+
out.init,
|
|
505
|
+
['local.set', `$${off}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
506
|
+
['local.set', `$${cap}`, ['call', '$__cap', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
507
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
508
|
+
['local.set', `$${o}`, ['i32.const', 0]],
|
|
509
|
+
['block', `$vbrk${id}`, ['loop', `$vloop${id}`,
|
|
510
|
+
['br_if', `$vbrk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${cap}`]]],
|
|
511
|
+
['local.set', `$${slot}`, ['i32.add', ['local.get', `$${off}`],
|
|
512
|
+
['i32.mul', ['local.get', `$${i}`], ['i32.const', 24]]]],
|
|
513
|
+
['if', ['f64.ne', ['f64.load', ['local.get', `$${slot}`]], ['f64.const', 0]],
|
|
514
|
+
['then',
|
|
515
|
+
elemStore(out.local, o,
|
|
516
|
+
['f64.load', ['i32.add', ['local.get', `$${slot}`], ['i32.const', 16]]]),
|
|
517
|
+
['local.set', `$${o}`, ['i32.add', ['local.get', `$${o}`], ['i32.const', 1]]]]],
|
|
518
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
519
|
+
['br', `$vloop${id}`]]],
|
|
520
|
+
out.ptr]
|
|
521
|
+
}
|
|
522
|
+
|
|
484
523
|
// Type-unknown receiver: bind the value, branch on ptr-type. HASH walks the
|
|
485
524
|
// probe table; OBJECT loads the schema's key array (registered statically at
|
|
486
525
|
// compile time or lazily at runtime by JSON.parse via __jp_schema_get); other
|
|
@@ -509,6 +548,26 @@ function emitRuntimeKeys(obj) {
|
|
|
509
548
|
['else', ['block', ['result', 'f64'], empty.init, empty.ptr]]]]]], 'f64')
|
|
510
549
|
}
|
|
511
550
|
|
|
551
|
+
function emitRuntimeValues(obj) {
|
|
552
|
+
inc('__ptr_type')
|
|
553
|
+
if (!ctx.scope.globals.has('__schema_tbl'))
|
|
554
|
+
ctx.scope.globals.set('__schema_tbl', '(global $__schema_tbl (mut i32) (i32.const 0))')
|
|
555
|
+
const t = temp('rv'), tt = tempI32('rvt')
|
|
556
|
+
const empty = allocPtr({ type: PTR.ARRAY, len: 0, tag: 'rve' })
|
|
557
|
+
return typed(['block', ['result', 'f64'],
|
|
558
|
+
['local.set', `$${t}`, asF64(emit(obj))],
|
|
559
|
+
['local.set', `$${tt}`, ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
560
|
+
['if', ['result', 'f64'],
|
|
561
|
+
['i32.eq', ['local.get', `$${tt}`], ['i32.const', PTR.HASH]],
|
|
562
|
+
['then', hashValuesFromTemp(t)],
|
|
563
|
+
['else', ['if', ['result', 'f64'],
|
|
564
|
+
['i32.and',
|
|
565
|
+
['i32.eq', ['local.get', `$${tt}`], ['i32.const', PTR.OBJECT]],
|
|
566
|
+
['i32.ne', ['global.get', '$__schema_tbl'], ['i32.const', 0]]],
|
|
567
|
+
['then', objectValuesFromTemp(t)],
|
|
568
|
+
['else', ['block', ['result', 'f64'], empty.init, empty.ptr]]]]]], 'f64')
|
|
569
|
+
}
|
|
570
|
+
|
|
512
571
|
// Schema-keyed Object.keys: copy the schema's keys array (a jz Array of
|
|
513
572
|
// STRINGs registered in __schema_tbl[sid]) into a fresh ARRAY so callers can
|
|
514
573
|
// mutate without aliasing the schema substrate.
|
|
@@ -537,3 +596,31 @@ function objectKeysFromTemp(t) {
|
|
|
537
596
|
['br', `$kloop${id}`]]],
|
|
538
597
|
mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${out}`])]
|
|
539
598
|
}
|
|
599
|
+
|
|
600
|
+
function objectValuesFromTemp(t) {
|
|
601
|
+
inc('__alloc_hdr', '__ptr_offset')
|
|
602
|
+
const sid = tempI32('ovs'), src = tempI32('ovsrc'), n = tempI32('ovn')
|
|
603
|
+
const base = tempI32('ovbase'), out = tempI32('ovo'), i = tempI32('ovi')
|
|
604
|
+
const id = ctx.func.uniq++
|
|
605
|
+
return ['block', ['result', 'f64'],
|
|
606
|
+
['local.set', `$${sid}`, ['i32.wrap_i64', ['i64.and',
|
|
607
|
+
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const', LAYOUT.AUX_SHIFT]],
|
|
608
|
+
['i64.const', LAYOUT.AUX_MASK]]]],
|
|
609
|
+
['local.set', `$${src}`, ['i32.wrap_i64', ['i64.and',
|
|
610
|
+
['i64.load', ['i32.add', ['global.get', '$__schema_tbl'], ['i32.shl', ['local.get', `$${sid}`], ['i32.const', 3]]]],
|
|
611
|
+
['i64.const', LAYOUT.OFFSET_MASK]]]],
|
|
612
|
+
['local.set', `$${n}`, ['i32.load', ['i32.sub', ['local.get', `$${src}`], ['i32.const', 8]]]],
|
|
613
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
614
|
+
['local.set', `$${out}`, ['call', '$__alloc_hdr',
|
|
615
|
+
['local.get', `$${n}`], ['local.get', `$${n}`], ['i32.const', 8]]],
|
|
616
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
617
|
+
['block', `$ovbrk${id}`, ['loop', `$ovloop${id}`,
|
|
618
|
+
['br_if', `$ovbrk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${n}`]]],
|
|
619
|
+
['f64.store',
|
|
620
|
+
['i32.add', ['local.get', `$${out}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]],
|
|
621
|
+
['f64.load',
|
|
622
|
+
['i32.add', ['local.get', `$${base}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]],
|
|
623
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
624
|
+
['br', `$ovloop${id}`]]],
|
|
625
|
+
mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${out}`])]
|
|
626
|
+
}
|
package/module/string.js
CHANGED
|
@@ -883,11 +883,12 @@ export default (ctx) => {
|
|
|
883
883
|
|
|
884
884
|
ctx.core.emit['.string:slice'] = (str, start, end) => {
|
|
885
885
|
inc('__str_slice')
|
|
886
|
-
|
|
886
|
+
const startIR = start == null ? ['i32.const', 0] : asI32(emit(start))
|
|
887
|
+
if (end != null) return typed(['call', '$__str_slice', asI64(emit(str)), startIR, asI32(emit(end))], 'f64')
|
|
887
888
|
const t = temp('t')
|
|
888
889
|
return typed(['block', ['result', 'f64'],
|
|
889
890
|
['local.set', `$${t}`, asF64(emit(str))],
|
|
890
|
-
['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${t}`]],
|
|
891
|
+
['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${t}`]], startIR,
|
|
891
892
|
['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]], 'f64')
|
|
892
893
|
}
|
|
893
894
|
|
package/module/typedarray.js
CHANGED
|
@@ -587,8 +587,14 @@ export default (ctx) => {
|
|
|
587
587
|
}
|
|
588
588
|
return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
|
|
589
589
|
(local $off i32) (local $et i32) (local $len i32) (local $aux i32)
|
|
590
|
-
(local.set $aux (call $__ptr_aux (local.get $ptr)))
|
|
591
590
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
591
|
+
;; ARRAY fast path: __ptr_offset already followed any forwarding — read header len + f64.load, no $__len call.
|
|
592
|
+
(if (i32.and (i32.eq (call $__ptr_type (local.get $ptr)) (i32.const ${PTR.ARRAY})) (i32.ge_u (local.get $off) (i32.const 8)))
|
|
593
|
+
(then (return (if (result f64)
|
|
594
|
+
(i32.and (i32.ge_s (local.get $i) (i32.const 0)) (i32.lt_u (local.get $i) (i32.load (i32.sub (local.get $off) (i32.const 8)))))
|
|
595
|
+
(then (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
596
|
+
(else (f64.const nan:${UNDEF_NAN}))))))
|
|
597
|
+
(local.set $aux (call $__ptr_aux (local.get $ptr)))
|
|
592
598
|
(if
|
|
593
599
|
(i32.and
|
|
594
600
|
(i32.eq (call $__ptr_type (local.get $ptr)) (i32.const ${PTR.TYPED}))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jz",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Modern functional JS compiling to WASM",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"author": "Dmitry Iv",
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"subscript": "^10.4.
|
|
42
|
-
"watr": "^4.5.
|
|
41
|
+
"subscript": "^10.4.7",
|
|
42
|
+
"watr": "^4.5.3"
|
|
43
43
|
},
|
|
44
44
|
"keywords": [
|
|
45
45
|
"javascript",
|
package/src/analyze.js
CHANGED
|
@@ -33,11 +33,27 @@ export const STMT_OPS = new Set([';', 'let', 'const', 'return', 'if', 'for', 'fo
|
|
|
33
33
|
'=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '>>=', '<<=', '>>>=', '||=', '&&=', '??=',
|
|
34
34
|
'throw', 'try', 'catch', 'finally', '++', '--', '()'])
|
|
35
35
|
|
|
36
|
+
/** Assignment operators — shared across analyze, plan, emit. */
|
|
37
|
+
export const ASSIGN_OPS = new Set(['=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '>>=', '<<=', '>>>=', '||=', '&&=', '??='])
|
|
38
|
+
|
|
36
39
|
/** Distinguish a function block body `{ ... }` from an expression-bodied object literal `({a:1})`.
|
|
37
40
|
* Both share the `'{}'` op tag; blocks have a non-`':'` first child (object literals start with key:val pairs). */
|
|
38
41
|
export const isBlockBody = (body) =>
|
|
39
42
|
Array.isArray(body) && body[0] === '{}' && body[1]?.[0] !== ':'
|
|
40
43
|
|
|
44
|
+
/** Extract integer value from AST literal node. Returns null if not a 32-bit integer. */
|
|
45
|
+
export function intLiteralValue(expr) {
|
|
46
|
+
let v = null
|
|
47
|
+
if (typeof expr === 'number') v = expr
|
|
48
|
+
else if (Array.isArray(expr) && expr[0] == null && typeof expr[1] === 'number') v = expr[1]
|
|
49
|
+
else if (Array.isArray(expr) && expr[0] === 'u-' && typeof expr[1] === 'number') v = -expr[1]
|
|
50
|
+
else if (typeof expr === 'string') v = repOf(expr)?.intConst ?? ctx.scope.constInts?.get(expr) ?? null
|
|
51
|
+
return v != null && Number.isInteger(v) && v >= -2147483648 && v <= 2147483647 ? v : null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Non-negative integer literal — used for string/typed-array index bounds. */
|
|
55
|
+
export const nonNegIntLiteral = (node) => { const n = intLiteralValue(node); return n != null && n >= 0 ? n : null }
|
|
56
|
+
|
|
41
57
|
/** Collect all `return X` expressions (X != null) from a function body, skipping nested arrow funcs.
|
|
42
58
|
* Pushes into `out`. Non-returning paths are silently skipped — pair with `alwaysReturns` if total
|
|
43
59
|
* coverage matters, or with `hasBareReturn` to detect `return;` (undef result). */
|
|
@@ -1892,8 +1908,6 @@ export function analyzePtrUnboxable(body, locals, boxed) {
|
|
|
1892
1908
|
for (const a of args) collect(a)
|
|
1893
1909
|
}
|
|
1894
1910
|
|
|
1895
|
-
const ASSIGN_OPS = new Set(['=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=',
|
|
1896
|
-
'<<=', '>>=', '>>>=', '||=', '&&=', '??='])
|
|
1897
1911
|
const NULL_CMP_OPS = new Set(['==', '!=', '===', '!=='])
|
|
1898
1912
|
|
|
1899
1913
|
function check(node) {
|
|
@@ -2035,8 +2049,6 @@ export function findFreeVars(node, bound, free, scope) {
|
|
|
2035
2049
|
for (const a of args) findFreeVars(a, bound, free, scope)
|
|
2036
2050
|
}
|
|
2037
2051
|
|
|
2038
|
-
const ASSIGN_OPS = new Set(['=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '>>=', '<<=', '>>>=', '||=', '&&=', '??='])
|
|
2039
|
-
|
|
2040
2052
|
/** Check if any of the given variable names are assigned anywhere in the AST. */
|
|
2041
2053
|
export function findMutations(node, names, mutated) {
|
|
2042
2054
|
if (node == null || typeof node !== 'object' || !Array.isArray(node)) return
|