jz 0.1.1 → 0.2.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 +256 -90
- package/cli.js +34 -8
- package/index.js +100 -14
- package/module/array.js +396 -133
- package/module/collection.js +455 -212
- package/module/console.js +169 -88
- package/module/core.js +246 -144
- package/module/date.js +783 -0
- package/module/function.js +81 -21
- package/module/index.js +2 -1
- package/module/json.js +483 -138
- package/module/math.js +79 -39
- package/module/number.js +188 -78
- package/module/object.js +227 -47
- package/module/regex.js +33 -30
- package/module/schema.js +14 -17
- package/module/string.js +439 -235
- package/module/symbol.js +4 -3
- package/module/timer.js +89 -31
- package/module/typedarray.js +174 -44
- package/package.json +3 -10
- package/src/analyze.js +627 -133
- package/src/autoload.js +15 -7
- package/src/compile.js +399 -70
- package/src/ctx.js +41 -12
- package/src/emit.js +658 -145
- package/src/host.js +244 -51
- package/src/ir.js +87 -31
- package/src/jzify.js +181 -24
- package/src/narrow.js +32 -4
- package/src/optimize.js +628 -42
- package/src/plan.js +1167 -4
- package/src/prepare.js +322 -75
- package/src/vectorize.js +1016 -0
- package/wasi.js +13 -0
- package/src/key.js +0 -73
- package/src/source.js +0 -76
package/module/string.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* String module — literals, char access, and string methods.
|
|
3
3
|
*
|
|
4
|
-
* Type=4 (STRING)
|
|
5
|
-
*
|
|
4
|
+
* Type=4 (STRING) covers both encodings; aux bit LAYOUT.SSO_BIT discriminates:
|
|
5
|
+
* bit clear: heap-allocated, length in header [-4:len], offset → bytes.
|
|
6
|
+
* bit set: inline ≤4 ASCII chars packed in offset (no memory),
|
|
7
|
+
* length in aux low bits (0..4).
|
|
6
8
|
*
|
|
7
9
|
* Methods use type-qualified keys (.string:slice) for array-colliding names,
|
|
8
10
|
* generic keys (.toUpperCase) for non-colliding ones.
|
|
@@ -10,16 +12,21 @@
|
|
|
10
12
|
* @module string
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
|
-
import { typed, asF64, asI32, NULL_NAN, UNDEF_NAN, mkPtrIR, temp, tempI32 } from '../src/ir.js'
|
|
15
|
+
import { typed, asF64, asI32, asI64, NULL_NAN, UNDEF_NAN, mkPtrIR, temp, tempI32 } from '../src/ir.js'
|
|
14
16
|
import { emit } from '../src/emit.js'
|
|
15
17
|
import { valTypeOf, VAL } from '../src/analyze.js'
|
|
16
|
-
import { inc, PTR } from '../src/ctx.js'
|
|
18
|
+
import { inc, PTR, LAYOUT } from '../src/ctx.js'
|
|
19
|
+
|
|
20
|
+
// SSO discriminator bit pre-shifted to its slot in the full i64 ptr (bit 46).
|
|
21
|
+
// Used as `i64.and ptr SSO_BIT_I64` for branch-without-extracting-aux.
|
|
22
|
+
const SSO_BIT_I64 = '0x' + (BigInt(LAYOUT.SSO_BIT) << BigInt(LAYOUT.AUX_SHIFT)).toString(16).toUpperCase().padStart(16, '0')
|
|
17
23
|
|
|
18
24
|
|
|
19
25
|
export default (ctx) => {
|
|
20
26
|
Object.assign(ctx.core.stdlibDeps, {
|
|
21
27
|
__str_concat: ['__to_str', '__str_byteLen', '__alloc', '__mkptr', '__str_copy'],
|
|
22
28
|
__str_concat_raw: ['__str_byteLen', '__alloc', '__mkptr', '__str_copy'],
|
|
29
|
+
__str_append_byte: ['__str_byteLen', '__alloc', '__mkptr', '__str_copy'],
|
|
23
30
|
__str_copy: [],
|
|
24
31
|
__str_slice: ['__str_byteLen', '__alloc'],
|
|
25
32
|
__str_indexof: ['__str_byteLen'],
|
|
@@ -34,8 +41,9 @@ export default (ctx) => {
|
|
|
34
41
|
__str_replace: ['__str_indexof', '__str_slice', '__str_concat'],
|
|
35
42
|
__str_replaceall: ['__str_indexof', '__str_slice', '__str_concat'],
|
|
36
43
|
__str_split: ['__str_slice', '__str_byteLen', '__char_at', '__alloc'],
|
|
37
|
-
__str_idx: [
|
|
44
|
+
__str_idx: [],
|
|
38
45
|
__str_eq: ['__char_at'],
|
|
46
|
+
__str_cmp: ['__char_at', '__str_byteLen'],
|
|
39
47
|
__str_pad: ['__str_byteLen', '__str_copy', '__alloc'],
|
|
40
48
|
__str_join: ['__str_concat', '__to_str', '__str_byteLen', '__len', '__ptr_offset'],
|
|
41
49
|
__str_encode: ['__str_byteLen', '__str_copy'],
|
|
@@ -52,7 +60,7 @@ export default (ctx) => {
|
|
|
52
60
|
if (ctx.features.sso && str.length <= MAX_SSO && /^[\x00-\x7f]*$/.test(str)) {
|
|
53
61
|
let packed = 0
|
|
54
62
|
for (let i = 0; i < str.length; i++) packed |= str.charCodeAt(i) << (i * 8)
|
|
55
|
-
return mkPtrIR(PTR.
|
|
63
|
+
return mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | str.length, packed)
|
|
56
64
|
}
|
|
57
65
|
const bytes = new TextEncoder().encode(str)
|
|
58
66
|
const len = bytes.length
|
|
@@ -91,28 +99,25 @@ export default (ctx) => {
|
|
|
91
99
|
|
|
92
100
|
// SSO/STRING ptrs never have forwarding pointers (only ARRAY does), so we extract
|
|
93
101
|
// the raw offset directly instead of paying the __ptr_offset function-call overhead.
|
|
94
|
-
ctx.core.stdlib['__sso_char'] = `(func $__sso_char (param $ptr
|
|
102
|
+
ctx.core.stdlib['__sso_char'] = `(func $__sso_char (param $ptr i64) (param $i i32) (result i32)
|
|
95
103
|
(i32.and
|
|
96
104
|
(i32.shr_u
|
|
97
|
-
(i32.wrap_i64 (i64.and (
|
|
105
|
+
(i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK})))
|
|
98
106
|
(i32.mul (local.get $i) (i32.const 8)))
|
|
99
107
|
(i32.const 0xFF)))`
|
|
100
108
|
|
|
101
|
-
ctx.core.stdlib['__str_char'] = `(func $__str_char (param $ptr
|
|
109
|
+
ctx.core.stdlib['__str_char'] = `(func $__str_char (param $ptr i64) (param $i i32) (result i32)
|
|
102
110
|
(i32.load8_u (i32.add
|
|
103
|
-
(i32.wrap_i64 (i64.and (
|
|
111
|
+
(i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK})))
|
|
104
112
|
(local.get $i))))`
|
|
105
113
|
|
|
106
|
-
// Hot (~37M calls in watr self-host).
|
|
107
|
-
// SSO
|
|
108
|
-
ctx.core.stdlib['__char_at'] = `(func $__char_at (param $ptr
|
|
109
|
-
(local $
|
|
110
|
-
(local.set $
|
|
111
|
-
(local.set $off (i32.wrap_i64 (i64.and (local.get $bits) (i64.const 0xFFFFFFFF))))
|
|
114
|
+
// Hot (~37M calls in watr self-host). Caller guarantees $ptr is a STRING;
|
|
115
|
+
// SSO bit picks inline-byte-extract vs heap memory load.
|
|
116
|
+
ctx.core.stdlib['__char_at'] = `(func $__char_at (param $ptr i64) (param $i i32) (result i32)
|
|
117
|
+
(local $off i32)
|
|
118
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
112
119
|
(if (result i32)
|
|
113
|
-
(
|
|
114
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $bits) (i64.const 47)) (i64.const 0xF)))
|
|
115
|
-
(i32.const ${PTR.SSO}))
|
|
120
|
+
(i64.ne (i64.and (local.get $ptr) (i64.const ${SSO_BIT_I64})) (i64.const 0))
|
|
116
121
|
(then
|
|
117
122
|
(i32.and
|
|
118
123
|
(i32.shr_u (local.get $off) (i32.mul (local.get $i) (i32.const 8)))
|
|
@@ -120,44 +125,66 @@ export default (ctx) => {
|
|
|
120
125
|
(else
|
|
121
126
|
(i32.load8_u (i32.add (local.get $off) (local.get $i))))))`
|
|
122
127
|
|
|
123
|
-
ctx.core.stdlib['__str_idx'] = `(func $__str_idx (param $ptr
|
|
124
|
-
(local $len i32)
|
|
125
|
-
(local.set $
|
|
128
|
+
ctx.core.stdlib['__str_idx'] = `(func $__str_idx (param $ptr i64) (param $i i32) (result f64)
|
|
129
|
+
(local $t i32) (local $off i32) (local $len i32) (local $isSso i32)
|
|
130
|
+
(local.set $t (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
131
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
132
|
+
(local.set $isSso (i32.and
|
|
133
|
+
(i32.wrap_i64 (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
134
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
135
|
+
(local.set $len
|
|
136
|
+
(if (result i32) (local.get $isSso)
|
|
137
|
+
(then (i32.and
|
|
138
|
+
(i32.wrap_i64 (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
139
|
+
(i32.const ${LAYOUT.SSO_BIT - 1})))
|
|
140
|
+
(else
|
|
141
|
+
(if (result i32) (i32.and (i32.eq (local.get $t) (i32.const ${PTR.STRING})) (i32.ge_u (local.get $off) (i32.const 4)))
|
|
142
|
+
(then (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
143
|
+
(else (i32.const 0))))))
|
|
126
144
|
(if (result f64)
|
|
127
|
-
(i32.or
|
|
128
|
-
(i32.lt_s (local.get $i) (i32.const 0))
|
|
129
|
-
(i32.ge_u (local.get $i) (local.get $len)))
|
|
145
|
+
(i32.or (i32.lt_s (local.get $i) (i32.const 0)) (i32.ge_u (local.get $i) (local.get $len)))
|
|
130
146
|
(then (f64.const nan:${UNDEF_NAN}))
|
|
131
147
|
(else
|
|
132
|
-
(
|
|
133
|
-
(
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
(f64.reinterpret_i64
|
|
149
|
+
(i64.or
|
|
150
|
+
;; mkptr(STRING, SSO_BIT|1, 0) = NAN_PREFIX | (STRING<<TAG_SHIFT) | ((SSO_BIT|1)<<AUX_SHIFT)
|
|
151
|
+
(i64.const ${'0x' + (LAYOUT.NAN_PREFIX_BITS | (BigInt(PTR.STRING) << BigInt(LAYOUT.TAG_SHIFT)) | ((BigInt(LAYOUT.SSO_BIT) | 1n) << BigInt(LAYOUT.AUX_SHIFT))).toString(16).toUpperCase().padStart(16, '0')})
|
|
152
|
+
(i64.extend_i32_u
|
|
153
|
+
(if (result i32) (local.get $isSso)
|
|
154
|
+
(then (i32.and (i32.shr_u (local.get $off) (i32.mul (local.get $i) (i32.const 8))) (i32.const 0xFF)))
|
|
155
|
+
(else (i32.load8_u (i32.add (local.get $off) (local.get $i)))))))))))`
|
|
136
156
|
|
|
137
157
|
// Hot: ~53M calls in watr self-host. Bit-eq covers identity. SSO/SSO with !bit-eq
|
|
138
158
|
// guarantees content differs (high 32 bits encode type+len; both equal → low 32 differs
|
|
139
|
-
// ⇒ bytes differ).
|
|
140
|
-
// Mixed
|
|
141
|
-
ctx.core.stdlib['__str_eq'] = `(func $__str_eq (param $a
|
|
159
|
+
// ⇒ bytes differ). Heap/heap uses raw load8_u — no per-byte function calls.
|
|
160
|
+
// Mixed SSO×heap is rare; falls back to __char_at.
|
|
161
|
+
ctx.core.stdlib['__str_eq'] = `(func $__str_eq (param $a i64) (param $b i64) (result i32)
|
|
142
162
|
(local $len i32) (local $lenB i32) (local $i i32)
|
|
143
|
-
(local $
|
|
163
|
+
(local $ta i32) (local $tb i32)
|
|
144
164
|
(local $offA i32) (local $offB i32)
|
|
145
|
-
(local
|
|
146
|
-
(
|
|
147
|
-
(if (i64.eq (local.get $ba) (local.get $bb))
|
|
165
|
+
(local $ssoA i32) (local $ssoB i32)
|
|
166
|
+
(if (i64.eq (local.get $a) (local.get $b))
|
|
148
167
|
(then (return (i32.const 1))))
|
|
149
|
-
(local.set $ta (i32.wrap_i64 (i64.and (i64.shr_u (local.get $
|
|
150
|
-
(local.set $tb (i32.wrap_i64 (i64.and (i64.shr_u (local.get $
|
|
151
|
-
(local.set $offA (i32.wrap_i64 (i64.and (local.get $
|
|
152
|
-
(local.set $offB (i32.wrap_i64 (i64.and (local.get $
|
|
153
|
-
|
|
154
|
-
|
|
168
|
+
(local.set $ta (i32.wrap_i64 (i64.and (i64.shr_u (local.get $a) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
169
|
+
(local.set $tb (i32.wrap_i64 (i64.and (i64.shr_u (local.get $b) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
170
|
+
(local.set $offA (i32.wrap_i64 (i64.and (local.get $a) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
171
|
+
(local.set $offB (i32.wrap_i64 (i64.and (local.get $b) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
172
|
+
(local.set $ssoA (i32.and
|
|
173
|
+
(i32.wrap_i64 (i64.shr_u (local.get $a) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
174
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
175
|
+
(local.set $ssoB (i32.and
|
|
176
|
+
(i32.wrap_i64 (i64.shr_u (local.get $b) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
177
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
178
|
+
;; Both SSO with !bit-eq ⇒ content differs (high 32 bits hold tag+aux; both equal here).
|
|
179
|
+
(if (i32.and (local.get $ssoA) (local.get $ssoB))
|
|
155
180
|
(then (return (i32.const 0))))
|
|
156
|
-
;; Both STRING fast path: inline len from header. Chunk by 4 bytes via unaligned
|
|
157
|
-
;; (wasm guarantees unaligned-OK), then byte-tail. Most string comparisons fail
|
|
158
|
-
;; the first 4-byte word, so this collapses the per-byte branch overhead into a
|
|
159
|
-
;; 32-bit equality.
|
|
160
|
-
(if (i32.and
|
|
181
|
+
;; Both heap STRING fast path: inline len from header. Chunk by 4 bytes via unaligned
|
|
182
|
+
;; i32.load (wasm guarantees unaligned-OK), then byte-tail. Most string comparisons fail
|
|
183
|
+
;; early on the first 4-byte word, so this collapses the per-byte branch overhead into a
|
|
184
|
+
;; single 32-bit equality.
|
|
185
|
+
(if (i32.and
|
|
186
|
+
(i32.and (i32.eq (local.get $ta) (i32.const ${PTR.STRING})) (i32.eqz (local.get $ssoA)))
|
|
187
|
+
(i32.and (i32.eq (local.get $tb) (i32.const ${PTR.STRING})) (i32.eqz (local.get $ssoB))))
|
|
161
188
|
(then
|
|
162
189
|
(if (i32.or (i32.lt_u (local.get $offA) (i32.const 4)) (i32.lt_u (local.get $offB) (i32.const 4)))
|
|
163
190
|
(then (return (i32.const 0))))
|
|
@@ -183,14 +210,18 @@ export default (ctx) => {
|
|
|
183
210
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
184
211
|
(br $lh)))
|
|
185
212
|
(return (i32.const 1))))
|
|
186
|
-
;; Mixed (
|
|
187
|
-
(if (
|
|
188
|
-
(then (local.set $len (i32.
|
|
213
|
+
;; Mixed (SSO×heap) or anything else: compute len per side then per-byte via __char_at.
|
|
214
|
+
(if (local.get $ssoA)
|
|
215
|
+
(then (local.set $len (i32.and
|
|
216
|
+
(i32.wrap_i64 (i64.shr_u (local.get $a) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
217
|
+
(i32.const ${LAYOUT.SSO_BIT - 1}))))
|
|
189
218
|
(else
|
|
190
219
|
(if (i32.and (i32.eq (local.get $ta) (i32.const ${PTR.STRING})) (i32.ge_u (local.get $offA) (i32.const 4)))
|
|
191
220
|
(then (local.set $len (i32.load (i32.sub (local.get $offA) (i32.const 4))))))))
|
|
192
|
-
(if (
|
|
193
|
-
(then (local.set $lenB (i32.
|
|
221
|
+
(if (local.get $ssoB)
|
|
222
|
+
(then (local.set $lenB (i32.and
|
|
223
|
+
(i32.wrap_i64 (i64.shr_u (local.get $b) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
224
|
+
(i32.const ${LAYOUT.SSO_BIT - 1}))))
|
|
194
225
|
(else
|
|
195
226
|
(if (i32.and (i32.eq (local.get $tb) (i32.const ${PTR.STRING})) (i32.ge_u (local.get $offB) (i32.const 4)))
|
|
196
227
|
(then (local.set $lenB (i32.load (i32.sub (local.get $offB) (i32.const 4))))))))
|
|
@@ -205,28 +236,62 @@ export default (ctx) => {
|
|
|
205
236
|
(br $lm)))
|
|
206
237
|
(i32.const 1))`
|
|
207
238
|
|
|
208
|
-
//
|
|
239
|
+
// Three-way byte-wise compare: -1 if a < b, 0 if equal, +1 if a > b. Returns
|
|
240
|
+
// i32 so callers can `i32.lt_s 0`, `i32.gt_s 0`, etc. without coercion.
|
|
241
|
+
// Comparison is unsigned (i32.load8_u via __char_at) — matches JS spec for
|
|
242
|
+
// ASCII; for non-ASCII it's a UTF-8 byte order, which collates the same as
|
|
243
|
+
// codepoint order for code points < 0x80 and well-formed strings. NOT locale-
|
|
244
|
+
// aware: this is the byte-wise variant suitable for sort-stability use cases,
|
|
245
|
+
// not human-language collation.
|
|
246
|
+
ctx.core.stdlib['__str_cmp'] = `(func $__str_cmp (param $a i64) (param $b i64) (result i32)
|
|
247
|
+
(local $lenA i32) (local $lenB i32) (local $minLen i32) (local $i i32)
|
|
248
|
+
(local $ca i32) (local $cb i32)
|
|
249
|
+
;; Bit-equal pointers (including same SSO inline form) ⇒ identical strings.
|
|
250
|
+
(if (i64.eq (local.get $a) (local.get $b))
|
|
251
|
+
(then (return (i32.const 0))))
|
|
252
|
+
(local.set $lenA (call $__str_byteLen (local.get $a)))
|
|
253
|
+
(local.set $lenB (call $__str_byteLen (local.get $b)))
|
|
254
|
+
(local.set $minLen (select (local.get $lenA) (local.get $lenB)
|
|
255
|
+
(i32.lt_s (local.get $lenA) (local.get $lenB))))
|
|
256
|
+
(block $done (loop $next
|
|
257
|
+
(br_if $done (i32.ge_s (local.get $i) (local.get $minLen)))
|
|
258
|
+
(local.set $ca (call $__char_at (local.get $a) (local.get $i)))
|
|
259
|
+
(local.set $cb (call $__char_at (local.get $b) (local.get $i)))
|
|
260
|
+
(if (i32.lt_u (local.get $ca) (local.get $cb)) (then (return (i32.const -1))))
|
|
261
|
+
(if (i32.gt_u (local.get $ca) (local.get $cb)) (then (return (i32.const 1))))
|
|
262
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
263
|
+
(br $next)))
|
|
264
|
+
;; Common prefix matches — shorter string sorts first.
|
|
265
|
+
(if (i32.lt_s (local.get $lenA) (local.get $lenB)) (then (return (i32.const -1))))
|
|
266
|
+
(if (i32.gt_s (local.get $lenA) (local.get $lenB)) (then (return (i32.const 1))))
|
|
267
|
+
(i32.const 0))`
|
|
268
|
+
|
|
269
|
+
// === WAT: unified byte length (SSO → aux low bits, heap → header) ===
|
|
209
270
|
|
|
210
|
-
ctx.core.stdlib['__str_byteLen'] = `(func $__str_byteLen (param $ptr
|
|
211
|
-
(local $
|
|
212
|
-
(local.set $
|
|
213
|
-
(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
(if (result i32)
|
|
219
|
-
(
|
|
220
|
-
(
|
|
221
|
-
|
|
271
|
+
ctx.core.stdlib['__str_byteLen'] = `(func $__str_byteLen (param $ptr i64) (result i32)
|
|
272
|
+
(local $t i32) (local $off i32) (local $aux i32)
|
|
273
|
+
(local.set $t (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
274
|
+
(if (result i32) (i32.eq (local.get $t) (i32.const ${PTR.STRING}))
|
|
275
|
+
(then
|
|
276
|
+
(local.set $aux (i32.and
|
|
277
|
+
(i32.wrap_i64 (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
278
|
+
(i32.const ${LAYOUT.AUX_MASK})))
|
|
279
|
+
(if (result i32) (i32.and (local.get $aux) (i32.const ${LAYOUT.SSO_BIT}))
|
|
280
|
+
(then (i32.and (local.get $aux) (i32.const ${LAYOUT.SSO_BIT - 1})))
|
|
281
|
+
(else
|
|
282
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
283
|
+
(if (result i32) (i32.ge_u (local.get $off) (i32.const 4))
|
|
284
|
+
(then (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
285
|
+
(else (i32.const 0))))))
|
|
286
|
+
(else (i32.const 0))))`
|
|
222
287
|
|
|
223
288
|
// === WAT: string methods ===
|
|
224
289
|
|
|
225
290
|
// SSO source uses an unrolled byte-extract loop (len ≤ 4); heap source uses memory.copy
|
|
226
291
|
// (single bulk op instead of nlen × __char_at).
|
|
227
|
-
ctx.core.stdlib['__str_slice'] = `(func $__str_slice (param $ptr
|
|
292
|
+
ctx.core.stdlib['__str_slice'] = `(func $__str_slice (param $ptr i64) (param $start i32) (param $end i32) (result f64)
|
|
228
293
|
(local $len i32) (local $nlen i32) (local $off i32) (local $i i32)
|
|
229
|
-
(local $
|
|
294
|
+
(local $srcOff i32) (local $isSso i32)
|
|
230
295
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
231
296
|
(if (i32.lt_s (local.get $start) (i32.const 0))
|
|
232
297
|
(then (local.set $start (i32.add (local.get $len) (local.get $start)))))
|
|
@@ -241,16 +306,15 @@ export default (ctx) => {
|
|
|
241
306
|
(if (i32.gt_s (local.get $end) (local.get $len))
|
|
242
307
|
(then (local.set $end (local.get $len))))
|
|
243
308
|
(if (i32.ge_s (local.get $start) (local.get $end))
|
|
244
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
309
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
245
310
|
(local.set $nlen (i32.sub (local.get $end) (local.get $start)))
|
|
246
311
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $nlen))))
|
|
247
312
|
(i32.store (local.get $off) (local.get $nlen))
|
|
248
313
|
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
249
|
-
(local.set $
|
|
250
|
-
(local.set $
|
|
251
|
-
|
|
252
|
-
(
|
|
253
|
-
(i32.const ${PTR.SSO})))
|
|
314
|
+
(local.set $srcOff (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
315
|
+
(local.set $isSso (i32.wrap_i64 (i64.shr_u
|
|
316
|
+
(i64.and (local.get $ptr) (i64.const ${SSO_BIT_I64}))
|
|
317
|
+
(i64.const ${LAYOUT.AUX_SHIFT}))))
|
|
254
318
|
(if (local.get $isSso)
|
|
255
319
|
(then
|
|
256
320
|
(block $done (loop $loop
|
|
@@ -265,7 +329,7 @@ export default (ctx) => {
|
|
|
265
329
|
(memory.copy (local.get $off) (i32.add (local.get $srcOff) (local.get $start)) (local.get $nlen))))
|
|
266
330
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
267
331
|
|
|
268
|
-
ctx.core.stdlib['__str_substring'] = `(func $__str_substring (param $ptr
|
|
332
|
+
ctx.core.stdlib['__str_substring'] = `(func $__str_substring (param $ptr i64) (param $start i32) (param $end i32) (result f64)
|
|
269
333
|
(local $len i32) (local $tmp i32)
|
|
270
334
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
271
335
|
(if (i32.lt_s (local.get $start) (i32.const 0))
|
|
@@ -285,24 +349,22 @@ export default (ctx) => {
|
|
|
285
349
|
|
|
286
350
|
// Hoist SSO/heap dispatch for hay and ndl out of the inner byte loop. Inner
|
|
287
351
|
// loop becomes (load8_u OR sso byte-extract) per side — no per-byte calls.
|
|
288
|
-
ctx.core.stdlib['__str_indexof'] = `(func $__str_indexof (param $hay
|
|
352
|
+
ctx.core.stdlib['__str_indexof'] = `(func $__str_indexof (param $hay i64) (param $ndl i64) (param $from i32) (result i32)
|
|
289
353
|
(local $hlen i32) (local $nlen i32) (local $i i32) (local $j i32) (local $match i32)
|
|
290
|
-
(local $
|
|
354
|
+
(local $hoff i32) (local $noff i32)
|
|
291
355
|
(local $hsso i32) (local $nsso i32) (local $hb i32) (local $nb i32) (local $k i32)
|
|
292
356
|
(local.set $hlen (call $__str_byteLen (local.get $hay)))
|
|
293
357
|
(local.set $nlen (call $__str_byteLen (local.get $ndl)))
|
|
294
358
|
(if (i32.eqz (local.get $nlen)) (then (return (local.get $from))))
|
|
295
359
|
(if (i32.gt_s (local.get $nlen) (local.get $hlen)) (then (return (i32.const -1))))
|
|
296
|
-
(local.set $
|
|
297
|
-
(local.set $
|
|
298
|
-
(local.set $
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
(i32.const ${
|
|
303
|
-
|
|
304
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $nbits) (i64.const 47)) (i64.const 0xF)))
|
|
305
|
-
(i32.const ${PTR.SSO})))
|
|
360
|
+
(local.set $hoff (i32.wrap_i64 (i64.and (local.get $hay) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
361
|
+
(local.set $noff (i32.wrap_i64 (i64.and (local.get $ndl) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
362
|
+
(local.set $hsso (i32.and
|
|
363
|
+
(i32.wrap_i64 (i64.shr_u (local.get $hay) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
364
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
365
|
+
(local.set $nsso (i32.and
|
|
366
|
+
(i32.wrap_i64 (i64.shr_u (local.get $ndl) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
367
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
306
368
|
(local.set $i (if (result i32) (i32.gt_s (local.get $from) (i32.const 0)) (then (local.get $from)) (else (i32.const 0))))
|
|
307
369
|
(block $done (loop $outer
|
|
308
370
|
(br_if $done (i32.gt_s (local.get $i) (i32.sub (local.get $hlen) (local.get $nlen))))
|
|
@@ -327,22 +389,20 @@ export default (ctx) => {
|
|
|
327
389
|
(i32.const -1))`
|
|
328
390
|
|
|
329
391
|
// SSO/heap dispatch hoisted; inner loop is two inlined byte-fetches and a compare.
|
|
330
|
-
ctx.core.stdlib['__str_startswith'] = `(func $__str_startswith (param $str
|
|
392
|
+
ctx.core.stdlib['__str_startswith'] = `(func $__str_startswith (param $str i64) (param $pfx i64) (result i32)
|
|
331
393
|
(local $plen i32) (local $i i32)
|
|
332
|
-
(local $
|
|
394
|
+
(local $soff i32) (local $poff i32) (local $ssso i32) (local $psso i32)
|
|
333
395
|
(local.set $plen (call $__str_byteLen (local.get $pfx)))
|
|
334
396
|
(if (i32.gt_s (local.get $plen) (call $__str_byteLen (local.get $str)))
|
|
335
397
|
(then (return (i32.const 0))))
|
|
336
|
-
(local.set $
|
|
337
|
-
(local.set $
|
|
338
|
-
(local.set $
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
(i32.const ${
|
|
343
|
-
|
|
344
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $pbits) (i64.const 47)) (i64.const 0xF)))
|
|
345
|
-
(i32.const ${PTR.SSO})))
|
|
398
|
+
(local.set $soff (i32.wrap_i64 (i64.and (local.get $str) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
399
|
+
(local.set $poff (i32.wrap_i64 (i64.and (local.get $pfx) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
400
|
+
(local.set $ssso (i32.and
|
|
401
|
+
(i32.wrap_i64 (i64.shr_u (local.get $str) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
402
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
403
|
+
(local.set $psso (i32.and
|
|
404
|
+
(i32.wrap_i64 (i64.shr_u (local.get $pfx) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
405
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
346
406
|
(block $done (loop $loop
|
|
347
407
|
(br_if $done (i32.ge_s (local.get $i) (local.get $plen)))
|
|
348
408
|
(if (i32.ne
|
|
@@ -357,24 +417,22 @@ export default (ctx) => {
|
|
|
357
417
|
(br $loop)))
|
|
358
418
|
(i32.const 1))`
|
|
359
419
|
|
|
360
|
-
ctx.core.stdlib['__str_endswith'] = `(func $__str_endswith (param $str
|
|
420
|
+
ctx.core.stdlib['__str_endswith'] = `(func $__str_endswith (param $str i64) (param $sfx i64) (result i32)
|
|
361
421
|
(local $slen i32) (local $flen i32) (local $off i32) (local $i i32) (local $k i32)
|
|
362
|
-
(local $
|
|
422
|
+
(local $soff i32) (local $foff i32) (local $ssso i32) (local $fsso i32)
|
|
363
423
|
(local.set $slen (call $__str_byteLen (local.get $str)))
|
|
364
424
|
(local.set $flen (call $__str_byteLen (local.get $sfx)))
|
|
365
425
|
(if (i32.gt_s (local.get $flen) (local.get $slen))
|
|
366
426
|
(then (return (i32.const 0))))
|
|
367
427
|
(local.set $off (i32.sub (local.get $slen) (local.get $flen)))
|
|
368
|
-
(local.set $
|
|
369
|
-
(local.set $
|
|
370
|
-
(local.set $
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
(i32.const ${
|
|
375
|
-
|
|
376
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $fbits) (i64.const 47)) (i64.const 0xF)))
|
|
377
|
-
(i32.const ${PTR.SSO})))
|
|
428
|
+
(local.set $soff (i32.wrap_i64 (i64.and (local.get $str) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
429
|
+
(local.set $foff (i32.wrap_i64 (i64.and (local.get $sfx) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
430
|
+
(local.set $ssso (i32.and
|
|
431
|
+
(i32.wrap_i64 (i64.shr_u (local.get $str) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
432
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
433
|
+
(local.set $fsso (i32.and
|
|
434
|
+
(i32.wrap_i64 (i64.shr_u (local.get $sfx) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
435
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
378
436
|
(block $done (loop $loop
|
|
379
437
|
(br_if $done (i32.ge_s (local.get $i) (local.get $flen)))
|
|
380
438
|
(local.set $k (i32.add (local.get $off) (local.get $i)))
|
|
@@ -391,20 +449,17 @@ export default (ctx) => {
|
|
|
391
449
|
(i32.const 1))`
|
|
392
450
|
|
|
393
451
|
// Source SSO/heap dispatch hoisted out of the byte loop (was a per-byte __char_at).
|
|
394
|
-
ctx.core.stdlib['__str_case'] = `(func $__str_case (param $ptr
|
|
452
|
+
ctx.core.stdlib['__str_case'] = `(func $__str_case (param $ptr i64) (param $lo i32) (param $hi i32) (param $delta i32) (result f64)
|
|
395
453
|
(local $len i32) (local $off i32) (local $i i32) (local $c i32)
|
|
396
|
-
(local $
|
|
454
|
+
(local $srcOff i32)
|
|
397
455
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
398
456
|
(if (i32.eqz (local.get $len))
|
|
399
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
457
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
400
458
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $len))))
|
|
401
459
|
(i32.store (local.get $off) (local.get $len))
|
|
402
460
|
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
403
|
-
(local.set $
|
|
404
|
-
(
|
|
405
|
-
(if (i32.eq
|
|
406
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $bits) (i64.const 47)) (i64.const 0xF)))
|
|
407
|
-
(i32.const ${PTR.SSO}))
|
|
461
|
+
(local.set $srcOff (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
462
|
+
(if (i64.ne (i64.and (local.get $ptr) (i64.const ${SSO_BIT_I64})) (i64.const 0))
|
|
408
463
|
(then
|
|
409
464
|
(block $dsso (loop $lsso
|
|
410
465
|
(br_if $dsso (i32.ge_s (local.get $i) (local.get $len)))
|
|
@@ -427,7 +482,7 @@ export default (ctx) => {
|
|
|
427
482
|
(br $lh)))))
|
|
428
483
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
429
484
|
|
|
430
|
-
ctx.core.stdlib['__str_trim'] = `(func $__str_trim (param $ptr
|
|
485
|
+
ctx.core.stdlib['__str_trim'] = `(func $__str_trim (param $ptr i64) (result f64)
|
|
431
486
|
(local $len i32) (local $start i32) (local $end i32)
|
|
432
487
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
433
488
|
(local.set $start (i32.const 0))
|
|
@@ -444,7 +499,7 @@ export default (ctx) => {
|
|
|
444
499
|
(br $l2)))
|
|
445
500
|
(call $__str_slice (local.get $ptr) (local.get $start) (local.get $end)))`
|
|
446
501
|
|
|
447
|
-
ctx.core.stdlib['__str_trimStart'] = `(func $__str_trimStart (param $ptr
|
|
502
|
+
ctx.core.stdlib['__str_trimStart'] = `(func $__str_trimStart (param $ptr i64) (result f64)
|
|
448
503
|
(local $len i32) (local $start i32)
|
|
449
504
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
450
505
|
(local.set $start (i32.const 0))
|
|
@@ -455,7 +510,7 @@ export default (ctx) => {
|
|
|
455
510
|
(br $loop)))
|
|
456
511
|
(call $__str_slice (local.get $ptr) (local.get $start) (local.get $len)))`
|
|
457
512
|
|
|
458
|
-
ctx.core.stdlib['__str_trimEnd'] = `(func $__str_trimEnd (param $ptr
|
|
513
|
+
ctx.core.stdlib['__str_trimEnd'] = `(func $__str_trimEnd (param $ptr i64) (result f64)
|
|
459
514
|
(local $len i32) (local $end i32)
|
|
460
515
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
461
516
|
(local.set $end (local.get $len))
|
|
@@ -468,11 +523,11 @@ export default (ctx) => {
|
|
|
468
523
|
|
|
469
524
|
// Materialize source bytes once via __str_copy (handles SSO/heap), then memory.copy
|
|
470
525
|
// each subsequent repetition (single bulk op vs len byte stores per copy).
|
|
471
|
-
ctx.core.stdlib['__str_repeat'] = `(func $__str_repeat (param $ptr
|
|
526
|
+
ctx.core.stdlib['__str_repeat'] = `(func $__str_repeat (param $ptr i64) (param $n i32) (result f64)
|
|
472
527
|
(local $len i32) (local $total i32) (local $off i32) (local $i i32)
|
|
473
528
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
474
529
|
(if (i32.or (i32.eqz (local.get $n)) (i32.eqz (local.get $len)))
|
|
475
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
530
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
476
531
|
(local.set $total (i32.mul (local.get $len) (local.get $n)))
|
|
477
532
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $total))))
|
|
478
533
|
(i32.store (local.get $off) (local.get $total))
|
|
@@ -491,39 +546,35 @@ export default (ctx) => {
|
|
|
491
546
|
|
|
492
547
|
// Coerce value to string: numbers → __ftoa, nullish → static strings,
|
|
493
548
|
// plain NaN → "NaN", arrays → join(","), other string-like pointers pass through.
|
|
494
|
-
ctx.core.stdlib['__to_str'] = `(func $__to_str (param $val
|
|
495
|
-
(local $type i32)
|
|
496
|
-
(local $
|
|
549
|
+
ctx.core.stdlib['__to_str'] = `(func $__to_str (param $val i64) (result i64)
|
|
550
|
+
(local $type i32) (local $f f64)
|
|
551
|
+
(local.set $f (f64.reinterpret_i64 (local.get $val)))
|
|
497
552
|
;; Not NaN → number, convert
|
|
498
|
-
(if (f64.eq (local.get $
|
|
499
|
-
(then (return (call $__ftoa (local.get $
|
|
500
|
-
(
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
(then (return (call $__static_str (i32.const 6)))))
|
|
553
|
+
(if (f64.eq (local.get $f) (local.get $f))
|
|
554
|
+
(then (return (i64.reinterpret_f64 (call $__ftoa (local.get $f) (i32.const 0) (i32.const 0))))))
|
|
555
|
+
(if (i64.eq (local.get $val) (i64.const ${NULL_NAN}))
|
|
556
|
+
(then (return (i64.reinterpret_f64 (call $__static_str (i32.const 5))))))
|
|
557
|
+
(if (i64.eq (local.get $val) (i64.const ${UNDEF_NAN}))
|
|
558
|
+
(then (return (i64.reinterpret_f64 (call $__static_str (i32.const 6))))))
|
|
505
559
|
(local.set $type (call $__ptr_type (local.get $val)))
|
|
506
560
|
;; Plain NaN (type=0) → "NaN" string
|
|
507
561
|
(if (i32.eqz (local.get $type))
|
|
508
|
-
(then (return (call $__static_str (i32.const 0)))))
|
|
562
|
+
(then (return (i64.reinterpret_f64 (call $__static_str (i32.const 0))))))
|
|
509
563
|
;; Array (type=1) → join(",") like JS Array.toString()
|
|
510
564
|
(if (i32.eq (local.get $type) (i32.const ${PTR.ARRAY}))
|
|
511
|
-
(then (return (call $__str_join (local.get $val)
|
|
512
|
-
(call $__mkptr (i32.const ${PTR.
|
|
565
|
+
(then (return (i64.reinterpret_f64 (call $__str_join (local.get $val)
|
|
566
|
+
(i64.reinterpret_f64 (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 1}) (i32.const 44))))))))
|
|
513
567
|
(local.get $val))`
|
|
514
568
|
|
|
515
569
|
// Copy bytes of a string (SSO or heap) into memory at dst. Uses memory.copy for
|
|
516
|
-
// heap strings (single native op); unpacks SSO
|
|
517
|
-
ctx.core.stdlib['__str_copy'] = `(func $__str_copy (param $src
|
|
518
|
-
(local $
|
|
519
|
-
(
|
|
520
|
-
(if (i32.eq
|
|
521
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $bits) (i64.const 47)) (i64.const 0xF)))
|
|
522
|
-
(i32.const ${PTR.SSO}))
|
|
570
|
+
// heap strings (single native op); unpacks SSO offset-packed bytes inline.
|
|
571
|
+
ctx.core.stdlib['__str_copy'] = `(func $__str_copy (param $src i64) (param $dst i32) (param $len i32)
|
|
572
|
+
(local $w i32)
|
|
573
|
+
(if (i64.ne (i64.and (local.get $src) (i64.const ${SSO_BIT_I64})) (i64.const 0))
|
|
523
574
|
(then
|
|
524
575
|
;; SSO: up to 4 chars packed in low 32 bits (LE byte order). Unroll: write 1/2/3/4 bytes
|
|
525
576
|
;; depending on len. (len > 4 is rare/disallowed in practice — fallback handles up to 4.)
|
|
526
|
-
(local.set $w (i32.wrap_i64 (local.get $
|
|
577
|
+
(local.set $w (i32.wrap_i64 (local.get $src)))
|
|
527
578
|
(if (i32.ge_u (local.get $len) (i32.const 4))
|
|
528
579
|
(then (i32.store (local.get $dst) (local.get $w)))
|
|
529
580
|
(else
|
|
@@ -536,11 +587,107 @@ export default (ctx) => {
|
|
|
536
587
|
(else
|
|
537
588
|
;; Heap STRING: memory.copy directly from string data
|
|
538
589
|
(memory.copy (local.get $dst)
|
|
539
|
-
(i32.wrap_i64 (i64.and (local.get $
|
|
590
|
+
(i32.wrap_i64 (i64.and (local.get $src) (i64.const ${LAYOUT.OFFSET_MASK})))
|
|
540
591
|
(local.get $len)))))`
|
|
541
592
|
|
|
542
|
-
|
|
593
|
+
// Bump-extend fast path: when `a` is a heap STRING sitting at the top of the
|
|
594
|
+
// bump allocator, extend its allocation in place instead of copying. Mutates
|
|
595
|
+
// memory[a.off-4] to the new length and bumps __heap. This makes the canonical
|
|
596
|
+
// `buf += char` build pattern O(N) instead of O(N²) — closing the asymptotic
|
|
597
|
+
// gap with V8's cons-strings. Tradeoff: aliased refs to `a` see the larger
|
|
598
|
+
// length too, so this departs from strict JS string immutability for the rare
|
|
599
|
+
// `let b = a; a += x` aliasing case. The fast path can't trigger when other
|
|
600
|
+
// allocations have happened since `a` was created (it's no longer at heap top).
|
|
601
|
+
// Only emitted for own-memory mode; shared memory falls back to slow path.
|
|
602
|
+
const concatFast = !ctx.memory.shared ? `
|
|
603
|
+
(local.set $ta (i32.wrap_i64 (i64.and (i64.shr_u (local.get $a) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
604
|
+
(local.set $aoff (i32.wrap_i64 (i64.and (local.get $a) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
605
|
+
;; Bump-extend requires heap STRING (not SSO — its offset holds packed bytes).
|
|
606
|
+
(if (i32.and
|
|
607
|
+
(i32.and
|
|
608
|
+
(i32.eq (local.get $ta) (i32.const ${PTR.STRING}))
|
|
609
|
+
(i64.eqz (i64.and (local.get $a) (i64.const ${SSO_BIT_I64}))))
|
|
610
|
+
(i32.eq
|
|
611
|
+
(i32.and (i32.add (i32.add (local.get $aoff) (local.get $alen)) (i32.const 7)) (i32.const -8))
|
|
612
|
+
(global.get $__heap)))
|
|
613
|
+
(then
|
|
614
|
+
(local.set $newHeap
|
|
615
|
+
(i32.and (i32.add (i32.add (local.get $aoff) (local.get $total)) (i32.const 7)) (i32.const -8)))
|
|
616
|
+
(if (i32.gt_u (local.get $newHeap) (i32.mul (memory.size) (i32.const 65536)))
|
|
617
|
+
(then (if (i32.eq (memory.grow
|
|
618
|
+
(i32.shr_u (i32.add (i32.sub (local.get $newHeap) (i32.mul (memory.size) (i32.const 65536))) (i32.const 65535)) (i32.const 16)))
|
|
619
|
+
(i32.const -1)) (then (unreachable)))))
|
|
620
|
+
(call $__str_copy (local.get $b)
|
|
621
|
+
(i32.add (local.get $aoff) (local.get $alen))
|
|
622
|
+
(local.get $blen))
|
|
623
|
+
(i32.store (i32.sub (local.get $aoff) (i32.const 4)) (local.get $total))
|
|
624
|
+
(global.set $__heap (local.get $newHeap))
|
|
625
|
+
(return (f64.reinterpret_i64 (local.get $a)))))` : ''
|
|
626
|
+
|
|
627
|
+
// Fused single-byte append: `buf += str[i]` lowers to this when both sides are
|
|
628
|
+
// VAL.STRING and the rhs is a string-index. Skips __str_idx's 1-char SSO
|
|
629
|
+
// construction and __str_concat's type-dispatch — byte goes directly from
|
|
630
|
+
// __char_at to memory. Bump-extends in place when `a` is at heap top.
|
|
631
|
+
ctx.core.stdlib['__str_append_byte'] = `(func $__str_append_byte (param $a i64) (param $byte i32) (result f64)
|
|
632
|
+
(local $ta i32) (local $aoff i32) (local $alen i32)
|
|
633
|
+
(local $newHeap i32) (local $off i32) (local $total i32)
|
|
634
|
+
(local.set $ta (i32.wrap_i64 (i64.and (i64.shr_u (local.get $a) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
635
|
+
(local.set $aoff (i32.wrap_i64 (i64.and (local.get $a) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
636
|
+
;; Heap STRING at heap top: bump-extend by 1 byte (own-memory mode only).
|
|
637
|
+
;; Gate on STRING tag AND !SSO_BIT — for SSO, $aoff holds packed bytes (not a heap addr).
|
|
638
|
+
${!ctx.memory.shared ? `
|
|
639
|
+
(if (i32.and
|
|
640
|
+
(i32.eq (local.get $ta) (i32.const ${PTR.STRING}))
|
|
641
|
+
(i64.eqz (i64.and (local.get $a) (i64.const ${SSO_BIT_I64}))))
|
|
642
|
+
(then
|
|
643
|
+
(local.set $alen (i32.load (i32.sub (local.get $aoff) (i32.const 4))))
|
|
644
|
+
(if (i32.eq
|
|
645
|
+
(i32.and (i32.add (i32.add (local.get $aoff) (local.get $alen)) (i32.const 7)) (i32.const -8))
|
|
646
|
+
(global.get $__heap))
|
|
647
|
+
(then
|
|
648
|
+
(local.set $newHeap
|
|
649
|
+
(i32.and (i32.add (i32.add (local.get $aoff) (local.get $alen)) (i32.const 8)) (i32.const -8)))
|
|
650
|
+
(if (i32.gt_u (local.get $newHeap) (i32.mul (memory.size) (i32.const 65536)))
|
|
651
|
+
(then (if (i32.eq (memory.grow
|
|
652
|
+
(i32.shr_u (i32.add (i32.sub (local.get $newHeap) (i32.mul (memory.size) (i32.const 65536))) (i32.const 65535)) (i32.const 16)))
|
|
653
|
+
(i32.const -1)) (then (unreachable)))))
|
|
654
|
+
(i32.store8 (i32.add (local.get $aoff) (local.get $alen)) (local.get $byte))
|
|
655
|
+
(i32.store (i32.sub (local.get $aoff) (i32.const 4)) (i32.add (local.get $alen) (i32.const 1)))
|
|
656
|
+
(global.set $__heap (local.get $newHeap))
|
|
657
|
+
(return (f64.reinterpret_i64 (local.get $a)))))))` : ''}
|
|
658
|
+
;; SSO (STRING with SSO bit) with len < 4 and ASCII byte: pack into SSO without allocation
|
|
659
|
+
(if (i32.and
|
|
660
|
+
(i32.eq (local.get $ta) (i32.const ${PTR.STRING}))
|
|
661
|
+
(i32.and
|
|
662
|
+
(i32.wrap_i64 (i64.shr_u (local.get $a) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
663
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
664
|
+
(then
|
|
665
|
+
(local.set $alen (i32.and
|
|
666
|
+
(i32.wrap_i64 (i64.shr_u (local.get $a) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
667
|
+
(i32.const ${LAYOUT.SSO_BIT - 1})))
|
|
668
|
+
(if (i32.and
|
|
669
|
+
(i32.lt_u (local.get $alen) (i32.const 4))
|
|
670
|
+
(i32.lt_u (local.get $byte) (i32.const 0x80)))
|
|
671
|
+
(then
|
|
672
|
+
(return (call $__mkptr
|
|
673
|
+
(i32.const ${PTR.STRING})
|
|
674
|
+
(i32.or (i32.const ${LAYOUT.SSO_BIT}) (i32.add (local.get $alen) (i32.const 1)))
|
|
675
|
+
(i32.or
|
|
676
|
+
(local.get $aoff)
|
|
677
|
+
(i32.shl (local.get $byte) (i32.shl (local.get $alen) (i32.const 3))))))))))
|
|
678
|
+
;; Slow path: allocate new heap STRING with original bytes + 1 new byte
|
|
679
|
+
(local.set $alen (call $__str_byteLen (local.get $a)))
|
|
680
|
+
(local.set $total (i32.add (local.get $alen) (i32.const 1)))
|
|
681
|
+
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $total))))
|
|
682
|
+
(i32.store (local.get $off) (local.get $total))
|
|
683
|
+
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
684
|
+
(call $__str_copy (local.get $a) (local.get $off) (local.get $alen))
|
|
685
|
+
(i32.store8 (i32.add (local.get $off) (local.get $alen)) (local.get $byte))
|
|
686
|
+
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
687
|
+
|
|
688
|
+
ctx.core.stdlib['__str_concat'] = `(func $__str_concat (param $a i64) (param $b i64) (result f64)
|
|
543
689
|
(local $alen i32) (local $blen i32) (local $total i32) (local $off i32)
|
|
690
|
+
(local $ta i32) (local $aoff i32) (local $newHeap i32)
|
|
544
691
|
;; Coerce operands to strings if needed
|
|
545
692
|
(local.set $a (call $__to_str (local.get $a)))
|
|
546
693
|
(local.set $b (call $__to_str (local.get $b)))
|
|
@@ -548,7 +695,8 @@ export default (ctx) => {
|
|
|
548
695
|
(local.set $blen (call $__str_byteLen (local.get $b)))
|
|
549
696
|
(local.set $total (i32.add (local.get $alen) (local.get $blen)))
|
|
550
697
|
(if (i32.eqz (local.get $total))
|
|
551
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
698
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
699
|
+
${concatFast}
|
|
552
700
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $total))))
|
|
553
701
|
(i32.store (local.get $off) (local.get $total))
|
|
554
702
|
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
@@ -556,13 +704,15 @@ export default (ctx) => {
|
|
|
556
704
|
(call $__str_copy (local.get $b) (i32.add (local.get $off) (local.get $alen)) (local.get $blen))
|
|
557
705
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
558
706
|
|
|
559
|
-
ctx.core.stdlib['__str_concat_raw'] = `(func $__str_concat_raw (param $a
|
|
707
|
+
ctx.core.stdlib['__str_concat_raw'] = `(func $__str_concat_raw (param $a i64) (param $b i64) (result f64)
|
|
560
708
|
(local $alen i32) (local $blen i32) (local $total i32) (local $off i32)
|
|
709
|
+
(local $ta i32) (local $aoff i32) (local $newHeap i32)
|
|
561
710
|
(local.set $alen (call $__str_byteLen (local.get $a)))
|
|
562
711
|
(local.set $blen (call $__str_byteLen (local.get $b)))
|
|
563
712
|
(local.set $total (i32.add (local.get $alen) (local.get $blen)))
|
|
564
713
|
(if (i32.eqz (local.get $total))
|
|
565
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
714
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
715
|
+
${concatFast}
|
|
566
716
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $total))))
|
|
567
717
|
(i32.store (local.get $off) (local.get $total))
|
|
568
718
|
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
@@ -570,39 +720,39 @@ export default (ctx) => {
|
|
|
570
720
|
(call $__str_copy (local.get $b) (i32.add (local.get $off) (local.get $alen)) (local.get $blen))
|
|
571
721
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
572
722
|
|
|
573
|
-
ctx.core.stdlib['__str_replace'] = `(func $__str_replace (param $str
|
|
723
|
+
ctx.core.stdlib['__str_replace'] = `(func $__str_replace (param $str i64) (param $search i64) (param $repl i64) (result f64)
|
|
574
724
|
(local $idx i32) (local $slen i32)
|
|
575
725
|
(local.set $idx (call $__str_indexof (local.get $str) (local.get $search) (i32.const 0)))
|
|
576
726
|
(if (result f64) (i32.lt_s (local.get $idx) (i32.const 0))
|
|
577
|
-
(then (local.get $str))
|
|
727
|
+
(then (f64.reinterpret_i64 (local.get $str)))
|
|
578
728
|
(else
|
|
579
729
|
(local.set $slen (call $__str_byteLen (local.get $search)))
|
|
580
730
|
(call $__str_concat
|
|
581
|
-
(call $__str_concat
|
|
582
|
-
(call $__str_slice (local.get $str) (i32.const 0) (local.get $idx))
|
|
583
|
-
(local.get $repl))
|
|
584
|
-
(call $__str_slice (local.get $str) (i32.add (local.get $idx) (local.get $slen))
|
|
585
|
-
(call $__str_byteLen (local.get $str)))))))`
|
|
586
|
-
|
|
587
|
-
ctx.core.stdlib['__str_replaceall'] = `(func $__str_replaceall (param $str
|
|
588
|
-
(local $idx i32) (local $slen i32) (local $pos i32) (local $result
|
|
731
|
+
(i64.reinterpret_f64 (call $__str_concat
|
|
732
|
+
(i64.reinterpret_f64 (call $__str_slice (local.get $str) (i32.const 0) (local.get $idx)))
|
|
733
|
+
(local.get $repl)))
|
|
734
|
+
(i64.reinterpret_f64 (call $__str_slice (local.get $str) (i32.add (local.get $idx) (local.get $slen))
|
|
735
|
+
(call $__str_byteLen (local.get $str))))))))`
|
|
736
|
+
|
|
737
|
+
ctx.core.stdlib['__str_replaceall'] = `(func $__str_replaceall (param $str i64) (param $search i64) (param $repl i64) (result f64)
|
|
738
|
+
(local $idx i32) (local $slen i32) (local $pos i32) (local $result i64)
|
|
589
739
|
(local.set $slen (call $__str_byteLen (local.get $search)))
|
|
590
740
|
(local.set $result (local.get $str))
|
|
591
741
|
(local.set $pos (i32.const 0))
|
|
592
742
|
(block $done (loop $next
|
|
593
743
|
(local.set $idx (call $__str_indexof (local.get $result) (local.get $search) (local.get $pos)))
|
|
594
744
|
(br_if $done (i32.lt_s (local.get $idx) (i32.const 0)))
|
|
595
|
-
(local.set $result (call $__str_concat
|
|
596
|
-
(call $__str_concat
|
|
597
|
-
(call $__str_slice (local.get $result) (i32.const 0) (local.get $idx))
|
|
598
|
-
(local.get $repl))
|
|
599
|
-
(call $__str_slice (local.get $result) (i32.add (local.get $idx) (local.get $slen))
|
|
600
|
-
(call $__str_byteLen (local.get $result)))))
|
|
745
|
+
(local.set $result (i64.reinterpret_f64 (call $__str_concat
|
|
746
|
+
(i64.reinterpret_f64 (call $__str_concat
|
|
747
|
+
(i64.reinterpret_f64 (call $__str_slice (local.get $result) (i32.const 0) (local.get $idx)))
|
|
748
|
+
(local.get $repl)))
|
|
749
|
+
(i64.reinterpret_f64 (call $__str_slice (local.get $result) (i32.add (local.get $idx) (local.get $slen))
|
|
750
|
+
(call $__str_byteLen (local.get $result)))))))
|
|
601
751
|
(local.set $pos (i32.add (local.get $idx) (call $__str_byteLen (local.get $repl))))
|
|
602
752
|
(br $next)))
|
|
603
|
-
(local.get $result))`
|
|
753
|
+
(f64.reinterpret_i64 (local.get $result)))`
|
|
604
754
|
|
|
605
|
-
ctx.core.stdlib['__str_split'] = `(func $__str_split (param $str
|
|
755
|
+
ctx.core.stdlib['__str_split'] = `(func $__str_split (param $str i64) (param $sep i64) (result f64)
|
|
606
756
|
(local $slen i32) (local $plen i32) (local $count i32)
|
|
607
757
|
(local $i i32) (local $j i32) (local $match i32)
|
|
608
758
|
(local $arr i32) (local $piece_start i32) (local $piece_idx i32)
|
|
@@ -658,32 +808,32 @@ export default (ctx) => {
|
|
|
658
808
|
(call $__str_slice (local.get $str) (local.get $piece_start) (local.get $slen)))
|
|
659
809
|
(call $__mkptr (i32.const 1) (i32.const 0) (local.get $arr)))`
|
|
660
810
|
|
|
661
|
-
ctx.core.stdlib['__str_join'] = `(func $__str_join (param $arr
|
|
811
|
+
ctx.core.stdlib['__str_join'] = `(func $__str_join (param $arr i64) (param $sep i64) (result f64)
|
|
662
812
|
(local $off i32) (local $len i32) (local $i i32) (local $result f64)
|
|
663
813
|
(local.set $off (call $__ptr_offset (local.get $arr)))
|
|
664
814
|
(local.set $len (call $__len (local.get $arr)))
|
|
665
815
|
(if (i32.eqz (local.get $len))
|
|
666
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
667
|
-
(local.set $result (f64.load (local.get $off)))
|
|
816
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
817
|
+
(local.set $result (f64.reinterpret_i64 (call $__to_str (i64.load (local.get $off)))))
|
|
668
818
|
(local.set $i (i32.const 1))
|
|
669
819
|
(block $done (loop $loop
|
|
670
820
|
(br_if $done (i32.ge_s (local.get $i) (local.get $len)))
|
|
671
|
-
(local.set $result (call $__str_concat (local.get $result) (local.get $sep)))
|
|
672
|
-
(local.set $result (call $__str_concat (local.get $result)
|
|
673
|
-
(
|
|
821
|
+
(local.set $result (call $__str_concat (i64.reinterpret_f64 (local.get $result)) (local.get $sep)))
|
|
822
|
+
(local.set $result (call $__str_concat (i64.reinterpret_f64 (local.get $result))
|
|
823
|
+
(i64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))))))
|
|
674
824
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
675
825
|
(br $loop)))
|
|
676
826
|
(local.get $result))`
|
|
677
827
|
|
|
678
828
|
// Source string copied via __str_copy (handles SSO/heap with memory.copy where possible).
|
|
679
829
|
// Pad fill loops a single tile of pad bytes — hoist pad dispatch out of the byte loop.
|
|
680
|
-
ctx.core.stdlib['__str_pad'] = `(func $__str_pad (param $str
|
|
830
|
+
ctx.core.stdlib['__str_pad'] = `(func $__str_pad (param $str i64) (param $target i32) (param $pad i64) (param $before i32) (result f64)
|
|
681
831
|
(local $slen i32) (local $plen i32) (local $fill i32) (local $off i32) (local $i i32)
|
|
682
832
|
(local $str_off i32) (local $pad_off i32)
|
|
683
833
|
(local $pbits i64) (local $poff i32) (local $psso i32)
|
|
684
834
|
(local.set $slen (call $__str_byteLen (local.get $str)))
|
|
685
835
|
(if (i32.ge_s (local.get $slen) (local.get $target))
|
|
686
|
-
(then (return (local.get $str))))
|
|
836
|
+
(then (return (f64.reinterpret_i64 (local.get $str)))))
|
|
687
837
|
(local.set $plen (call $__str_byteLen (local.get $pad)))
|
|
688
838
|
(local.set $fill (i32.sub (local.get $target) (local.get $slen)))
|
|
689
839
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $target))))
|
|
@@ -692,11 +842,11 @@ export default (ctx) => {
|
|
|
692
842
|
(local.set $str_off (select (local.get $fill) (i32.const 0) (local.get $before)))
|
|
693
843
|
(local.set $pad_off (select (i32.const 0) (local.get $slen) (local.get $before)))
|
|
694
844
|
(call $__str_copy (local.get $str) (i32.add (local.get $off) (local.get $str_off)) (local.get $slen))
|
|
695
|
-
(local.set $pbits (
|
|
696
|
-
(local.set $poff (i32.wrap_i64 (i64.and (local.get $pbits) (i64.const
|
|
697
|
-
(local.set $psso (i32.
|
|
698
|
-
(i32.wrap_i64 (i64.
|
|
699
|
-
(i32.const ${
|
|
845
|
+
(local.set $pbits (local.get $pad))
|
|
846
|
+
(local.set $poff (i32.wrap_i64 (i64.and (local.get $pbits) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
847
|
+
(local.set $psso (i32.and
|
|
848
|
+
(i32.wrap_i64 (i64.shr_u (local.get $pbits) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
849
|
+
(i32.const ${LAYOUT.SSO_BIT})))
|
|
700
850
|
(block $d2 (loop $l2
|
|
701
851
|
(br_if $d2 (i32.ge_s (local.get $i) (local.get $fill)))
|
|
702
852
|
(i32.store8 (i32.add (local.get $off) (i32.add (local.get $pad_off) (local.get $i)))
|
|
@@ -716,36 +866,51 @@ export default (ctx) => {
|
|
|
716
866
|
// === Method emitters ===
|
|
717
867
|
|
|
718
868
|
// Type-qualified (collide with array: slice, indexOf, includes)
|
|
869
|
+
// String.prototype.toString / .valueOf — both return the receiver per spec
|
|
870
|
+
// (21.1.3.27/28). Typed forms cover the static-string case; generic forms
|
|
871
|
+
// pair with them so the dispatcher can pick a runtime ptr-type branch when
|
|
872
|
+
// the receiver type can't be statically inferred (e.g. a callback param).
|
|
873
|
+
ctx.core.emit['.string:toString'] = (str) => asF64(emit(str))
|
|
874
|
+
ctx.core.emit['.string:valueOf'] = (str) => asF64(emit(str))
|
|
875
|
+
ctx.core.emit['.toString'] = (val) => {
|
|
876
|
+
inc('__to_str')
|
|
877
|
+
return typed(['f64.reinterpret_i64', ['call', '$__to_str', asI64(emit(val))]], 'f64')
|
|
878
|
+
}
|
|
879
|
+
// Object.prototype.valueOf returns the receiver (per ES2024 20.1.3.7).
|
|
880
|
+
// Array/Object inherit this; only primitive wrappers (Number/Boolean/String)
|
|
881
|
+
// override to return the primitive — strings already covered by .string:valueOf.
|
|
882
|
+
ctx.core.emit['.valueOf'] = (val) => asF64(emit(val))
|
|
883
|
+
|
|
719
884
|
ctx.core.emit['.string:slice'] = (str, start, end) => {
|
|
720
885
|
inc('__str_slice')
|
|
721
|
-
if (end != null) return typed(['call', '$__str_slice',
|
|
886
|
+
if (end != null) return typed(['call', '$__str_slice', asI64(emit(str)), asI32(emit(start)), asI32(emit(end))], 'f64')
|
|
722
887
|
const t = temp('t')
|
|
723
888
|
return typed(['block', ['result', 'f64'],
|
|
724
889
|
['local.set', `$${t}`, asF64(emit(str))],
|
|
725
|
-
['call', '$__str_slice', ['local.get', `$${t}`], asI32(emit(start)),
|
|
726
|
-
['call', '$__str_byteLen', ['local.get', `$${t}`]]]], 'f64')
|
|
890
|
+
['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${t}`]], asI32(emit(start)),
|
|
891
|
+
['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]], 'f64')
|
|
727
892
|
}
|
|
728
893
|
|
|
729
894
|
ctx.core.emit['.string:indexOf'] = (str, search, from) => {
|
|
730
895
|
inc('__str_indexof')
|
|
731
|
-
return typed(['f64.convert_i32_s', ['call', '$__str_indexof',
|
|
896
|
+
return typed(['f64.convert_i32_s', ['call', '$__str_indexof', asI64(emit(str)), asI64(emit(search)), from ? asI32(emit(from)) : ['i32.const', 0]]], 'f64')
|
|
732
897
|
}
|
|
733
898
|
|
|
734
899
|
ctx.core.emit['.string:includes'] = (str, search) => {
|
|
735
900
|
inc('__str_indexof')
|
|
736
901
|
return typed(['f64.convert_i32_s',
|
|
737
|
-
['i32.ge_s', ['call', '$__str_indexof',
|
|
902
|
+
['i32.ge_s', ['call', '$__str_indexof', asI64(emit(str)), asI64(emit(search)), ['i32.const', 0]], ['i32.const', 0]]], 'f64')
|
|
738
903
|
}
|
|
739
904
|
|
|
740
905
|
// Generic (no collision)
|
|
741
906
|
ctx.core.emit['.substring'] = (str, start, end) => {
|
|
742
907
|
inc('__str_substring')
|
|
743
|
-
if (end != null) return typed(['call', '$__str_substring',
|
|
908
|
+
if (end != null) return typed(['call', '$__str_substring', asI64(emit(str)), asI32(emit(start)), asI32(emit(end))], 'f64')
|
|
744
909
|
const t = temp('t')
|
|
745
910
|
return typed(['block', ['result', 'f64'],
|
|
746
911
|
['local.set', `$${t}`, asF64(emit(str))],
|
|
747
|
-
['call', '$__str_substring', ['local.get', `$${t}`], asI32(emit(start)),
|
|
748
|
-
['call', '$__str_byteLen', ['local.get', `$${t}`]]]], 'f64')
|
|
912
|
+
['call', '$__str_substring', ['i64.reinterpret_f64', ['local.get', `$${t}`]], asI32(emit(start)),
|
|
913
|
+
['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]], 'f64')
|
|
749
914
|
}
|
|
750
915
|
|
|
751
916
|
// Factory for simple str→call patterns: [emitKey, stdlibName, argCoercions, i32Result?]
|
|
@@ -756,38 +921,72 @@ export default (ctx) => {
|
|
|
756
921
|
return typed(i32Result ? ['f64.convert_i32_s', call] : call, 'f64')
|
|
757
922
|
}
|
|
758
923
|
|
|
759
|
-
//
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
924
|
+
// Search args go through ToString per spec — coerce non-string-typed args
|
|
925
|
+
// via __to_str so the underlying byte-compare receives an actual string.
|
|
926
|
+
const stringSearchMethod = (name) => (str, sfx) => {
|
|
927
|
+
inc(name)
|
|
928
|
+
let sfxArg = asI64(emit(sfx))
|
|
929
|
+
if (valTypeOf(sfx) !== VAL.STRING) {
|
|
930
|
+
inc('__to_str')
|
|
931
|
+
sfxArg = ['call', '$__to_str', sfxArg]
|
|
932
|
+
}
|
|
933
|
+
return typed(['f64.convert_i32_s', ['call', `$${name}`, asI64(emit(str)), sfxArg]], 'f64')
|
|
934
|
+
}
|
|
935
|
+
ctx.core.emit['.startsWith'] = stringSearchMethod('__str_startswith')
|
|
936
|
+
ctx.core.emit['.endsWith'] = stringSearchMethod('__str_endswith')
|
|
937
|
+
ctx.core.emit['.trim'] = (str) => (inc('__str_trim'),
|
|
938
|
+
typed(['call', '$__str_trim', asI64(emit(str))], 'f64'))
|
|
939
|
+
ctx.core.emit['.trimStart'] = (str) => (inc('__str_trimStart'),
|
|
940
|
+
typed(['call', '$__str_trimStart', asI64(emit(str))], 'f64'))
|
|
941
|
+
ctx.core.emit['.trimEnd'] = (str) => (inc('__str_trimEnd'),
|
|
942
|
+
typed(['call', '$__str_trimEnd', asI64(emit(str))], 'f64'))
|
|
943
|
+
ctx.core.emit['.repeat'] = (str, n) => (inc('__str_repeat'),
|
|
944
|
+
typed(['call', '$__str_repeat', asI64(emit(str)), asI32(emit(n))], 'f64'))
|
|
945
|
+
ctx.core.emit['.split'] = (str, sep) => (inc('__str_split'),
|
|
946
|
+
typed(['call', '$__str_split', asI64(emit(str)), asI64(emit(sep))], 'f64'))
|
|
947
|
+
ctx.core.emit['.replace'] = (str, search, repl) => (inc('__str_replace'),
|
|
948
|
+
typed(['call', '$__str_replace', asI64(emit(str)), asI64(emit(search)), asI64(emit(repl))], 'f64'))
|
|
949
|
+
ctx.core.emit['.replaceAll'] = (str, search, repl) => (inc('__str_replaceall'),
|
|
950
|
+
typed(['call', '$__str_replaceall', asI64(emit(str)), asI64(emit(search)), asI64(emit(repl))], 'f64'))
|
|
769
951
|
|
|
770
952
|
ctx.core.emit['.toUpperCase'] = (str) => {
|
|
771
953
|
inc('__str_case')
|
|
772
|
-
return typed(['call', '$__str_case',
|
|
954
|
+
return typed(['call', '$__str_case', asI64(emit(str)), ['i32.const', 97], ['i32.const', 122], ['i32.const', -32]], 'f64')
|
|
773
955
|
}
|
|
774
956
|
|
|
775
957
|
ctx.core.emit['.toLowerCase'] = (str) => {
|
|
776
958
|
inc('__str_case')
|
|
777
|
-
return typed(['call', '$__str_case',
|
|
959
|
+
return typed(['call', '$__str_case', asI64(emit(str)), ['i32.const', 65], ['i32.const', 90], ['i32.const', 32]], 'f64')
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Locale-specific casing needs ICU/CLDR data. jz intentionally has no
|
|
963
|
+
// runtime, so this follows the existing ASCII-only lowercase helper and
|
|
964
|
+
// ignores optional locale arguments.
|
|
965
|
+
ctx.core.emit['.toLocaleLowerCase'] = ctx.core.emit['.toLowerCase']
|
|
966
|
+
|
|
967
|
+
// Byte-wise variant of String.prototype.localeCompare. Returns -1/0/1 from
|
|
968
|
+
// an unsigned byte-by-byte compare with shorter-string-sorts-first tiebreak.
|
|
969
|
+
// NOT locale-aware: real localeCompare is ICU-driven (CLDR collation, case
|
|
970
|
+
// folding, accent ordering). For ASCII inputs the byte-wise result matches
|
|
971
|
+
// the spec exactly; for non-ASCII it follows UTF-8 byte order, which is
|
|
972
|
+
// codepoint order for well-formed strings — close enough for sort-stability
|
|
973
|
+
// use cases, wrong for human-language collation.
|
|
974
|
+
ctx.core.emit['.localeCompare'] = (str, other) => {
|
|
975
|
+
inc('__str_cmp')
|
|
976
|
+
return typed(['f64.convert_i32_s', ['call', '$__str_cmp', asI64(emit(str)), asI64(emit(other))]], 'f64')
|
|
778
977
|
}
|
|
779
978
|
|
|
780
979
|
ctx.core.emit['.string:concat'] = (str, ...others) => {
|
|
781
980
|
inc('__str_concat')
|
|
782
981
|
let result = asF64(emit(str))
|
|
783
|
-
for (const other of others) result = typed(['call', '$__str_concat', result,
|
|
982
|
+
for (const other of others) result = typed(['call', '$__str_concat', ['i64.reinterpret_f64', result], asI64(emit(other))], 'f64')
|
|
784
983
|
return result
|
|
785
984
|
}
|
|
786
985
|
|
|
787
986
|
ctx.core.emit['strcat'] = (...parts) => {
|
|
788
987
|
inc('__to_str', '__str_byteLen', '__alloc', '__mkptr', '__str_copy')
|
|
789
|
-
if (!parts.length) return mkPtrIR(PTR.
|
|
790
|
-
if (parts.length === 1) return typed(['call', '$__to_str',
|
|
988
|
+
if (!parts.length) return mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT, 0)
|
|
989
|
+
if (parts.length === 1) return typed(['f64.reinterpret_i64', ['call', '$__to_str', asI64(emit(parts[0]))]], 'f64')
|
|
791
990
|
|
|
792
991
|
const vals = parts.map(() => temp('s'))
|
|
793
992
|
const lens = parts.map(() => tempI32('sl'))
|
|
@@ -797,8 +996,8 @@ export default (ctx) => {
|
|
|
797
996
|
const ir = []
|
|
798
997
|
|
|
799
998
|
for (let i = 0; i < parts.length; i++) {
|
|
800
|
-
ir.push(['local.set', `$${vals[i]}`, ['call', '$__to_str',
|
|
801
|
-
ir.push(['local.set', `$${lens[i]}`, ['call', '$__str_byteLen', ['local.get', `$${vals[i]}`]]])
|
|
999
|
+
ir.push(['local.set', `$${vals[i]}`, ['f64.reinterpret_i64', ['call', '$__to_str', asI64(emit(parts[i]))]]])
|
|
1000
|
+
ir.push(['local.set', `$${lens[i]}`, ['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${vals[i]}`]]]])
|
|
802
1001
|
}
|
|
803
1002
|
ir.push(['local.set', `$${total}`, ['i32.const', 0]])
|
|
804
1003
|
for (const len of lens)
|
|
@@ -810,26 +1009,26 @@ export default (ctx) => {
|
|
|
810
1009
|
['local.set', `$${dst}`, ['local.get', `$${off}`]],
|
|
811
1010
|
]
|
|
812
1011
|
for (let i = 0; i < parts.length; i++) {
|
|
813
|
-
alloc.push(['call', '$__str_copy', ['local.get', `$${vals[i]}`], ['local.get', `$${dst}`], ['local.get', `$${lens[i]}`]])
|
|
1012
|
+
alloc.push(['call', '$__str_copy', ['i64.reinterpret_f64', ['local.get', `$${vals[i]}`]], ['local.get', `$${dst}`], ['local.get', `$${lens[i]}`]])
|
|
814
1013
|
alloc.push(['local.set', `$${dst}`, ['i32.add', ['local.get', `$${dst}`], ['local.get', `$${lens[i]}`]]])
|
|
815
1014
|
}
|
|
816
1015
|
alloc.push(['call', '$__mkptr', ['i32.const', PTR.STRING], ['i32.const', 0], ['local.get', `$${off}`]])
|
|
817
1016
|
ir.push(['if', ['result', 'f64'], ['i32.eqz', ['local.get', `$${total}`]],
|
|
818
|
-
['then', mkPtrIR(PTR.
|
|
1017
|
+
['then', mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT, 0)],
|
|
819
1018
|
['else', ['block', ['result', 'f64'], ...alloc]]])
|
|
820
1019
|
return typed(['block', ['result', 'f64'], ...ir], 'f64')
|
|
821
1020
|
}
|
|
822
1021
|
|
|
823
1022
|
ctx.core.emit['.padStart'] = (str, len, pad) => {
|
|
824
1023
|
inc('__str_pad')
|
|
825
|
-
const vpad = pad != null ?
|
|
826
|
-
return typed(['call', '$__str_pad',
|
|
1024
|
+
const vpad = pad != null ? asI64(emit(pad)) : ['i64.reinterpret_f64', mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, 32)]
|
|
1025
|
+
return typed(['call', '$__str_pad', asI64(emit(str)), asI32(emit(len)), vpad, ['i32.const', 1]], 'f64')
|
|
827
1026
|
}
|
|
828
1027
|
|
|
829
1028
|
ctx.core.emit['.padEnd'] = (str, len, pad) => {
|
|
830
1029
|
inc('__str_pad')
|
|
831
|
-
const vpad = pad != null ?
|
|
832
|
-
return typed(['call', '$__str_pad',
|
|
1030
|
+
const vpad = pad != null ? asI64(emit(pad)) : ['i64.reinterpret_f64', mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, 32)]
|
|
1031
|
+
return typed(['call', '$__str_pad', asI64(emit(str)), asI32(emit(len)), vpad, ['i32.const', 0]], 'f64')
|
|
833
1032
|
}
|
|
834
1033
|
|
|
835
1034
|
// .charAt(i) → 1-char string from char code at index i
|
|
@@ -838,8 +1037,8 @@ export default (ctx) => {
|
|
|
838
1037
|
const t = tempI32('ch')
|
|
839
1038
|
// Get char code, create SSO string with 1 byte
|
|
840
1039
|
return typed(['block', ['result', 'f64'],
|
|
841
|
-
['local.set', `$${t}`, ['call', '$__char_at',
|
|
842
|
-
mkPtrIR(PTR.
|
|
1040
|
+
['local.set', `$${t}`, ['call', '$__char_at', asI64(emit(str)), asI32(emit(idx))]],
|
|
1041
|
+
mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, ['local.get', `$${t}`])], 'f64')
|
|
843
1042
|
}
|
|
844
1043
|
|
|
845
1044
|
// .charCodeAt(i) → integer char code (0..255 for ASCII bytes — unsigned, always
|
|
@@ -848,7 +1047,7 @@ export default (ctx) => {
|
|
|
848
1047
|
// `c - 48` arithmetic skip the per-iteration f64 widen + i32 trunc round-trip.
|
|
849
1048
|
ctx.core.emit['.charCodeAt'] = (str, idx) => {
|
|
850
1049
|
inc('__char_at')
|
|
851
|
-
return typed(['call', '$__char_at',
|
|
1050
|
+
return typed(['call', '$__char_at', asI64(emit(str)), asI32(emit(idx))], 'i32')
|
|
852
1051
|
}
|
|
853
1052
|
|
|
854
1053
|
// String.fromCharCode(code) → 1-char SSO string
|
|
@@ -860,32 +1059,35 @@ export default (ctx) => {
|
|
|
860
1059
|
return typed(['call', '$__ftoa', asF64(emit(value)), ['i32.const', 0], ['i32.const', 0]], 'f64')
|
|
861
1060
|
}
|
|
862
1061
|
inc('__to_str')
|
|
863
|
-
return typed(['call', '$__to_str',
|
|
1062
|
+
return typed(['f64.reinterpret_i64', ['call', '$__to_str', asI64(emit(value))]], 'f64')
|
|
864
1063
|
}
|
|
865
1064
|
|
|
866
|
-
ctx.core.emit['String.fromCharCode'] = (code) =>
|
|
1065
|
+
ctx.core.emit['String.fromCharCode'] = (code) => {
|
|
1066
|
+
if (code === undefined) return emit(['str', ''])
|
|
1067
|
+
return mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, asI32(emit(code)))
|
|
1068
|
+
}
|
|
867
1069
|
|
|
868
1070
|
// String.fromCodePoint(cp) → UTF-8 encoded string
|
|
869
1071
|
ctx.core.stdlib['__fromCodePoint'] = `(func $__fromCodePoint (param $cp i32) (result f64)
|
|
870
1072
|
(local $off i32) (local $len i32)
|
|
871
1073
|
;; ASCII: 1 byte SSO
|
|
872
1074
|
(if (i32.lt_u (local.get $cp) (i32.const 128))
|
|
873
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
1075
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 1}) (local.get $cp)))))
|
|
874
1076
|
;; 2-byte: 0x80-0x7FF → SSO
|
|
875
1077
|
(if (i32.lt_u (local.get $cp) (i32.const 0x800))
|
|
876
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
1078
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 2})
|
|
877
1079
|
(i32.or
|
|
878
1080
|
(i32.or (i32.const 0xC0) (i32.shr_u (local.get $cp) (i32.const 6)))
|
|
879
1081
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 8)))))))
|
|
880
1082
|
;; 3-byte: 0x800-0xFFFF → SSO (3 bytes fits)
|
|
881
1083
|
(if (i32.lt_u (local.get $cp) (i32.const 0x10000))
|
|
882
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
1084
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 3})
|
|
883
1085
|
(i32.or (i32.or
|
|
884
1086
|
(i32.or (i32.const 0xE0) (i32.shr_u (local.get $cp) (i32.const 12)))
|
|
885
1087
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 6)) (i32.const 0x3F))) (i32.const 8)))
|
|
886
1088
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 16)))))))
|
|
887
1089
|
;; 4-byte: 0x10000-0x10FFFF → SSO (4 bytes fits)
|
|
888
|
-
(return (call $__mkptr (i32.const ${PTR.
|
|
1090
|
+
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 4})
|
|
889
1091
|
(i32.or (i32.or (i32.or
|
|
890
1092
|
(i32.or (i32.const 0xF0) (i32.shr_u (local.get $cp) (i32.const 18)))
|
|
891
1093
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 12)) (i32.const 0x3F))) (i32.const 8)))
|
|
@@ -893,12 +1095,13 @@ export default (ctx) => {
|
|
|
893
1095
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 24))))))`
|
|
894
1096
|
|
|
895
1097
|
ctx.core.emit['String.fromCodePoint'] = (code) => {
|
|
1098
|
+
if (code === undefined) return emit(['str', ''])
|
|
896
1099
|
inc('__fromCodePoint')
|
|
897
1100
|
return typed(['call', '$__fromCodePoint', asI32(emit(code))], 'f64')
|
|
898
1101
|
}
|
|
899
1102
|
|
|
900
1103
|
// .at(i) → charAt with negative index support
|
|
901
|
-
ctx.core.emit['.at'] = (str, idx) => {
|
|
1104
|
+
ctx.core.emit['.string:at'] = (str, idx) => {
|
|
902
1105
|
inc('__char_at', '__str_byteLen')
|
|
903
1106
|
const t = tempI32('at'), s = temp('as')
|
|
904
1107
|
return typed(['block', ['result', 'f64'],
|
|
@@ -907,14 +1110,14 @@ export default (ctx) => {
|
|
|
907
1110
|
// Negative index: t += length
|
|
908
1111
|
['if', ['i32.lt_s', ['local.get', `$${t}`], ['i32.const', 0]],
|
|
909
1112
|
['then', ['local.set', `$${t}`, ['i32.add', ['local.get', `$${t}`],
|
|
910
|
-
['call', '$__str_byteLen', ['local.get', `$${s}`]]]]]],
|
|
911
|
-
mkPtrIR(PTR.
|
|
1113
|
+
['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]]]],
|
|
1114
|
+
mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, ['call', '$__char_at', ['i64.reinterpret_f64', ['local.get', `$${s}`]], ['local.get', `$${t}`]])], 'f64')
|
|
912
1115
|
}
|
|
913
1116
|
|
|
914
1117
|
// .search(str) → indexOf (same as indexOf for string args)
|
|
915
1118
|
ctx.core.emit['.search'] = (str, search) => {
|
|
916
1119
|
inc('__str_indexof')
|
|
917
|
-
return typed(['f64.convert_i32_s', ['call', '$__str_indexof',
|
|
1120
|
+
return typed(['f64.convert_i32_s', ['call', '$__str_indexof', asI64(emit(str)), asI64(emit(search)), ['i32.const', 0]]], 'f64')
|
|
918
1121
|
}
|
|
919
1122
|
|
|
920
1123
|
// .match(str) → [match] array if found, or 0 (null) if not
|
|
@@ -926,24 +1129,25 @@ export default (ctx) => {
|
|
|
926
1129
|
return typed(['block', ['result', 'f64'],
|
|
927
1130
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
928
1131
|
['local.set', `$${q}`, asF64(emit(search))],
|
|
929
|
-
['local.set', `$${idx}`, ['call', '$__str_indexof', ['local.get', `$${s}`], ['local.get', `$${q}`], ['i32.const', 0]]],
|
|
1132
|
+
['local.set', `$${idx}`, ['call', '$__str_indexof', ['i64.reinterpret_f64', ['local.get', `$${s}`]], ['i64.reinterpret_f64', ['local.get', `$${q}`]], ['i32.const', 0]]],
|
|
930
1133
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${idx}`], ['i32.const', 0]],
|
|
931
1134
|
['then', ['f64.const', 0]], // null
|
|
932
1135
|
['else',
|
|
933
1136
|
// Build 1-element array containing the search string
|
|
934
1137
|
['call', '$__wrap1',
|
|
935
|
-
['
|
|
936
|
-
['local.get', `$${
|
|
937
|
-
|
|
1138
|
+
['i64.reinterpret_f64',
|
|
1139
|
+
['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${s}`]],
|
|
1140
|
+
['local.get', `$${idx}`],
|
|
1141
|
+
['i32.add', ['local.get', `$${idx}`], ['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${q}`]]]]]]]]]], 'f64')
|
|
938
1142
|
}
|
|
939
1143
|
|
|
940
|
-
// __wrap1(val:
|
|
941
|
-
ctx.core.stdlib['__wrap1'] = `(func $__wrap1 (param $val
|
|
1144
|
+
// __wrap1(val: i64) → f64 — create 1-element array [val]
|
|
1145
|
+
ctx.core.stdlib['__wrap1'] = `(func $__wrap1 (param $val i64) (result f64)
|
|
942
1146
|
(local $ptr i32)
|
|
943
1147
|
(local.set $ptr (call $__alloc (i32.const 16)))
|
|
944
1148
|
(i32.store (local.get $ptr) (i32.const 1))
|
|
945
1149
|
(i32.store (i32.add (local.get $ptr) (i32.const 4)) (i32.const 1))
|
|
946
|
-
(
|
|
1150
|
+
(i64.store (i32.add (local.get $ptr) (i32.const 8)) (local.get $val))
|
|
947
1151
|
(call $__mkptr (i32.const 1) (i32.const 0) (i32.add (local.get $ptr) (i32.const 8))))`
|
|
948
1152
|
|
|
949
1153
|
// TextEncoder() / TextDecoder() → dummy values (methods do the work)
|
|
@@ -952,7 +1156,7 @@ export default (ctx) => {
|
|
|
952
1156
|
|
|
953
1157
|
// .encode(str) → Uint8Array of string's UTF-8 bytes
|
|
954
1158
|
// Copies bytes from string (SSO or heap) into a new Uint8Array
|
|
955
|
-
ctx.core.stdlib['__str_encode'] = `(func $__str_encode (param $str
|
|
1159
|
+
ctx.core.stdlib['__str_encode'] = `(func $__str_encode (param $str i64) (result f64)
|
|
956
1160
|
(local $len i32) (local $dst i32)
|
|
957
1161
|
(local.set $len (call $__str_byteLen (local.get $str)))
|
|
958
1162
|
(local.set $dst (call $__alloc (i32.add (i32.const 8) (local.get $len))))
|
|
@@ -964,11 +1168,11 @@ export default (ctx) => {
|
|
|
964
1168
|
|
|
965
1169
|
ctx.core.emit['.encode'] = (obj, str) => {
|
|
966
1170
|
inc('__str_encode')
|
|
967
|
-
return typed(['call', '$__str_encode',
|
|
1171
|
+
return typed(['call', '$__str_encode', asI64(emit(str))], 'f64')
|
|
968
1172
|
}
|
|
969
1173
|
|
|
970
1174
|
// .decode(uint8arr) → string from byte data
|
|
971
|
-
ctx.core.stdlib['__bytes_decode'] = `(func $__bytes_decode (param $arr
|
|
1175
|
+
ctx.core.stdlib['__bytes_decode'] = `(func $__bytes_decode (param $arr i64) (result f64)
|
|
972
1176
|
(local $off i32) (local $len i32) (local $dst i32)
|
|
973
1177
|
(local.set $off (call $__ptr_offset (local.get $arr)))
|
|
974
1178
|
(local.set $len (call $__len (local.get $arr)))
|
|
@@ -980,6 +1184,6 @@ export default (ctx) => {
|
|
|
980
1184
|
|
|
981
1185
|
ctx.core.emit['.decode'] = (obj, arr) => {
|
|
982
1186
|
inc('__bytes_decode')
|
|
983
|
-
return typed(['call', '$__bytes_decode',
|
|
1187
|
+
return typed(['call', '$__bytes_decode', asI64(emit(arr))], 'f64')
|
|
984
1188
|
}
|
|
985
1189
|
}
|