jz 0.2.1 → 0.3.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 +6 -25
- package/cli.js +19 -2
- 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 +306 -7
- 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/core.js
CHANGED
|
@@ -173,16 +173,25 @@ export default (ctx) => {
|
|
|
173
173
|
} else {
|
|
174
174
|
// Own memory: heap offset in a global, auto-grow when needed
|
|
175
175
|
ctx.scope.globals.set('__heap', '(global $__heap (mut i32) (i32.const 1024))')
|
|
176
|
+
// Bump allocator with geometric growth. Growing one page at a time turns a
|
|
177
|
+
// long-running embedding (e.g. watr called thousands of times) into O(n²) —
|
|
178
|
+
// each memory.grow may relocate and copy the whole heap. So when we must
|
|
179
|
+
// grow, request at least the current size (≥2× total) in one shot; only on
|
|
180
|
+
// hitting the declared maximum do we fall back to the bare minimum.
|
|
176
181
|
ctx.core.stdlib['__alloc'] = `(func $__alloc (param $bytes i32) (result i32)
|
|
177
|
-
(local $ptr i32) (local $next i32)
|
|
182
|
+
(local $ptr i32) (local $next i32) (local $cur i32) (local $need i32)
|
|
178
183
|
(local.set $ptr (global.get $__heap))
|
|
179
184
|
;; Align next allocation to 8 bytes
|
|
180
185
|
(local.set $next (i32.and (i32.add (i32.add (local.get $ptr) (local.get $bytes)) (i32.const 7)) (i32.const -8)))
|
|
181
|
-
|
|
182
|
-
(if (i32.gt_u (local.get $next) (
|
|
183
|
-
(then
|
|
184
|
-
(i32.shr_u (i32.add (i32.sub (local.get $next) (
|
|
185
|
-
(i32.
|
|
186
|
+
(local.set $cur (i32.shl (memory.size) (i32.const 16)))
|
|
187
|
+
(if (i32.gt_u (local.get $next) (local.get $cur))
|
|
188
|
+
(then
|
|
189
|
+
(local.set $need (i32.shr_u (i32.add (i32.sub (local.get $next) (local.get $cur)) (i32.const 65535)) (i32.const 16)))
|
|
190
|
+
(if (i32.lt_u (local.get $need) (memory.size)) (then (local.set $need (memory.size))))
|
|
191
|
+
(if (i32.eq (memory.grow (local.get $need)) (i32.const -1))
|
|
192
|
+
(then (if (i32.eq (memory.grow
|
|
193
|
+
(i32.shr_u (i32.add (i32.sub (local.get $next) (local.get $cur)) (i32.const 65535)) (i32.const 16)))
|
|
194
|
+
(i32.const -1)) (then (unreachable)))))))
|
|
186
195
|
(global.set $__heap (local.get $next))
|
|
187
196
|
(local.get $ptr))`
|
|
188
197
|
ctx.core.stdlib['__clear'] = `(func $__clear
|
|
@@ -404,9 +413,15 @@ export default (ctx) => {
|
|
|
404
413
|
return ['call', '$__ptr_type', receiver]
|
|
405
414
|
}
|
|
406
415
|
|
|
407
|
-
function emitDynGetExprTyped(base, key, vt) {
|
|
408
|
-
inc('__dyn_get_expr_t')
|
|
416
|
+
function emitDynGetExprTyped(base, key, vt, prop) {
|
|
409
417
|
const receiver = asI64(base?.type ? base : typed(base, 'f64'))
|
|
418
|
+
// Constant string key: fold the FNV hash at compile time and call the
|
|
419
|
+
// prehashed body — no __str_hash on every access.
|
|
420
|
+
if (typeof prop === 'string') {
|
|
421
|
+
inc('__dyn_get_expr_t_h')
|
|
422
|
+
return typed(['f64.reinterpret_i64', ['call', '$__dyn_get_expr_t_h', receiver, key, emitTypeTag(receiver, vt), ['i32.const', strHashLiteral(prop)]]], 'f64')
|
|
423
|
+
}
|
|
424
|
+
inc('__dyn_get_expr_t')
|
|
410
425
|
return typed(['f64.reinterpret_i64', ['call', '$__dyn_get_expr_t', receiver, key, emitTypeTag(receiver, vt)]], 'f64')
|
|
411
426
|
}
|
|
412
427
|
|
|
@@ -486,7 +501,7 @@ export default (ctx) => {
|
|
|
486
501
|
if (typeof obj === 'string') {
|
|
487
502
|
const vt = lookupValType(obj)
|
|
488
503
|
if (usesDynProps(vt)) {
|
|
489
|
-
return emitDynGetExprTyped(va, key, vt)
|
|
504
|
+
return emitDynGetExprTyped(va, key, vt, prop)
|
|
490
505
|
}
|
|
491
506
|
if (vt === VAL.HASH) {
|
|
492
507
|
return emitHashGetLocalConst(va, key, prop)
|
|
@@ -495,12 +510,12 @@ export default (ctx) => {
|
|
|
495
510
|
// at off-16 (set by __dyn_set). __hash_get assumes HASH bucket layout
|
|
496
511
|
// and would mis-read OBJECT memory.
|
|
497
512
|
if (vt === VAL.OBJECT) {
|
|
498
|
-
return emitDynGetExprTyped(va, key, vt)
|
|
513
|
+
return emitDynGetExprTyped(va, key, vt, prop)
|
|
499
514
|
}
|
|
500
515
|
if (vt == null) {
|
|
501
516
|
// In WASI mode, values are always JSON-derived (never PTR.EXTERNAL host objects).
|
|
502
517
|
// Skip the external branch and dispatch through the typed HASH/OBJECT path.
|
|
503
|
-
if (ctx.transform.host === 'wasi') return emitDynGetExprTyped(va, key, vt)
|
|
518
|
+
if (ctx.transform.host === 'wasi') return emitDynGetExprTyped(va, key, vt, prop)
|
|
504
519
|
ctx.features.external = true
|
|
505
520
|
return emitDynGetAnyTyped(va, key, vt)
|
|
506
521
|
}
|
|
@@ -598,7 +613,8 @@ export default (ctx) => {
|
|
|
598
613
|
|
|
599
614
|
// Module-registered property emitter (.size, etc.)
|
|
600
615
|
const propKey = `.${prop}`
|
|
601
|
-
|
|
616
|
+
const propEmitter = ctx.core.emit[propKey]
|
|
617
|
+
if (propEmitter && propEmitter.length <= 1) return propEmitter(obj)
|
|
602
618
|
|
|
603
619
|
return emitPropAccess(emit(obj), obj, prop)
|
|
604
620
|
}
|
|
@@ -618,7 +634,7 @@ export default (ctx) => {
|
|
|
618
634
|
if (typeof obj === 'string') {
|
|
619
635
|
const objType = lookupValType(obj)
|
|
620
636
|
if (usesDynProps(objType)) {
|
|
621
|
-
access = emitDynGetExprTyped(['local.get', `$${t}`], asI64(emit(['str', prop])), objType)
|
|
637
|
+
access = emitDynGetExprTyped(['local.get', `$${t}`], asI64(emit(['str', prop])), objType, prop)
|
|
622
638
|
} else if (objType === VAL.HASH) {
|
|
623
639
|
access = emitHashGetLocalConst(['local.get', `$${t}`], asI64(emit(['str', prop])), prop)
|
|
624
640
|
} else if (objType == null) {
|
|
@@ -626,7 +642,7 @@ export default (ctx) => {
|
|
|
626
642
|
// In JS host mode use __dyn_get_any_t but don't force features.external here
|
|
627
643
|
// since ?.prop short-circuits on nullish (EXTERNAL arm is dead unless already on).
|
|
628
644
|
access = ctx.transform.host === 'wasi'
|
|
629
|
-
? emitDynGetExprTyped(['local.get', `$${t}`], asI64(emit(['str', prop])), objType)
|
|
645
|
+
? emitDynGetExprTyped(['local.get', `$${t}`], asI64(emit(['str', prop])), objType, prop)
|
|
630
646
|
: emitDynGetAnyTyped(['local.get', `$${t}`], asI64(emit(['str', prop])), objType)
|
|
631
647
|
} else {
|
|
632
648
|
inc('__hash_get', '__str_hash', '__str_eq')
|
|
@@ -636,12 +652,20 @@ export default (ctx) => {
|
|
|
636
652
|
if (valTypeOf(obj) === VAL.HASH) {
|
|
637
653
|
access = emitHashGetLocalConst(['local.get', `$${t}`], asI64(emit(['str', prop])), prop)
|
|
638
654
|
} else {
|
|
639
|
-
access = emitDynGetExprTyped(['local.get', `$${t}`], asI64(emit(['str', prop])), valTypeOf(obj))
|
|
655
|
+
access = emitDynGetExprTyped(['local.get', `$${t}`], asI64(emit(['str', prop])), valTypeOf(obj), prop)
|
|
640
656
|
}
|
|
641
657
|
}
|
|
642
658
|
}
|
|
643
659
|
}
|
|
644
|
-
|
|
660
|
+
// Use local.set + local.get (not local.tee inside guard) because isNullish
|
|
661
|
+
// inlines the null/undefined check as (i32.or (i64.eq X NULL) (i64.eq X UNDEF)),
|
|
662
|
+
// which duplicates X — a local.tee(block(call $sideEffect)) would run twice.
|
|
663
|
+
return typed(['block', ['result', 'f64'],
|
|
664
|
+
['local.set', `$${t}`, va],
|
|
665
|
+
['if', ['result', 'f64'],
|
|
666
|
+
notNullish(typed(['local.get', `$${t}`], 'f64')),
|
|
667
|
+
['then', access],
|
|
668
|
+
['else', ['f64.const', `nan:${UNDEF_NAN}`]]]], 'f64')
|
|
645
669
|
}
|
|
646
670
|
|
|
647
671
|
// Optional index: arr?.[i] → null if arr is null, else arr[i]
|
|
@@ -656,7 +680,15 @@ export default (ctx) => {
|
|
|
656
680
|
if (!ctx.types.typedElem) ctx.types.typedElem = new Map()
|
|
657
681
|
ctx.types.typedElem.set(t, ctx.types.typedElem.get(arr))
|
|
658
682
|
}
|
|
659
|
-
|
|
683
|
+
// Use local.set + local.get (not local.tee inside guard) because isNullish
|
|
684
|
+
// inlines the null/undefined check as (i32.or (i64.eq X NULL) (i64.eq X UNDEF)),
|
|
685
|
+
// which duplicates X — a local.tee(block(call $sideEffect)) would run twice.
|
|
686
|
+
return typed(['block', ['result', 'f64'],
|
|
687
|
+
['local.set', `$${t}`, va],
|
|
688
|
+
['if', ['result', 'f64'],
|
|
689
|
+
notNullish(typed(['local.get', `$${t}`], 'f64')),
|
|
690
|
+
['then', asF64(ctx.core.emit['[]'](t, idx))],
|
|
691
|
+
['else', ['f64.const', `nan:${UNDEF_NAN}`]]]], 'f64')
|
|
660
692
|
}
|
|
661
693
|
|
|
662
694
|
// Optional call: fn?.(...args) → null if fn is null, else call fn
|
|
@@ -674,7 +706,14 @@ export default (ctx) => {
|
|
|
674
706
|
const vt = typeof recv === 'string' ? repOf(recv)?.val : valTypeOf(recv)
|
|
675
707
|
if (vt) updateRep(t, { val: vt })
|
|
676
708
|
const callResult = methodEmit(t, ...args)
|
|
677
|
-
|
|
709
|
+
// Use local.set + local.get (not local.tee inside guard) because isNullish
|
|
710
|
+
// inlines the null/undefined check which duplicates the expression.
|
|
711
|
+
return typed(['block', ['result', 'f64'],
|
|
712
|
+
['local.set', `$${t}`, va],
|
|
713
|
+
['if', ['result', 'f64'],
|
|
714
|
+
notNullish(typed(['local.get', `$${t}`], 'f64')),
|
|
715
|
+
['then', asF64(callResult)],
|
|
716
|
+
['else', ['f64.const', `nan:${UNDEF_NAN}`]]]], 'f64')
|
|
678
717
|
}
|
|
679
718
|
}
|
|
680
719
|
const t = temp()
|
|
@@ -682,7 +721,14 @@ export default (ctx) => {
|
|
|
682
721
|
// If nullish → return NULL_NAN, else call via fn.call
|
|
683
722
|
if (!ctx.closure.call) err('Optional call requires fn module')
|
|
684
723
|
const callResult = ctx.closure.call(typed(['local.get', `$${t}`], 'f64'), args)
|
|
685
|
-
|
|
724
|
+
// Use local.set + local.get (not local.tee inside guard) because isNullish
|
|
725
|
+
// inlines the null/undefined check which duplicates the expression.
|
|
726
|
+
return typed(['block', ['result', 'f64'],
|
|
727
|
+
['local.set', `$${t}`, va],
|
|
728
|
+
['if', ['result', 'f64'],
|
|
729
|
+
notNullish(typed(['local.get', `$${t}`], 'f64')),
|
|
730
|
+
['then', asF64(callResult)],
|
|
731
|
+
['else', ['f64.const', `nan:${UNDEF_NAN}`]]]], 'f64')
|
|
686
732
|
}
|
|
687
733
|
|
|
688
734
|
// typeof: returns JS-style string. Reachable results are number/undefined/string/function/symbol/object
|
package/module/date.js
CHANGED
|
@@ -76,6 +76,11 @@ export default (ctx) => {
|
|
|
76
76
|
], 'f64')
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
ctx.core.emit['Date.parse'] = (value) => {
|
|
80
|
+
inc('__date_from_value')
|
|
81
|
+
return typed(['call', '$__date_from_value', ['i64.reinterpret_f64', asF64(emit(value))]], 'f64')
|
|
82
|
+
}
|
|
83
|
+
|
|
79
84
|
// ── Core algorithms ───────────────────────────────────────────────────────
|
|
80
85
|
|
|
81
86
|
ctx.core.stdlib['__date_days_from_year'] = `(func $__date_days_from_year (param $y f64) (result f64)
|
package/module/function.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { typed, asF64, asI32, mkPtrIR, temp, tempI32, MAX_CLOSURE_ARITY, UNDEF_NAN } from '../src/ir.js'
|
|
14
|
-
import { emit
|
|
14
|
+
import { emit } from '../src/emit.js'
|
|
15
|
+
import { isReassigned } from '../src/ast.js'
|
|
15
16
|
import { T, lookupValType, repOf, findFreeVars } from '../src/analyze.js'
|
|
16
17
|
import { PTR, LAYOUT, inc, err } from '../src/ctx.js'
|
|
17
18
|
|
package/module/json.js
CHANGED
|
@@ -50,7 +50,8 @@ export default (ctx) => {
|
|
|
50
50
|
__jput_str: ['__char_at', '__str_byteLen'],
|
|
51
51
|
__jp: ['__jp_val', '__jp_str', '__jp_num', '__jp_arr', '__jp_obj', '__sso_char', '__ptr_aux', '__ptr_type', '__ptr_offset', '__str_byteLen'],
|
|
52
52
|
__jp_val: ['__jp_str', '__jp_num', '__jp_arr', '__jp_obj'],
|
|
53
|
-
__jp_str: ['__sso_char', '__char_at', '__str_byteLen'],
|
|
53
|
+
__jp_str: ['__sso_char', '__char_at', '__str_byteLen', '__hex4', '__utf8_enc'],
|
|
54
|
+
__hex4: ['__hex1'],
|
|
54
55
|
__jp_num: ['__pow10'],
|
|
55
56
|
__jp_arr: ['__jp_val'],
|
|
56
57
|
__jp_obj: ['__jp_val', '__jp_str', '__jp_schema_get', '__alloc_hdr', '__mkptr'],
|
|
@@ -319,8 +320,45 @@ export default (ctx) => {
|
|
|
319
320
|
// SSO byte packing for ≤4-char ASCII keys, and FNV-1a hash. The hash is
|
|
320
321
|
// stashed in $__jp_keyh so __jp_obj can use the prehashed insert and skip
|
|
321
322
|
// a redundant __str_hash call.
|
|
323
|
+
// Hex nibble: '0'-'9' / 'a'-'f' / 'A'-'F' → 0..15; anything else → 0 (lenient).
|
|
324
|
+
ctx.core.stdlib['__hex1'] = `(func $__hex1 (param $c i32) (result i32)
|
|
325
|
+
(if (i32.le_u (i32.sub (local.get $c) (i32.const 48)) (i32.const 9))
|
|
326
|
+
(then (return (i32.sub (local.get $c) (i32.const 48)))))
|
|
327
|
+
(if (i32.le_u (i32.sub (i32.or (local.get $c) (i32.const 0x20)) (i32.const 97)) (i32.const 5))
|
|
328
|
+
(then (return (i32.sub (i32.or (local.get $c) (i32.const 0x20)) (i32.const 87)))))
|
|
329
|
+
(i32.const 0))`
|
|
330
|
+
|
|
331
|
+
// Read 4 hex bytes at absolute address $p → 16-bit value.
|
|
332
|
+
ctx.core.stdlib['__hex4'] = `(func $__hex4 (param $p i32) (result i32)
|
|
333
|
+
(i32.or (i32.or (i32.or
|
|
334
|
+
(i32.shl (call $__hex1 (i32.load8_u (local.get $p))) (i32.const 12))
|
|
335
|
+
(i32.shl (call $__hex1 (i32.load8_u (i32.add (local.get $p) (i32.const 1)))) (i32.const 8)))
|
|
336
|
+
(i32.shl (call $__hex1 (i32.load8_u (i32.add (local.get $p) (i32.const 2)))) (i32.const 4)))
|
|
337
|
+
(call $__hex1 (i32.load8_u (i32.add (local.get $p) (i32.const 3))))))`
|
|
338
|
+
|
|
339
|
+
// Encode code point $cp as UTF-8 at $off; returns bytes written (1-4).
|
|
340
|
+
ctx.core.stdlib['__utf8_enc'] = `(func $__utf8_enc (param $off i32) (param $cp i32) (result i32)
|
|
341
|
+
(if (i32.lt_u (local.get $cp) (i32.const 0x80))
|
|
342
|
+
(then (i32.store8 (local.get $off) (local.get $cp)) (return (i32.const 1))))
|
|
343
|
+
(if (i32.lt_u (local.get $cp) (i32.const 0x800))
|
|
344
|
+
(then
|
|
345
|
+
(i32.store8 (local.get $off) (i32.or (i32.const 0xC0) (i32.shr_u (local.get $cp) (i32.const 6))))
|
|
346
|
+
(i32.store8 (i32.add (local.get $off) (i32.const 1)) (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))))
|
|
347
|
+
(return (i32.const 2))))
|
|
348
|
+
(if (i32.lt_u (local.get $cp) (i32.const 0x10000))
|
|
349
|
+
(then
|
|
350
|
+
(i32.store8 (local.get $off) (i32.or (i32.const 0xE0) (i32.shr_u (local.get $cp) (i32.const 12))))
|
|
351
|
+
(i32.store8 (i32.add (local.get $off) (i32.const 1)) (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 6)) (i32.const 0x3F))))
|
|
352
|
+
(i32.store8 (i32.add (local.get $off) (i32.const 2)) (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))))
|
|
353
|
+
(return (i32.const 3))))
|
|
354
|
+
(i32.store8 (local.get $off) (i32.or (i32.const 0xF0) (i32.shr_u (local.get $cp) (i32.const 18))))
|
|
355
|
+
(i32.store8 (i32.add (local.get $off) (i32.const 1)) (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 12)) (i32.const 0x3F))))
|
|
356
|
+
(i32.store8 (i32.add (local.get $off) (i32.const 2)) (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 6)) (i32.const 0x3F))))
|
|
357
|
+
(i32.store8 (i32.add (local.get $off) (i32.const 3)) (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))))
|
|
358
|
+
(i32.const 4))`
|
|
359
|
+
|
|
322
360
|
ctx.core.stdlib['__jp_str'] = `(func $__jp_str (result f64)
|
|
323
|
-
(local $start i32) (local $ch i32) (local $len i32) (local $off i32) (local $i i32) (local $simple i32) (local $sso i32) (local $h i32)
|
|
361
|
+
(local $start i32) (local $ch i32) (local $len i32) (local $off i32) (local $i i32) (local $simple i32) (local $sso i32) (local $h i32) (local $cp i32)
|
|
324
362
|
(local.set $start (global.get $__jppos))
|
|
325
363
|
(local.set $simple (i32.const 1))
|
|
326
364
|
(local.set $h (i32.const 0x811c9dc5))
|
|
@@ -382,12 +420,32 @@ export default (ctx) => {
|
|
|
382
420
|
${ADV(1)}
|
|
383
421
|
(local.set $ch ${PEEK})
|
|
384
422
|
${ADV(1)}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
423
|
+
(if (i32.eq (local.get $ch) (i32.const 117)) ;; \\uXXXX
|
|
424
|
+
(then
|
|
425
|
+
(local.set $cp (call $__hex4 (i32.add (global.get $__jpstr) (global.get $__jppos))))
|
|
426
|
+
${ADV(4)}
|
|
427
|
+
;; High surrogate immediately followed by \\uXXXX low surrogate → combine.
|
|
428
|
+
(if (i32.and
|
|
429
|
+
(i32.eq (i32.and (local.get $cp) (i32.const 0xFC00)) (i32.const 0xD800))
|
|
430
|
+
(i32.and (i32.eq ${PEEK} (i32.const 92))
|
|
431
|
+
(i32.eq (i32.load8_u (i32.add (global.get $__jpstr) (i32.add (global.get $__jppos) (i32.const 1)))) (i32.const 117))))
|
|
432
|
+
(then
|
|
433
|
+
${ADV(2)}
|
|
434
|
+
(local.set $i (call $__hex4 (i32.add (global.get $__jpstr) (global.get $__jppos))))
|
|
435
|
+
${ADV(4)}
|
|
436
|
+
(local.set $cp (i32.add (i32.const 0x10000)
|
|
437
|
+
(i32.or (i32.shl (i32.and (local.get $cp) (i32.const 0x3FF)) (i32.const 10))
|
|
438
|
+
(i32.and (local.get $i) (i32.const 0x3FF)))))))
|
|
439
|
+
(local.set $len (i32.add (local.get $len)
|
|
440
|
+
(call $__utf8_enc (i32.add (local.get $off) (local.get $len)) (local.get $cp))))
|
|
441
|
+
(br $l2))
|
|
442
|
+
(else
|
|
443
|
+
;; Decode simple escape: n→10 t→9 r→13 b→8 f→12, else literal char.
|
|
444
|
+
(if (i32.eq (local.get $ch) (i32.const 110)) (then (local.set $ch (i32.const 10))))
|
|
445
|
+
(if (i32.eq (local.get $ch) (i32.const 116)) (then (local.set $ch (i32.const 9))))
|
|
446
|
+
(if (i32.eq (local.get $ch) (i32.const 114)) (then (local.set $ch (i32.const 13))))
|
|
447
|
+
(if (i32.eq (local.get $ch) (i32.const 98)) (then (local.set $ch (i32.const 8))))
|
|
448
|
+
(if (i32.eq (local.get $ch) (i32.const 102)) (then (local.set $ch (i32.const 12)))))))
|
|
391
449
|
(else ${ADV(1)}))
|
|
392
450
|
(i32.store8 (i32.add (local.get $off) (local.get $len)) (local.get $ch))
|
|
393
451
|
(local.set $len (i32.add (local.get $len) (i32.const 1)))
|
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.0",
|
|
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",
|