jz 0.0.0 → 0.1.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/LICENSE +21 -0
- package/README.md +381 -0
- package/cli.js +163 -0
- package/index.js +217 -0
- package/module/array.js +1317 -0
- package/module/collection.js +791 -0
- package/module/console.js +190 -0
- package/module/core.js +642 -0
- package/module/function.js +180 -0
- package/module/index.js +15 -0
- package/module/json.js +504 -0
- package/module/math.js +389 -0
- package/module/number.js +605 -0
- package/module/object.js +357 -0
- package/module/regex.js +913 -0
- package/module/schema.js +104 -0
- package/module/string.js +928 -0
- package/module/symbol.js +54 -0
- package/module/timer.js +253 -0
- package/module/typedarray.js +711 -0
- package/package.json +54 -5
- package/src/analyze.js +1906 -0
- package/src/compile.js +2175 -0
- package/src/ctx.js +243 -0
- package/src/emit.js +2095 -0
- package/src/host.js +524 -0
- package/src/ir.js +649 -0
- package/src/jzify.js +391 -0
- package/src/optimize.js +1352 -0
- package/src/prepare.js +1598 -0
- package/wasi.js +74 -0
package/module/number.js
ADDED
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number module — toString, toFixed, toPrecision, toExponential, String().
|
|
3
|
+
*
|
|
4
|
+
* Core: __ftoa(f64, precision, mode) → f64 (NaN-boxed string pointer).
|
|
5
|
+
* Modes: 0=default (shortest repr), 1=fixed (toFixed).
|
|
6
|
+
* Uses integer-based digit extraction to avoid float drift.
|
|
7
|
+
* Static string table at address 0 for NaN, Infinity, etc.
|
|
8
|
+
*
|
|
9
|
+
* @module number
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { emit, typed, asF64, asI32, asI64, NULL_NAN, UNDEF_NAN, valTypeOf, VAL, temp, tempI32, tempI64 } from '../src/compile.js'
|
|
13
|
+
import { inc, PTR } from '../src/ctx.js'
|
|
14
|
+
|
|
15
|
+
export default (ctx) => {
|
|
16
|
+
Object.assign(ctx.core.stdlibDeps, {
|
|
17
|
+
__mkstr: ['__alloc'],
|
|
18
|
+
__ftoa: ['__itoa', '__pow10', '__mkstr', '__static_str'],
|
|
19
|
+
__toExp: ['__itoa', '__pow10', '__mkstr', '__static_str'],
|
|
20
|
+
__to_num: ['__char_at', '__str_byteLen', '__pow10'],
|
|
21
|
+
__parseInt: ['__char_at', '__str_byteLen'],
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// __pow10(n: i32) → f64 — compute 10^n via loop
|
|
26
|
+
ctx.core.stdlib['__pow10'] = `(func $__pow10 (param $n i32) (result f64)
|
|
27
|
+
(local $r f64)
|
|
28
|
+
(local.set $r (f64.const 1))
|
|
29
|
+
(block $d (loop $l
|
|
30
|
+
(br_if $d (i32.le_s (local.get $n) (i32.const 0)))
|
|
31
|
+
(local.set $r (f64.mul (local.get $r) (f64.const 10)))
|
|
32
|
+
(local.set $n (i32.sub (local.get $n) (i32.const 1)))
|
|
33
|
+
(br $l)))
|
|
34
|
+
(local.get $r))`
|
|
35
|
+
|
|
36
|
+
// __itoa(val: i32, buf: i32) → i32 (digit count). Writes decimal digits to buf.
|
|
37
|
+
ctx.core.stdlib['__itoa'] = `(func $__itoa (param $val i32) (param $buf i32) (result i32)
|
|
38
|
+
(local $len i32) (local $i i32) (local $j i32) (local $tmp i32)
|
|
39
|
+
(if (i32.eqz (local.get $val))
|
|
40
|
+
(then (i32.store8 (local.get $buf) (i32.const 48)) (return (i32.const 1))))
|
|
41
|
+
(local.set $tmp (local.get $val))
|
|
42
|
+
(block $d (loop $l
|
|
43
|
+
(br_if $d (i32.eqz (local.get $tmp)))
|
|
44
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $len))
|
|
45
|
+
(i32.add (i32.const 48) (i32.rem_u (local.get $tmp) (i32.const 10))))
|
|
46
|
+
(local.set $tmp (i32.div_u (local.get $tmp) (i32.const 10)))
|
|
47
|
+
(local.set $len (i32.add (local.get $len) (i32.const 1)))
|
|
48
|
+
(br $l)))
|
|
49
|
+
;; Reverse
|
|
50
|
+
(local.set $j (i32.sub (local.get $len) (i32.const 1)))
|
|
51
|
+
(block $rd (loop $rl
|
|
52
|
+
(br_if $rd (i32.ge_s (local.get $i) (local.get $j)))
|
|
53
|
+
(local.set $tmp (i32.load8_u (i32.add (local.get $buf) (local.get $i))))
|
|
54
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $i))
|
|
55
|
+
(i32.load8_u (i32.add (local.get $buf) (local.get $j))))
|
|
56
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $j)) (local.get $tmp))
|
|
57
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
58
|
+
(local.set $j (i32.sub (local.get $j) (i32.const 1)))
|
|
59
|
+
(br $rl)))
|
|
60
|
+
(local.get $len))`
|
|
61
|
+
|
|
62
|
+
// __mkstr(buf: i32, len: i32) → f64 — copy scratch buffer to heap string.
|
|
63
|
+
// Hot (~60M calls in watr self-host via __ftoa). bulk memory.copy is ~10× faster than
|
|
64
|
+
// a hand-rolled byte loop (wasm2c lowers it to memcpy under PGO+LTO).
|
|
65
|
+
ctx.core.stdlib['__mkstr'] = `(func $__mkstr (param $buf i32) (param $len i32) (result f64)
|
|
66
|
+
(local $off i32)
|
|
67
|
+
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $len))))
|
|
68
|
+
(i32.store (local.get $off) (local.get $len))
|
|
69
|
+
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
70
|
+
(memory.copy (local.get $off) (local.get $buf) (local.get $len))
|
|
71
|
+
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
72
|
+
|
|
73
|
+
// __ftoa(val: f64, prec: i32, mode: i32) → f64 (NaN-boxed string)
|
|
74
|
+
// mode 0: default (shortest repr, strip trailing zeros)
|
|
75
|
+
// mode 1: fixed (exactly prec decimal places)
|
|
76
|
+
// Uses integer-scaled digit extraction to avoid float drift.
|
|
77
|
+
ctx.core.stdlib['__ftoa'] = `(func $__ftoa (param $val f64) (param $prec i32) (param $mode i32) (result f64)
|
|
78
|
+
(local $buf i32) (local $pos i32) (local $neg i32)
|
|
79
|
+
(local $abs f64) (local $scale f64) (local $scaled f64)
|
|
80
|
+
(local $int i32) (local $frac i32) (local $ilen i32) (local $flen i32)
|
|
81
|
+
(local $i i32) (local $j i32)
|
|
82
|
+
;; Special values
|
|
83
|
+
(if (f64.ne (local.get $val) (local.get $val)) (then (return (call $__static_str (i32.const 0)))))
|
|
84
|
+
(if (f64.eq (local.get $val) (f64.const inf)) (then (return (call $__static_str (i32.const 1)))))
|
|
85
|
+
(if (f64.eq (local.get $val) (f64.const -inf)) (then (return (call $__static_str (i32.const 2)))))
|
|
86
|
+
(local.set $buf (call $__alloc (i32.const 40)))
|
|
87
|
+
;; Sign
|
|
88
|
+
(if (f64.lt (local.get $val) (f64.const 0))
|
|
89
|
+
(then (local.set $neg (i32.const 1)) (local.set $val (f64.neg (local.get $val)))))
|
|
90
|
+
(if (i32.and (f64.eq (local.get $val) (f64.const 0)) (local.get $neg))
|
|
91
|
+
(then (local.set $neg (i32.const 0))))
|
|
92
|
+
(if (local.get $neg)
|
|
93
|
+
(then (i32.store8 (local.get $buf) (i32.const 45))
|
|
94
|
+
(local.set $pos (i32.const 1))))
|
|
95
|
+
;; Default mode: auto-select precision (up to 9 digits, must fit i32 when scaled)
|
|
96
|
+
(if (i32.eqz (local.get $mode))
|
|
97
|
+
(then (local.set $prec (i32.const 9))))
|
|
98
|
+
;; Round and scale to integer: scaled = nearest(val * 10^prec)
|
|
99
|
+
(local.set $scale (call $__pow10 (local.get $prec)))
|
|
100
|
+
(local.set $scaled (f64.nearest (f64.mul (local.get $val) (local.get $scale))))
|
|
101
|
+
;; If scaled doesn't fit i32, reduce precision until it does (min prec=0)
|
|
102
|
+
(block $fit (loop $fitl
|
|
103
|
+
(br_if $fit (f64.lt (local.get $scaled) (f64.const 2147483648)))
|
|
104
|
+
(br_if $fit (i32.le_s (local.get $prec) (i32.const 0)))
|
|
105
|
+
(local.set $prec (i32.sub (local.get $prec) (i32.const 1)))
|
|
106
|
+
(local.set $scale (call $__pow10 (local.get $prec)))
|
|
107
|
+
(local.set $scaled (f64.nearest (f64.mul (local.get $val) (local.get $scale))))
|
|
108
|
+
(br $fitl)))
|
|
109
|
+
;; Split: int = scaled / scale, frac = scaled % scale
|
|
110
|
+
(if (f64.lt (local.get $scaled) (f64.const 2147483648))
|
|
111
|
+
(then
|
|
112
|
+
(local.set $int (i32.trunc_f64_u (f64.div (local.get $scaled) (local.get $scale))))
|
|
113
|
+
(local.set $frac (i32.trunc_f64_u (f64.sub (local.get $scaled)
|
|
114
|
+
(f64.mul (f64.convert_i32_u (local.get $int)) (local.get $scale))))))
|
|
115
|
+
(else
|
|
116
|
+
(local.set $int (i32.const 0))
|
|
117
|
+
(local.set $frac (i32.const 0))
|
|
118
|
+
(local.set $prec (i32.const 0))
|
|
119
|
+
(local.set $abs (f64.trunc (local.get $val)))
|
|
120
|
+
;; Write large integer digits reversed
|
|
121
|
+
(local.set $ilen (local.get $pos))
|
|
122
|
+
(block $ld (loop $ll
|
|
123
|
+
(br_if $ld (f64.lt (local.get $abs) (f64.const 1)))
|
|
124
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $pos))
|
|
125
|
+
(i32.add (i32.const 48) (i32.trunc_f64_u (f64.sub (local.get $abs)
|
|
126
|
+
(f64.mul (f64.trunc (f64.div (local.get $abs) (f64.const 10))) (f64.const 10))))))
|
|
127
|
+
(local.set $abs (f64.trunc (f64.div (local.get $abs) (f64.const 10))))
|
|
128
|
+
(local.set $pos (i32.add (local.get $pos) (i32.const 1)))
|
|
129
|
+
(br $ll)))
|
|
130
|
+
;; Reverse
|
|
131
|
+
(local.set $i (local.get $ilen)) (local.set $j (i32.sub (local.get $pos) (i32.const 1)))
|
|
132
|
+
(block $rd (loop $rl
|
|
133
|
+
(br_if $rd (i32.ge_s (local.get $i) (local.get $j)))
|
|
134
|
+
(local.set $int (i32.load8_u (i32.add (local.get $buf) (local.get $i))))
|
|
135
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $i))
|
|
136
|
+
(i32.load8_u (i32.add (local.get $buf) (local.get $j))))
|
|
137
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $j)) (local.get $int))
|
|
138
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
139
|
+
(local.set $j (i32.sub (local.get $j) (i32.const 1)))
|
|
140
|
+
(br $rl)))
|
|
141
|
+
(return (call $__mkstr (local.get $buf) (local.get $pos)))))
|
|
142
|
+
;; Write integer part
|
|
143
|
+
(local.set $ilen (call $__itoa (local.get $int) (i32.add (local.get $buf) (local.get $pos))))
|
|
144
|
+
(local.set $pos (i32.add (local.get $pos) (local.get $ilen)))
|
|
145
|
+
;; Write fractional part: extract digits from $frac by dividing by 10^(prec-1), 10^(prec-2), ...
|
|
146
|
+
(if (i32.gt_s (local.get $prec) (i32.const 0))
|
|
147
|
+
(then
|
|
148
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $pos)) (i32.const 46))
|
|
149
|
+
(local.set $pos (i32.add (local.get $pos) (i32.const 1)))
|
|
150
|
+
(local.set $i (i32.sub (local.get $prec) (i32.const 1)))
|
|
151
|
+
(block $fd (loop $fl
|
|
152
|
+
(br_if $fd (i32.lt_s (local.get $i) (i32.const 0)))
|
|
153
|
+
(local.set $j (i32.div_u (local.get $frac) (i32.trunc_f64_u (call $__pow10 (local.get $i)))))
|
|
154
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $pos))
|
|
155
|
+
(i32.add (i32.const 48) (i32.rem_u (local.get $j) (i32.const 10))))
|
|
156
|
+
(local.set $pos (i32.add (local.get $pos) (i32.const 1)))
|
|
157
|
+
(local.set $i (i32.sub (local.get $i) (i32.const 1)))
|
|
158
|
+
(br $fl)))))
|
|
159
|
+
;; Default mode: strip trailing zeros and dot — only when a fractional part was emitted.
|
|
160
|
+
;; Gating on $prec>0 prevents stripping zeros from the integer part (e.g. 1079623680 → 107962368)
|
|
161
|
+
;; for values where auto-fit reduced prec to 0 because the scaled integer wouldn't fit i32.
|
|
162
|
+
(if (i32.and (i32.eqz (local.get $mode)) (i32.gt_s (local.get $prec) (i32.const 0)))
|
|
163
|
+
(then
|
|
164
|
+
(block $sd (loop $sl
|
|
165
|
+
(br_if $sd (i32.le_s (local.get $pos) (i32.const 0)))
|
|
166
|
+
(br_if $sd (i32.ne (i32.load8_u (i32.add (local.get $buf) (i32.sub (local.get $pos) (i32.const 1)))) (i32.const 48)))
|
|
167
|
+
(local.set $pos (i32.sub (local.get $pos) (i32.const 1)))
|
|
168
|
+
(br $sl)))
|
|
169
|
+
(if (i32.and (i32.gt_s (local.get $pos) (i32.const 0))
|
|
170
|
+
(i32.eq (i32.load8_u (i32.add (local.get $buf) (i32.sub (local.get $pos) (i32.const 1)))) (i32.const 46)))
|
|
171
|
+
(then (local.set $pos (i32.sub (local.get $pos) (i32.const 1)))))))
|
|
172
|
+
(call $__mkstr (local.get $buf) (local.get $pos)))`
|
|
173
|
+
|
|
174
|
+
// __toExp(val: f64, prec: i32) → f64 (NaN-boxed string)
|
|
175
|
+
// Format: [-]d.ddd...e[+/-]dd — integer-based digit extraction
|
|
176
|
+
ctx.core.stdlib['__toExp'] = `(func $__toExp (param $val f64) (param $prec i32) (result f64)
|
|
177
|
+
(local $buf i32) (local $pos i32) (local $neg i32) (local $exp i32)
|
|
178
|
+
(local $len i32) (local $i i32) (local $j i32)
|
|
179
|
+
(local $mantissa f64) (local $scale f64)
|
|
180
|
+
(if (f64.ne (local.get $val) (local.get $val)) (then (return (call $__static_str (i32.const 0)))))
|
|
181
|
+
(if (f64.eq (local.get $val) (f64.const inf)) (then (return (call $__static_str (i32.const 1)))))
|
|
182
|
+
(if (f64.eq (local.get $val) (f64.const -inf)) (then (return (call $__static_str (i32.const 2)))))
|
|
183
|
+
(local.set $buf (call $__alloc (i32.const 32)))
|
|
184
|
+
;; Sign
|
|
185
|
+
(if (f64.lt (local.get $val) (f64.const 0))
|
|
186
|
+
(then (local.set $neg (i32.const 1)) (local.set $val (f64.neg (local.get $val)))))
|
|
187
|
+
(if (i32.and (f64.eq (local.get $val) (f64.const 0)) (local.get $neg))
|
|
188
|
+
(then (local.set $neg (i32.const 0))))
|
|
189
|
+
(if (local.get $neg)
|
|
190
|
+
(then (i32.store8 (local.get $buf) (i32.const 45))
|
|
191
|
+
(local.set $pos (i32.const 1))))
|
|
192
|
+
;; Normalize: 1 <= val < 10
|
|
193
|
+
(if (f64.gt (local.get $val) (f64.const 0))
|
|
194
|
+
(then
|
|
195
|
+
(block $d1 (loop $l1
|
|
196
|
+
(br_if $d1 (f64.lt (local.get $val) (f64.const 10)))
|
|
197
|
+
(local.set $val (f64.div (local.get $val) (f64.const 10)))
|
|
198
|
+
(local.set $exp (i32.add (local.get $exp) (i32.const 1)))
|
|
199
|
+
(br $l1)))
|
|
200
|
+
(block $d2 (loop $l2
|
|
201
|
+
(br_if $d2 (f64.ge (local.get $val) (f64.const 1)))
|
|
202
|
+
(local.set $val (f64.mul (local.get $val) (f64.const 10)))
|
|
203
|
+
(local.set $exp (i32.sub (local.get $exp) (i32.const 1)))
|
|
204
|
+
(br $l2)))))
|
|
205
|
+
;; Scale to integer mantissa: nearest(val * 10^prec)
|
|
206
|
+
(local.set $scale (call $__pow10 (local.get $prec)))
|
|
207
|
+
(local.set $mantissa (f64.nearest (f64.mul (local.get $val) (local.get $scale))))
|
|
208
|
+
;; Rounding overflow (e.g. 9.95 → 1000 when prec=1, scale=10)
|
|
209
|
+
(if (f64.ge (local.get $mantissa) (f64.mul (f64.const 10) (local.get $scale)))
|
|
210
|
+
(then
|
|
211
|
+
(local.set $mantissa (f64.div (local.get $mantissa) (f64.const 10)))
|
|
212
|
+
(local.set $exp (i32.add (local.get $exp) (i32.const 1)))))
|
|
213
|
+
;; Write mantissa digits via itoa
|
|
214
|
+
(local.set $len (call $__itoa (i32.trunc_f64_u (local.get $mantissa)) (i32.add (local.get $buf) (local.get $pos))))
|
|
215
|
+
;; Insert '.' after first digit
|
|
216
|
+
(if (i32.gt_s (local.get $prec) (i32.const 0))
|
|
217
|
+
(then
|
|
218
|
+
(local.set $i (local.get $len))
|
|
219
|
+
(block $md (loop $ml
|
|
220
|
+
(br_if $md (i32.le_s (local.get $i) (i32.const 1)))
|
|
221
|
+
(i32.store8 (i32.add (local.get $buf) (i32.add (local.get $pos) (local.get $i)))
|
|
222
|
+
(i32.load8_u (i32.add (local.get $buf) (i32.add (local.get $pos) (i32.sub (local.get $i) (i32.const 1))))))
|
|
223
|
+
(local.set $i (i32.sub (local.get $i) (i32.const 1)))
|
|
224
|
+
(br $ml)))
|
|
225
|
+
(i32.store8 (i32.add (local.get $buf) (i32.add (local.get $pos) (i32.const 1))) (i32.const 46))
|
|
226
|
+
(local.set $pos (i32.add (local.get $pos) (i32.add (local.get $len) (i32.const 1)))))
|
|
227
|
+
(else (local.set $pos (i32.add (local.get $pos) (local.get $len)))))
|
|
228
|
+
;; Write 'e', sign, exponent
|
|
229
|
+
(i32.store8 (i32.add (local.get $buf) (local.get $pos)) (i32.const 101))
|
|
230
|
+
(local.set $pos (i32.add (local.get $pos) (i32.const 1)))
|
|
231
|
+
(if (i32.lt_s (local.get $exp) (i32.const 0))
|
|
232
|
+
(then (i32.store8 (i32.add (local.get $buf) (local.get $pos)) (i32.const 45))
|
|
233
|
+
(local.set $pos (i32.add (local.get $pos) (i32.const 1)))
|
|
234
|
+
(local.set $exp (i32.sub (i32.const 0) (local.get $exp))))
|
|
235
|
+
(else (i32.store8 (i32.add (local.get $buf) (local.get $pos)) (i32.const 43))
|
|
236
|
+
(local.set $pos (i32.add (local.get $pos) (i32.const 1)))))
|
|
237
|
+
(local.set $pos (i32.add (local.get $pos) (call $__itoa (local.get $exp) (i32.add (local.get $buf) (local.get $pos)))))
|
|
238
|
+
(call $__mkstr (local.get $buf) (local.get $pos)))`
|
|
239
|
+
|
|
240
|
+
// __static_str(id: i32) → f64 — create heap string from data segment
|
|
241
|
+
// 0=NaN 1=Infinity 2=-Infinity 3=true 4=false 5=null 6=undefined 7=[Array] 8=[Object]
|
|
242
|
+
ctx.core.stdlib['__static_str'] = `(func $__static_str (param $id i32) (result f64)
|
|
243
|
+
(local $src i32) (local $len i32)
|
|
244
|
+
(local.set $src (i32.const 0)) (local.set $len (i32.const 0))
|
|
245
|
+
(if (i32.eqz (local.get $id)) (then (local.set $len (i32.const 3))))
|
|
246
|
+
(if (i32.eq (local.get $id) (i32.const 1)) (then (local.set $src (i32.const 3)) (local.set $len (i32.const 8))))
|
|
247
|
+
(if (i32.eq (local.get $id) (i32.const 2)) (then (local.set $src (i32.const 11)) (local.set $len (i32.const 9))))
|
|
248
|
+
(if (i32.eq (local.get $id) (i32.const 3)) (then (local.set $src (i32.const 20)) (local.set $len (i32.const 4))))
|
|
249
|
+
(if (i32.eq (local.get $id) (i32.const 4)) (then (local.set $src (i32.const 24)) (local.set $len (i32.const 5))))
|
|
250
|
+
(if (i32.eq (local.get $id) (i32.const 5)) (then (local.set $src (i32.const 29)) (local.set $len (i32.const 4))))
|
|
251
|
+
(if (i32.eq (local.get $id) (i32.const 6)) (then (local.set $src (i32.const 33)) (local.set $len (i32.const 9))))
|
|
252
|
+
(if (i32.eq (local.get $id) (i32.const 7)) (then (local.set $src (i32.const 42)) (local.set $len (i32.const 7))))
|
|
253
|
+
(if (i32.eq (local.get $id) (i32.const 8)) (then (local.set $src (i32.const 49)) (local.set $len (i32.const 8))))
|
|
254
|
+
(call $__mkstr (local.get $src) (local.get $len)))`
|
|
255
|
+
|
|
256
|
+
// R: Static strings seeded at address 0. Compile.js strips if __static_str unused.
|
|
257
|
+
// 0=NaN 1=Infinity 2=-Infinity 3=true 4=false 5=null 6=undefined 7=[Array] 8=[Object]
|
|
258
|
+
const staticStr = 'NaNInfinity-Infinitytruefalsenullundefined[Array][Object]'
|
|
259
|
+
ctx.runtime.staticDataLen = staticStr.length
|
|
260
|
+
ctx.runtime.data = (ctx.runtime.data || '') + staticStr
|
|
261
|
+
|
|
262
|
+
// === Number constants ===
|
|
263
|
+
|
|
264
|
+
ctx.core.emit['Number.MAX_SAFE_INTEGER'] = () => typed(['f64.const', 9007199254740991], 'f64')
|
|
265
|
+
ctx.core.emit['Number.MIN_SAFE_INTEGER'] = () => typed(['f64.const', -9007199254740991], 'f64')
|
|
266
|
+
ctx.core.emit['Number.EPSILON'] = () => typed(['f64.const', 2.220446049250313e-16], 'f64')
|
|
267
|
+
ctx.core.emit['Number.MAX_VALUE'] = () => typed(['f64.const', 1.7976931348623157e+308], 'f64')
|
|
268
|
+
ctx.core.emit['Number.MIN_VALUE'] = () => typed(['f64.const', 5e-324], 'f64')
|
|
269
|
+
ctx.core.emit['Number.POSITIVE_INFINITY'] = () => typed(['f64.const', Infinity], 'f64')
|
|
270
|
+
ctx.core.emit['Number.NEGATIVE_INFINITY'] = () => typed(['f64.const', -Infinity], 'f64')
|
|
271
|
+
ctx.core.emit['Number.NaN'] = () => typed(['f64.const', NaN], 'f64')
|
|
272
|
+
|
|
273
|
+
// === Number static methods ===
|
|
274
|
+
|
|
275
|
+
const emitIsNaN = (x) => {
|
|
276
|
+
const v = asF64(emit(x))
|
|
277
|
+
const t = temp('t')
|
|
278
|
+
return typed(['f64.ne', ['local.tee', `$${t}`, v], ['local.get', `$${t}`]], 'i32')
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const emitIsFinite = (x) => {
|
|
282
|
+
const v = asF64(emit(x))
|
|
283
|
+
const t = temp('t')
|
|
284
|
+
return typed(['i32.and',
|
|
285
|
+
['f64.eq', ['local.tee', `$${t}`, v], ['local.get', `$${t}`]],
|
|
286
|
+
['f64.lt', ['f64.abs', ['local.get', `$${t}`]], ['f64.const', Infinity]]], 'i32')
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
ctx.core.emit['Number.isNaN'] = emitIsNaN
|
|
290
|
+
ctx.core.emit['Number.isFinite'] = emitIsFinite
|
|
291
|
+
|
|
292
|
+
// Global isNaN/isFinite — coerce string→number first (unlike Number.isNaN/isFinite)
|
|
293
|
+
ctx.core.emit['isNaN'] = (x) => {
|
|
294
|
+
inc('__to_num')
|
|
295
|
+
const v = asF64(emit(x))
|
|
296
|
+
const t = temp('t')
|
|
297
|
+
return typed(['f64.ne',
|
|
298
|
+
['local.tee', `$${t}`, ['call', '$__to_num', v]],
|
|
299
|
+
['local.get', `$${t}`]], 'i32')
|
|
300
|
+
}
|
|
301
|
+
ctx.core.emit['isFinite'] = (x) => {
|
|
302
|
+
inc('__to_num')
|
|
303
|
+
const v = asF64(emit(x))
|
|
304
|
+
const t = temp('t')
|
|
305
|
+
return typed(['i32.and',
|
|
306
|
+
['f64.eq', ['local.tee', `$${t}`, ['call', '$__to_num', v]], ['local.get', `$${t}`]],
|
|
307
|
+
['f64.lt', ['f64.abs', ['local.get', `$${t}`]], ['f64.const', Infinity]]], 'i32')
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
ctx.core.emit['Number.isInteger'] = (x) => {
|
|
311
|
+
const v = asF64(emit(x))
|
|
312
|
+
const t = temp('t')
|
|
313
|
+
return typed(['i32.and',
|
|
314
|
+
['i32.and',
|
|
315
|
+
['f64.eq', ['local.tee', `$${t}`, v], ['local.get', `$${t}`]],
|
|
316
|
+
['f64.lt', ['f64.abs', ['local.get', `$${t}`]], ['f64.const', Infinity]]],
|
|
317
|
+
['f64.eq', ['local.get', `$${t}`], ['f64.trunc', ['local.get', `$${t}`]]]], 'i32')
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// parseInt(str, radix) — parse string to integer
|
|
321
|
+
ctx.core.stdlib['__parseInt'] = `(func $__parseInt (param $str f64) (param $radix i32) (result f64)
|
|
322
|
+
(local $off i32) (local $len i32) (local $i i32) (local $c i32) (local $neg i32)
|
|
323
|
+
(local $result f64) (local $digit i32) (local $seen i32)
|
|
324
|
+
;; If input is a number, just truncate
|
|
325
|
+
(if (f64.eq (local.get $str) (local.get $str)) (then (return (f64.trunc (local.get $str)))))
|
|
326
|
+
;; If NaN-boxed but not a string type (4=heap,5=SSO) → return NaN
|
|
327
|
+
(if (i32.and
|
|
328
|
+
(i32.ne (call $__ptr_type (local.get $str)) (i32.const 4))
|
|
329
|
+
(i32.ne (call $__ptr_type (local.get $str)) (i32.const 5)))
|
|
330
|
+
(then (return (f64.const nan))))
|
|
331
|
+
(local.set $off (call $__ptr_offset (local.get $str)))
|
|
332
|
+
(local.set $len (call $__str_byteLen (local.get $str)))
|
|
333
|
+
(local.set $i (i32.const 0))
|
|
334
|
+
;; Skip whitespace
|
|
335
|
+
(block $ws (loop $wsl
|
|
336
|
+
(br_if $ws (i32.ge_s (local.get $i) (local.get $len)))
|
|
337
|
+
(br_if $ws (i32.gt_s (call $__char_at (local.get $str) (local.get $i)) (i32.const 32)))
|
|
338
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
339
|
+
(br $wsl)))
|
|
340
|
+
;; Sign
|
|
341
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
342
|
+
(i32.eq (call $__char_at (local.get $str) (local.get $i)) (i32.const 45)))
|
|
343
|
+
(then (local.set $neg (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
344
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
345
|
+
(i32.eq (call $__char_at (local.get $str) (local.get $i)) (i32.const 43)))
|
|
346
|
+
(then (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
347
|
+
;; 0x prefix → radix 16
|
|
348
|
+
(if (i32.and (i32.eqz (local.get $radix))
|
|
349
|
+
(i32.and (i32.le_s (i32.add (local.get $i) (i32.const 1)) (local.get $len))
|
|
350
|
+
(i32.and (i32.eq (call $__char_at (local.get $str) (local.get $i)) (i32.const 48))
|
|
351
|
+
(i32.or (i32.eq (call $__char_at (local.get $str) (i32.add (local.get $i) (i32.const 1))) (i32.const 120))
|
|
352
|
+
(i32.eq (call $__char_at (local.get $str) (i32.add (local.get $i) (i32.const 1))) (i32.const 88))))))
|
|
353
|
+
(then (local.set $radix (i32.const 16)) (local.set $i (i32.add (local.get $i) (i32.const 2)))))
|
|
354
|
+
(if (i32.eqz (local.get $radix)) (then (local.set $radix (i32.const 10))))
|
|
355
|
+
;; Parse digits
|
|
356
|
+
(local.set $result (f64.const 0))
|
|
357
|
+
(block $done (loop $lp
|
|
358
|
+
(br_if $done (i32.ge_s (local.get $i) (local.get $len)))
|
|
359
|
+
(local.set $c (call $__char_at (local.get $str) (local.get $i)))
|
|
360
|
+
;; Digit value
|
|
361
|
+
(local.set $digit (i32.const -1))
|
|
362
|
+
(if (i32.and (i32.ge_s (local.get $c) (i32.const 48)) (i32.le_s (local.get $c) (i32.const 57)))
|
|
363
|
+
(then (local.set $digit (i32.sub (local.get $c) (i32.const 48)))))
|
|
364
|
+
(if (i32.and (i32.ge_s (local.get $c) (i32.const 97)) (i32.le_s (local.get $c) (i32.const 122)))
|
|
365
|
+
(then (local.set $digit (i32.sub (local.get $c) (i32.const 87)))))
|
|
366
|
+
(if (i32.and (i32.ge_s (local.get $c) (i32.const 65)) (i32.le_s (local.get $c) (i32.const 90)))
|
|
367
|
+
(then (local.set $digit (i32.sub (local.get $c) (i32.const 55)))))
|
|
368
|
+
(br_if $done (i32.or (i32.lt_s (local.get $digit) (i32.const 0)) (i32.ge_s (local.get $digit) (local.get $radix))))
|
|
369
|
+
(local.set $seen (i32.const 1))
|
|
370
|
+
(local.set $result (f64.add (f64.mul (local.get $result) (f64.convert_i32_s (local.get $radix))) (f64.convert_i32_s (local.get $digit))))
|
|
371
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
372
|
+
(br $lp)))
|
|
373
|
+
;; No digits consumed → NaN
|
|
374
|
+
(if (i32.eqz (local.get $seen)) (then (return (f64.const nan))))
|
|
375
|
+
(if (result f64) (local.get $neg) (then (f64.neg (local.get $result))) (else (local.get $result))))`
|
|
376
|
+
|
|
377
|
+
ctx.core.stdlib['__to_num'] = `(func $__to_num (param $v f64) (result f64)
|
|
378
|
+
(local $t i32) (local $len i32) (local $i i32) (local $c i32) (local $neg i32)
|
|
379
|
+
(local $seen i32) (local $exp i32) (local $expNeg i32)
|
|
380
|
+
(local $result f64) (local $scale f64)
|
|
381
|
+
(if (f64.eq (local.get $v) (local.get $v)) (then (return (local.get $v))))
|
|
382
|
+
(if (i64.eq (i64.reinterpret_f64 (local.get $v)) (i64.const ${NULL_NAN})) (then (return (f64.const 0))))
|
|
383
|
+
(if (i64.eq (i64.reinterpret_f64 (local.get $v)) (i64.const ${UNDEF_NAN})) (then (return (f64.const nan))))
|
|
384
|
+
(local.set $t (call $__ptr_type (local.get $v)))
|
|
385
|
+
(if (i32.eqz
|
|
386
|
+
(i32.or
|
|
387
|
+
(i32.eq (local.get $t) (i32.const ${PTR.STRING}))
|
|
388
|
+
(i32.eq (local.get $t) (i32.const ${PTR.SSO}))))
|
|
389
|
+
(then (return (f64.const nan))))
|
|
390
|
+
(local.set $len (call $__str_byteLen (local.get $v)))
|
|
391
|
+
;; Skip leading whitespace.
|
|
392
|
+
(block $ws (loop $wsl
|
|
393
|
+
(br_if $ws (i32.ge_s (local.get $i) (local.get $len)))
|
|
394
|
+
(br_if $ws (i32.gt_s (call $__char_at (local.get $v) (local.get $i)) (i32.const 32)))
|
|
395
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
396
|
+
(br $wsl)))
|
|
397
|
+
;; Sign.
|
|
398
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
399
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 45)))
|
|
400
|
+
(then (local.set $neg (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
401
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
402
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 43)))
|
|
403
|
+
(then (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
404
|
+
;; 0x prefix → hex parse and early return
|
|
405
|
+
(if (i32.and
|
|
406
|
+
(i32.le_s (i32.add (local.get $i) (i32.const 1)) (local.get $len))
|
|
407
|
+
(i32.and (i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 48))
|
|
408
|
+
(i32.or (i32.eq (call $__char_at (local.get $v) (i32.add (local.get $i) (i32.const 1))) (i32.const 120))
|
|
409
|
+
(i32.eq (call $__char_at (local.get $v) (i32.add (local.get $i) (i32.const 1))) (i32.const 88)))))
|
|
410
|
+
(then
|
|
411
|
+
(local.set $i (i32.add (local.get $i) (i32.const 2)))
|
|
412
|
+
(block $hexDone (loop $hexLoop
|
|
413
|
+
(br_if $hexDone (i32.ge_s (local.get $i) (local.get $len)))
|
|
414
|
+
(local.set $c (call $__char_at (local.get $v) (local.get $i)))
|
|
415
|
+
(if (i32.and (i32.ge_s (local.get $c) (i32.const 48)) (i32.le_s (local.get $c) (i32.const 57)))
|
|
416
|
+
(then (local.set $result (f64.add (f64.mul (local.get $result) (f64.const 16)) (f64.convert_i32_s (i32.sub (local.get $c) (i32.const 48)))))
|
|
417
|
+
(local.set $seen (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1))) (br $hexLoop)))
|
|
418
|
+
(if (i32.and (i32.ge_s (local.get $c) (i32.const 97)) (i32.le_s (local.get $c) (i32.const 102)))
|
|
419
|
+
(then (local.set $result (f64.add (f64.mul (local.get $result) (f64.const 16)) (f64.convert_i32_s (i32.sub (local.get $c) (i32.const 87)))))
|
|
420
|
+
(local.set $seen (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1))) (br $hexLoop)))
|
|
421
|
+
(if (i32.and (i32.ge_s (local.get $c) (i32.const 65)) (i32.le_s (local.get $c) (i32.const 70)))
|
|
422
|
+
(then (local.set $result (f64.add (f64.mul (local.get $result) (f64.const 16)) (f64.convert_i32_s (i32.sub (local.get $c) (i32.const 55)))))
|
|
423
|
+
(local.set $seen (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1))) (br $hexLoop)))))
|
|
424
|
+
(return (if (result f64) (local.get $neg) (then (f64.neg (local.get $result))) (else (local.get $result))))))
|
|
425
|
+
;; Integer part.
|
|
426
|
+
(block $intDone (loop $intLoop
|
|
427
|
+
(br_if $intDone (i32.ge_s (local.get $i) (local.get $len)))
|
|
428
|
+
(local.set $c (call $__char_at (local.get $v) (local.get $i)))
|
|
429
|
+
(br_if $intDone
|
|
430
|
+
(i32.or
|
|
431
|
+
(i32.lt_s (local.get $c) (i32.const 48))
|
|
432
|
+
(i32.gt_s (local.get $c) (i32.const 57))))
|
|
433
|
+
(local.set $result
|
|
434
|
+
(f64.add
|
|
435
|
+
(f64.mul (local.get $result) (f64.const 10))
|
|
436
|
+
(f64.convert_i32_s (i32.sub (local.get $c) (i32.const 48)))))
|
|
437
|
+
(local.set $seen (i32.const 1))
|
|
438
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
439
|
+
(br $intLoop)))
|
|
440
|
+
;; Fractional part.
|
|
441
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
442
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 46)))
|
|
443
|
+
(then
|
|
444
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
445
|
+
(local.set $scale (f64.const 0.1))
|
|
446
|
+
(block $fracDone (loop $fracLoop
|
|
447
|
+
(br_if $fracDone (i32.ge_s (local.get $i) (local.get $len)))
|
|
448
|
+
(local.set $c (call $__char_at (local.get $v) (local.get $i)))
|
|
449
|
+
(br_if $fracDone
|
|
450
|
+
(i32.or
|
|
451
|
+
(i32.lt_s (local.get $c) (i32.const 48))
|
|
452
|
+
(i32.gt_s (local.get $c) (i32.const 57))))
|
|
453
|
+
(local.set $result
|
|
454
|
+
(f64.add
|
|
455
|
+
(local.get $result)
|
|
456
|
+
(f64.mul
|
|
457
|
+
(f64.convert_i32_s (i32.sub (local.get $c) (i32.const 48)))
|
|
458
|
+
(local.get $scale))))
|
|
459
|
+
(local.set $scale (f64.mul (local.get $scale) (f64.const 0.1)))
|
|
460
|
+
(local.set $seen (i32.const 1))
|
|
461
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
462
|
+
(br $fracLoop)))))
|
|
463
|
+
(if (i32.eqz (local.get $seen)) (then (return (f64.const nan))))
|
|
464
|
+
;; Scientific notation.
|
|
465
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
466
|
+
(i32.or
|
|
467
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 101))
|
|
468
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 69))))
|
|
469
|
+
(then
|
|
470
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
471
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
472
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 45)))
|
|
473
|
+
(then (local.set $expNeg (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
474
|
+
(if (i32.and (i32.lt_s (local.get $i) (local.get $len))
|
|
475
|
+
(i32.eq (call $__char_at (local.get $v) (local.get $i)) (i32.const 43)))
|
|
476
|
+
(then (local.set $i (i32.add (local.get $i) (i32.const 1)))))
|
|
477
|
+
(block $expDone (loop $expLoop
|
|
478
|
+
(br_if $expDone (i32.ge_s (local.get $i) (local.get $len)))
|
|
479
|
+
(local.set $c (call $__char_at (local.get $v) (local.get $i)))
|
|
480
|
+
(br_if $expDone
|
|
481
|
+
(i32.or
|
|
482
|
+
(i32.lt_s (local.get $c) (i32.const 48))
|
|
483
|
+
(i32.gt_s (local.get $c) (i32.const 57))))
|
|
484
|
+
(local.set $exp
|
|
485
|
+
(i32.add
|
|
486
|
+
(i32.mul (local.get $exp) (i32.const 10))
|
|
487
|
+
(i32.sub (local.get $c) (i32.const 48))))
|
|
488
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
489
|
+
(br $expLoop)))
|
|
490
|
+
(if (local.get $expNeg)
|
|
491
|
+
(then (local.set $result (f64.div (local.get $result) (call $__pow10 (local.get $exp)))))
|
|
492
|
+
(else (local.set $result (f64.mul (local.get $result) (call $__pow10 (local.get $exp))))))))
|
|
493
|
+
(if (result f64) (local.get $neg) (then (f64.neg (local.get $result))) (else (local.get $result))))`
|
|
494
|
+
|
|
495
|
+
ctx.core.emit['Number.parseInt'] = (x, radix) => {
|
|
496
|
+
inc('__parseInt')
|
|
497
|
+
return typed(['call', '$__parseInt', asF64(emit(x)), radix ? asI32(emit(radix)) : ['i32.const', 0]], 'f64')
|
|
498
|
+
}
|
|
499
|
+
ctx.core.emit['parseInt'] = ctx.core.emit['Number.parseInt']
|
|
500
|
+
ctx.core.emit['Number.parseFloat'] = (x) => {
|
|
501
|
+
inc('__to_num')
|
|
502
|
+
return typed(['call', '$__to_num', asF64(emit(x))], 'f64')
|
|
503
|
+
}
|
|
504
|
+
ctx.core.emit['parseFloat'] = ctx.core.emit['Number.parseFloat']
|
|
505
|
+
|
|
506
|
+
// Boolean(x) → truthiness (non-zero → 1, zero → 0)
|
|
507
|
+
ctx.core.emit['Boolean'] = (x) => {
|
|
508
|
+
inc('__is_truthy')
|
|
509
|
+
const v = asF64(emit(x))
|
|
510
|
+
return typed(['if', ['result', 'f64'], ['call', '$__is_truthy', v], ['then', ['f64.const', 1]], ['else', ['f64.const', 0]]], 'f64')
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// === Instance method emitters ===
|
|
514
|
+
|
|
515
|
+
ctx.core.emit['.number:toString'] = (n) => {
|
|
516
|
+
inc('__ftoa')
|
|
517
|
+
return typed(['call', '$__ftoa', asF64(emit(n)), ['i32.const', 0], ['i32.const', 0]], 'f64')
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
ctx.core.emit['.number:toFixed'] = (n, d) => {
|
|
521
|
+
inc('__ftoa')
|
|
522
|
+
return typed(['call', '$__ftoa', asF64(emit(n)), asI32(emit(d || [, 0])), ['i32.const', 1]], 'f64')
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
ctx.core.emit['.number:toExponential'] = (n, d) => {
|
|
526
|
+
inc('__toExp')
|
|
527
|
+
return typed(['call', '$__toExp', asF64(emit(n)), asI32(emit(d || [, 0]))], 'f64')
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
ctx.core.emit['.number:toPrecision'] = (n, p) => {
|
|
531
|
+
inc('__ftoa', '__toExp')
|
|
532
|
+
const val = temp('pv'), t = temp('tp'), exp = tempI32('te'), pr = tempI32('pp')
|
|
533
|
+
return typed(['block', ['result', 'f64'],
|
|
534
|
+
['local.set', `$${val}`, asF64(emit(n))],
|
|
535
|
+
['local.set', `$${pr}`, asI32(emit(p))],
|
|
536
|
+
['local.set', `$${t}`, ['f64.abs', ['local.get', `$${val}`]]],
|
|
537
|
+
['local.set', `$${exp}`, ['i32.const', 0]],
|
|
538
|
+
['if', ['f64.gt', ['local.get', `$${t}`], ['f64.const', 0]],
|
|
539
|
+
['then',
|
|
540
|
+
['block', '$d1', ['loop', '$l1',
|
|
541
|
+
['br_if', '$d1', ['f64.lt', ['local.get', `$${t}`], ['f64.const', 10]]],
|
|
542
|
+
['local.set', `$${t}`, ['f64.div', ['local.get', `$${t}`], ['f64.const', 10]]],
|
|
543
|
+
['local.set', `$${exp}`, ['i32.add', ['local.get', `$${exp}`], ['i32.const', 1]]],
|
|
544
|
+
['br', '$l1']]],
|
|
545
|
+
['block', '$d2', ['loop', '$l2',
|
|
546
|
+
['br_if', '$d2', ['f64.ge', ['local.get', `$${t}`], ['f64.const', 1]]],
|
|
547
|
+
['local.set', `$${t}`, ['f64.mul', ['local.get', `$${t}`], ['f64.const', 10]]],
|
|
548
|
+
['local.set', `$${exp}`, ['i32.sub', ['local.get', `$${exp}`], ['i32.const', 1]]],
|
|
549
|
+
['br', '$l2']]]]],
|
|
550
|
+
['if', ['result', 'f64'],
|
|
551
|
+
['i32.or',
|
|
552
|
+
['i32.lt_s', ['local.get', `$${exp}`], ['i32.const', -6]],
|
|
553
|
+
['i32.ge_s', ['local.get', `$${exp}`], ['local.get', `$${pr}`]]],
|
|
554
|
+
['then', ['call', '$__toExp', ['local.get', `$${val}`], ['i32.sub', ['local.get', `$${pr}`], ['i32.const', 1]]]],
|
|
555
|
+
['else', ['call', '$__ftoa', ['local.get', `$${val}`],
|
|
556
|
+
['i32.sub', ['i32.sub', ['local.get', `$${pr}`], ['i32.const', 1]], ['local.get', `$${exp}`]],
|
|
557
|
+
['i32.const', 1]]]]], 'f64')
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
ctx.core.emit['String'] = (x) => {
|
|
561
|
+
inc('__ftoa')
|
|
562
|
+
if (Array.isArray(x) && x[0] === 'str') return emit(x)
|
|
563
|
+
return typed(['call', '$__ftoa', asF64(emit(x)), ['i32.const', 0], ['i32.const', 0]], 'f64')
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Number(x) — identity for numbers, i64→f64 conversion for BigInt
|
|
567
|
+
ctx.core.emit['Number'] = (x) => {
|
|
568
|
+
if (valTypeOf(x) === VAL.BIGINT)
|
|
569
|
+
return typed(['f64.convert_i64_s', asI64(emit(x))], 'f64')
|
|
570
|
+
inc('__to_num')
|
|
571
|
+
return typed(['call', '$__to_num', asF64(emit(x))], 'f64')
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// BigInt(x) — f64→i64 conversion (reinterpret as BigInt-as-f64).
|
|
575
|
+
// For number input: truncate directly. For string / unknown: first coerce via __to_num
|
|
576
|
+
// (handles both decimal and hex string parse), then truncate.
|
|
577
|
+
ctx.core.emit['BigInt'] = (x) => {
|
|
578
|
+
const vt = valTypeOf(x)
|
|
579
|
+
if (vt === VAL.BIGINT) return emit(x)
|
|
580
|
+
if (vt === VAL.NUMBER)
|
|
581
|
+
return typed(['f64.reinterpret_i64', ['i64.trunc_sat_f64_s', asF64(emit(x))]], 'f64')
|
|
582
|
+
inc('__to_num')
|
|
583
|
+
return typed(['f64.reinterpret_i64',
|
|
584
|
+
['i64.trunc_sat_f64_s', ['call', '$__to_num', asF64(emit(x))]]], 'f64')
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// BigInt.asIntN(bits, bigint) — truncate to signed N-bit
|
|
588
|
+
ctx.core.emit['BigInt.asIntN'] = (bits, val) => {
|
|
589
|
+
const vbits = asI32(emit(bits)), vval = asI64(emit(val))
|
|
590
|
+
// (val << (64 - bits)) >> (64 - bits) — arithmetic shift for sign extension
|
|
591
|
+
const shift = typed(['i64.sub', ['i64.const', 64], ['i64.extend_i32_s', vbits]], 'i64')
|
|
592
|
+
const t = tempI64('bi')
|
|
593
|
+
return typed(['f64.reinterpret_i64', ['block', ['result', 'i64'],
|
|
594
|
+
['local.set', `$${t}`, shift],
|
|
595
|
+
['i64.shr_s', ['i64.shl', vval, ['local.get', `$${t}`]], ['local.get', `$${t}`]]]], 'f64')
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// BigInt.asUintN(bits, bigint) — truncate to unsigned N-bit
|
|
599
|
+
ctx.core.emit['BigInt.asUintN'] = (bits, val) => {
|
|
600
|
+
const vbits = asI32(emit(bits)), vval = asI64(emit(val))
|
|
601
|
+
// val & ((1 << bits) - 1)
|
|
602
|
+
return typed(['f64.reinterpret_i64',
|
|
603
|
+
['i64.and', vval, ['i64.sub', ['i64.shl', ['i64.const', 1], ['i64.extend_i32_s', vbits]], ['i64.const', 1]]]], 'f64')
|
|
604
|
+
}
|
|
605
|
+
}
|