jz 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -62
- package/cli.js +34 -8
- package/index.js +133 -14
- package/module/array.js +399 -131
- package/module/collection.js +457 -212
- package/module/console.js +170 -87
- package/module/core.js +247 -143
- package/module/date.js +691 -0
- package/module/function.js +84 -23
- package/module/index.js +2 -1
- package/module/json.js +485 -138
- package/module/math.js +81 -40
- package/module/number.js +189 -83
- package/module/object.js +228 -46
- package/module/regex.js +34 -30
- package/module/schema.js +17 -18
- package/module/string.js +483 -227
- package/module/symbol.js +6 -4
- package/module/timer.js +90 -32
- package/module/typedarray.js +176 -44
- package/package.json +5 -10
- package/src/analyze.js +639 -124
- package/src/autoload.js +183 -0
- package/src/compile.js +448 -1083
- package/src/ctx.js +42 -12
- package/src/emit.js +646 -147
- package/src/host.js +273 -68
- package/src/ir.js +81 -31
- package/src/jzify.js +220 -36
- package/src/narrow.js +956 -0
- package/src/optimize.js +628 -42
- package/src/plan.js +1233 -0
- package/src/prepare.js +438 -256
- package/src/vectorize.js +1016 -0
- package/wasi.js +23 -4
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,14 +12,21 @@
|
|
|
10
12
|
* @module string
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
+
import { typed, asF64, asI32, asI64, NULL_NAN, UNDEF_NAN, mkPtrIR, temp, tempI32 } from '../src/ir.js'
|
|
16
|
+
import { emit } from '../src/emit.js'
|
|
17
|
+
import { valTypeOf, VAL } from '../src/analyze.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')
|
|
15
23
|
|
|
16
24
|
|
|
17
25
|
export default (ctx) => {
|
|
18
26
|
Object.assign(ctx.core.stdlibDeps, {
|
|
19
27
|
__str_concat: ['__to_str', '__str_byteLen', '__alloc', '__mkptr', '__str_copy'],
|
|
20
28
|
__str_concat_raw: ['__str_byteLen', '__alloc', '__mkptr', '__str_copy'],
|
|
29
|
+
__str_append_byte: ['__str_byteLen', '__alloc', '__mkptr', '__str_copy'],
|
|
21
30
|
__str_copy: [],
|
|
22
31
|
__str_slice: ['__str_byteLen', '__alloc'],
|
|
23
32
|
__str_indexof: ['__str_byteLen'],
|
|
@@ -32,8 +41,9 @@ export default (ctx) => {
|
|
|
32
41
|
__str_replace: ['__str_indexof', '__str_slice', '__str_concat'],
|
|
33
42
|
__str_replaceall: ['__str_indexof', '__str_slice', '__str_concat'],
|
|
34
43
|
__str_split: ['__str_slice', '__str_byteLen', '__char_at', '__alloc'],
|
|
35
|
-
__str_idx: [
|
|
44
|
+
__str_idx: [],
|
|
36
45
|
__str_eq: ['__char_at'],
|
|
46
|
+
__str_cmp: ['__char_at', '__str_byteLen'],
|
|
37
47
|
__str_pad: ['__str_byteLen', '__str_copy', '__alloc'],
|
|
38
48
|
__str_join: ['__str_concat', '__to_str', '__str_byteLen', '__len', '__ptr_offset'],
|
|
39
49
|
__str_encode: ['__str_byteLen', '__str_copy'],
|
|
@@ -50,9 +60,10 @@ export default (ctx) => {
|
|
|
50
60
|
if (ctx.features.sso && str.length <= MAX_SSO && /^[\x00-\x7f]*$/.test(str)) {
|
|
51
61
|
let packed = 0
|
|
52
62
|
for (let i = 0; i < str.length; i++) packed |= str.charCodeAt(i) << (i * 8)
|
|
53
|
-
return mkPtrIR(PTR.
|
|
63
|
+
return mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | str.length, packed)
|
|
54
64
|
}
|
|
55
|
-
const
|
|
65
|
+
const bytes = new TextEncoder().encode(str)
|
|
66
|
+
const len = bytes.length
|
|
56
67
|
if (!ctx.memory.shared) {
|
|
57
68
|
// Own memory: place in static data segment (no runtime allocation)
|
|
58
69
|
if (!ctx.runtime.data) ctx.runtime.data = ''
|
|
@@ -61,7 +72,7 @@ export default (ctx) => {
|
|
|
61
72
|
while (ctx.runtime.data.length % 4 !== 0) ctx.runtime.data += '\0'
|
|
62
73
|
const offset = ctx.runtime.data.length
|
|
63
74
|
ctx.runtime.data += String.fromCharCode(len & 0xFF, (len >> 8) & 0xFF, (len >> 16) & 0xFF, (len >> 24) & 0xFF)
|
|
64
|
-
for (let i = 0; i < len; i++) ctx.runtime.data += String.fromCharCode(
|
|
75
|
+
for (let i = 0; i < len; i++) ctx.runtime.data += String.fromCharCode(bytes[i])
|
|
65
76
|
ctx.runtime.dataDedup.set(str, offset)
|
|
66
77
|
return mkPtrIR(PTR.STRING, 0, offset + 4)
|
|
67
78
|
}
|
|
@@ -75,10 +86,10 @@ export default (ctx) => {
|
|
|
75
86
|
}
|
|
76
87
|
let off = ctx.runtime.strPoolDedup.get(str)
|
|
77
88
|
if (off === undefined) {
|
|
78
|
-
// Pack length header then bytes; offset points PAST the length (at the data).
|
|
89
|
+
// Pack length header then UTF-8 bytes; offset points PAST the length (at the data).
|
|
79
90
|
ctx.runtime.strPool += String.fromCharCode(len & 0xFF, (len >> 8) & 0xFF, (len >> 16) & 0xFF, (len >> 24) & 0xFF)
|
|
80
91
|
off = ctx.runtime.strPool.length
|
|
81
|
-
ctx.runtime.strPool +=
|
|
92
|
+
for (let i = 0; i < len; i++) ctx.runtime.strPool += String.fromCharCode(bytes[i])
|
|
82
93
|
ctx.runtime.strPoolDedup.set(str, off)
|
|
83
94
|
}
|
|
84
95
|
return mkPtrIR(PTR.STRING, 0, ['i32.add', ['global.get', '$__strBase'], ['i32.const', off]])
|
|
@@ -88,28 +99,25 @@ export default (ctx) => {
|
|
|
88
99
|
|
|
89
100
|
// SSO/STRING ptrs never have forwarding pointers (only ARRAY does), so we extract
|
|
90
101
|
// the raw offset directly instead of paying the __ptr_offset function-call overhead.
|
|
91
|
-
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)
|
|
92
103
|
(i32.and
|
|
93
104
|
(i32.shr_u
|
|
94
|
-
(i32.wrap_i64 (i64.and (
|
|
105
|
+
(i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK})))
|
|
95
106
|
(i32.mul (local.get $i) (i32.const 8)))
|
|
96
107
|
(i32.const 0xFF)))`
|
|
97
108
|
|
|
98
|
-
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)
|
|
99
110
|
(i32.load8_u (i32.add
|
|
100
|
-
(i32.wrap_i64 (i64.and (
|
|
111
|
+
(i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK})))
|
|
101
112
|
(local.get $i))))`
|
|
102
113
|
|
|
103
|
-
// Hot (~37M calls in watr self-host).
|
|
104
|
-
// SSO
|
|
105
|
-
ctx.core.stdlib['__char_at'] = `(func $__char_at (param $ptr
|
|
106
|
-
(local $
|
|
107
|
-
(local.set $
|
|
108
|
-
(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}))))
|
|
109
119
|
(if (result i32)
|
|
110
|
-
(
|
|
111
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $bits) (i64.const 47)) (i64.const 0xF)))
|
|
112
|
-
(i32.const ${PTR.SSO}))
|
|
120
|
+
(i64.ne (i64.and (local.get $ptr) (i64.const ${SSO_BIT_I64})) (i64.const 0))
|
|
113
121
|
(then
|
|
114
122
|
(i32.and
|
|
115
123
|
(i32.shr_u (local.get $off) (i32.mul (local.get $i) (i32.const 8)))
|
|
@@ -117,44 +125,66 @@ export default (ctx) => {
|
|
|
117
125
|
(else
|
|
118
126
|
(i32.load8_u (i32.add (local.get $off) (local.get $i))))))`
|
|
119
127
|
|
|
120
|
-
ctx.core.stdlib['__str_idx'] = `(func $__str_idx (param $ptr
|
|
121
|
-
(local $len i32)
|
|
122
|
-
(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))))))
|
|
123
144
|
(if (result f64)
|
|
124
|
-
(i32.or
|
|
125
|
-
(i32.lt_s (local.get $i) (i32.const 0))
|
|
126
|
-
(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)))
|
|
127
146
|
(then (f64.const nan:${UNDEF_NAN}))
|
|
128
147
|
(else
|
|
129
|
-
(
|
|
130
|
-
(
|
|
131
|
-
|
|
132
|
-
|
|
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)))))))))))`
|
|
133
156
|
|
|
134
157
|
// Hot: ~53M calls in watr self-host. Bit-eq covers identity. SSO/SSO with !bit-eq
|
|
135
158
|
// guarantees content differs (high 32 bits encode type+len; both equal → low 32 differs
|
|
136
|
-
// ⇒ bytes differ).
|
|
137
|
-
// Mixed
|
|
138
|
-
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)
|
|
139
162
|
(local $len i32) (local $lenB i32) (local $i i32)
|
|
140
|
-
(local $
|
|
163
|
+
(local $ta i32) (local $tb i32)
|
|
141
164
|
(local $offA i32) (local $offB i32)
|
|
142
|
-
(local
|
|
143
|
-
(
|
|
144
|
-
(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))
|
|
145
167
|
(then (return (i32.const 1))))
|
|
146
|
-
(local.set $ta (i32.wrap_i64 (i64.and (i64.shr_u (local.get $
|
|
147
|
-
(local.set $tb (i32.wrap_i64 (i64.and (i64.shr_u (local.get $
|
|
148
|
-
(local.set $offA (i32.wrap_i64 (i64.and (local.get $
|
|
149
|
-
(local.set $offB (i32.wrap_i64 (i64.and (local.get $
|
|
150
|
-
|
|
151
|
-
|
|
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))
|
|
152
180
|
(then (return (i32.const 0))))
|
|
153
|
-
;; Both STRING fast path: inline len from header. Chunk by 4 bytes via unaligned
|
|
154
|
-
;; (wasm guarantees unaligned-OK), then byte-tail. Most string comparisons fail
|
|
155
|
-
;; the first 4-byte word, so this collapses the per-byte branch overhead into a
|
|
156
|
-
;; 32-bit equality.
|
|
157
|
-
(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))))
|
|
158
188
|
(then
|
|
159
189
|
(if (i32.or (i32.lt_u (local.get $offA) (i32.const 4)) (i32.lt_u (local.get $offB) (i32.const 4)))
|
|
160
190
|
(then (return (i32.const 0))))
|
|
@@ -180,14 +210,18 @@ export default (ctx) => {
|
|
|
180
210
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
181
211
|
(br $lh)))
|
|
182
212
|
(return (i32.const 1))))
|
|
183
|
-
;; Mixed (
|
|
184
|
-
(if (
|
|
185
|
-
(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}))))
|
|
186
218
|
(else
|
|
187
219
|
(if (i32.and (i32.eq (local.get $ta) (i32.const ${PTR.STRING})) (i32.ge_u (local.get $offA) (i32.const 4)))
|
|
188
220
|
(then (local.set $len (i32.load (i32.sub (local.get $offA) (i32.const 4))))))))
|
|
189
|
-
(if (
|
|
190
|
-
(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}))))
|
|
191
225
|
(else
|
|
192
226
|
(if (i32.and (i32.eq (local.get $tb) (i32.const ${PTR.STRING})) (i32.ge_u (local.get $offB) (i32.const 4)))
|
|
193
227
|
(then (local.set $lenB (i32.load (i32.sub (local.get $offB) (i32.const 4))))))))
|
|
@@ -202,28 +236,62 @@ export default (ctx) => {
|
|
|
202
236
|
(br $lm)))
|
|
203
237
|
(i32.const 1))`
|
|
204
238
|
|
|
205
|
-
//
|
|
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))`
|
|
206
268
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
(local
|
|
211
|
-
(
|
|
212
|
-
|
|
213
|
-
(
|
|
214
|
-
(local.set $
|
|
215
|
-
|
|
216
|
-
(i32.
|
|
217
|
-
|
|
218
|
-
(
|
|
269
|
+
// === WAT: unified byte length (SSO → aux low bits, heap → header) ===
|
|
270
|
+
|
|
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))))`
|
|
219
287
|
|
|
220
288
|
// === WAT: string methods ===
|
|
221
289
|
|
|
222
290
|
// SSO source uses an unrolled byte-extract loop (len ≤ 4); heap source uses memory.copy
|
|
223
291
|
// (single bulk op instead of nlen × __char_at).
|
|
224
|
-
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)
|
|
225
293
|
(local $len i32) (local $nlen i32) (local $off i32) (local $i i32)
|
|
226
|
-
(local $
|
|
294
|
+
(local $srcOff i32) (local $isSso i32)
|
|
227
295
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
228
296
|
(if (i32.lt_s (local.get $start) (i32.const 0))
|
|
229
297
|
(then (local.set $start (i32.add (local.get $len) (local.get $start)))))
|
|
@@ -238,16 +306,15 @@ export default (ctx) => {
|
|
|
238
306
|
(if (i32.gt_s (local.get $end) (local.get $len))
|
|
239
307
|
(then (local.set $end (local.get $len))))
|
|
240
308
|
(if (i32.ge_s (local.get $start) (local.get $end))
|
|
241
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
309
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
242
310
|
(local.set $nlen (i32.sub (local.get $end) (local.get $start)))
|
|
243
311
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $nlen))))
|
|
244
312
|
(i32.store (local.get $off) (local.get $nlen))
|
|
245
313
|
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
246
|
-
(local.set $
|
|
247
|
-
(local.set $
|
|
248
|
-
|
|
249
|
-
(
|
|
250
|
-
(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}))))
|
|
251
318
|
(if (local.get $isSso)
|
|
252
319
|
(then
|
|
253
320
|
(block $done (loop $loop
|
|
@@ -262,7 +329,7 @@ export default (ctx) => {
|
|
|
262
329
|
(memory.copy (local.get $off) (i32.add (local.get $srcOff) (local.get $start)) (local.get $nlen))))
|
|
263
330
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
264
331
|
|
|
265
|
-
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)
|
|
266
333
|
(local $len i32) (local $tmp i32)
|
|
267
334
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
268
335
|
(if (i32.lt_s (local.get $start) (i32.const 0))
|
|
@@ -282,24 +349,22 @@ export default (ctx) => {
|
|
|
282
349
|
|
|
283
350
|
// Hoist SSO/heap dispatch for hay and ndl out of the inner byte loop. Inner
|
|
284
351
|
// loop becomes (load8_u OR sso byte-extract) per side — no per-byte calls.
|
|
285
|
-
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)
|
|
286
353
|
(local $hlen i32) (local $nlen i32) (local $i i32) (local $j i32) (local $match i32)
|
|
287
|
-
(local $
|
|
354
|
+
(local $hoff i32) (local $noff i32)
|
|
288
355
|
(local $hsso i32) (local $nsso i32) (local $hb i32) (local $nb i32) (local $k i32)
|
|
289
356
|
(local.set $hlen (call $__str_byteLen (local.get $hay)))
|
|
290
357
|
(local.set $nlen (call $__str_byteLen (local.get $ndl)))
|
|
291
358
|
(if (i32.eqz (local.get $nlen)) (then (return (local.get $from))))
|
|
292
359
|
(if (i32.gt_s (local.get $nlen) (local.get $hlen)) (then (return (i32.const -1))))
|
|
293
|
-
(local.set $
|
|
294
|
-
(local.set $
|
|
295
|
-
(local.set $
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
(i32.const ${
|
|
300
|
-
|
|
301
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $nbits) (i64.const 47)) (i64.const 0xF)))
|
|
302
|
-
(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})))
|
|
303
368
|
(local.set $i (if (result i32) (i32.gt_s (local.get $from) (i32.const 0)) (then (local.get $from)) (else (i32.const 0))))
|
|
304
369
|
(block $done (loop $outer
|
|
305
370
|
(br_if $done (i32.gt_s (local.get $i) (i32.sub (local.get $hlen) (local.get $nlen))))
|
|
@@ -324,22 +389,20 @@ export default (ctx) => {
|
|
|
324
389
|
(i32.const -1))`
|
|
325
390
|
|
|
326
391
|
// SSO/heap dispatch hoisted; inner loop is two inlined byte-fetches and a compare.
|
|
327
|
-
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)
|
|
328
393
|
(local $plen i32) (local $i i32)
|
|
329
|
-
(local $
|
|
394
|
+
(local $soff i32) (local $poff i32) (local $ssso i32) (local $psso i32)
|
|
330
395
|
(local.set $plen (call $__str_byteLen (local.get $pfx)))
|
|
331
396
|
(if (i32.gt_s (local.get $plen) (call $__str_byteLen (local.get $str)))
|
|
332
397
|
(then (return (i32.const 0))))
|
|
333
|
-
(local.set $
|
|
334
|
-
(local.set $
|
|
335
|
-
(local.set $
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
(i32.const ${
|
|
340
|
-
|
|
341
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $pbits) (i64.const 47)) (i64.const 0xF)))
|
|
342
|
-
(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})))
|
|
343
406
|
(block $done (loop $loop
|
|
344
407
|
(br_if $done (i32.ge_s (local.get $i) (local.get $plen)))
|
|
345
408
|
(if (i32.ne
|
|
@@ -354,24 +417,22 @@ export default (ctx) => {
|
|
|
354
417
|
(br $loop)))
|
|
355
418
|
(i32.const 1))`
|
|
356
419
|
|
|
357
|
-
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)
|
|
358
421
|
(local $slen i32) (local $flen i32) (local $off i32) (local $i i32) (local $k i32)
|
|
359
|
-
(local $
|
|
422
|
+
(local $soff i32) (local $foff i32) (local $ssso i32) (local $fsso i32)
|
|
360
423
|
(local.set $slen (call $__str_byteLen (local.get $str)))
|
|
361
424
|
(local.set $flen (call $__str_byteLen (local.get $sfx)))
|
|
362
425
|
(if (i32.gt_s (local.get $flen) (local.get $slen))
|
|
363
426
|
(then (return (i32.const 0))))
|
|
364
427
|
(local.set $off (i32.sub (local.get $slen) (local.get $flen)))
|
|
365
|
-
(local.set $
|
|
366
|
-
(local.set $
|
|
367
|
-
(local.set $
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
(i32.const ${
|
|
372
|
-
|
|
373
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $fbits) (i64.const 47)) (i64.const 0xF)))
|
|
374
|
-
(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})))
|
|
375
436
|
(block $done (loop $loop
|
|
376
437
|
(br_if $done (i32.ge_s (local.get $i) (local.get $flen)))
|
|
377
438
|
(local.set $k (i32.add (local.get $off) (local.get $i)))
|
|
@@ -388,20 +449,17 @@ export default (ctx) => {
|
|
|
388
449
|
(i32.const 1))`
|
|
389
450
|
|
|
390
451
|
// Source SSO/heap dispatch hoisted out of the byte loop (was a per-byte __char_at).
|
|
391
|
-
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)
|
|
392
453
|
(local $len i32) (local $off i32) (local $i i32) (local $c i32)
|
|
393
|
-
(local $
|
|
454
|
+
(local $srcOff i32)
|
|
394
455
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
395
456
|
(if (i32.eqz (local.get $len))
|
|
396
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
457
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
397
458
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $len))))
|
|
398
459
|
(i32.store (local.get $off) (local.get $len))
|
|
399
460
|
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
400
|
-
(local.set $
|
|
401
|
-
(
|
|
402
|
-
(if (i32.eq
|
|
403
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $bits) (i64.const 47)) (i64.const 0xF)))
|
|
404
|
-
(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))
|
|
405
463
|
(then
|
|
406
464
|
(block $dsso (loop $lsso
|
|
407
465
|
(br_if $dsso (i32.ge_s (local.get $i) (local.get $len)))
|
|
@@ -424,7 +482,7 @@ export default (ctx) => {
|
|
|
424
482
|
(br $lh)))))
|
|
425
483
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
426
484
|
|
|
427
|
-
ctx.core.stdlib['__str_trim'] = `(func $__str_trim (param $ptr
|
|
485
|
+
ctx.core.stdlib['__str_trim'] = `(func $__str_trim (param $ptr i64) (result f64)
|
|
428
486
|
(local $len i32) (local $start i32) (local $end i32)
|
|
429
487
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
430
488
|
(local.set $start (i32.const 0))
|
|
@@ -441,7 +499,7 @@ export default (ctx) => {
|
|
|
441
499
|
(br $l2)))
|
|
442
500
|
(call $__str_slice (local.get $ptr) (local.get $start) (local.get $end)))`
|
|
443
501
|
|
|
444
|
-
ctx.core.stdlib['__str_trimStart'] = `(func $__str_trimStart (param $ptr
|
|
502
|
+
ctx.core.stdlib['__str_trimStart'] = `(func $__str_trimStart (param $ptr i64) (result f64)
|
|
445
503
|
(local $len i32) (local $start i32)
|
|
446
504
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
447
505
|
(local.set $start (i32.const 0))
|
|
@@ -452,7 +510,7 @@ export default (ctx) => {
|
|
|
452
510
|
(br $loop)))
|
|
453
511
|
(call $__str_slice (local.get $ptr) (local.get $start) (local.get $len)))`
|
|
454
512
|
|
|
455
|
-
ctx.core.stdlib['__str_trimEnd'] = `(func $__str_trimEnd (param $ptr
|
|
513
|
+
ctx.core.stdlib['__str_trimEnd'] = `(func $__str_trimEnd (param $ptr i64) (result f64)
|
|
456
514
|
(local $len i32) (local $end i32)
|
|
457
515
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
458
516
|
(local.set $end (local.get $len))
|
|
@@ -465,11 +523,11 @@ export default (ctx) => {
|
|
|
465
523
|
|
|
466
524
|
// Materialize source bytes once via __str_copy (handles SSO/heap), then memory.copy
|
|
467
525
|
// each subsequent repetition (single bulk op vs len byte stores per copy).
|
|
468
|
-
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)
|
|
469
527
|
(local $len i32) (local $total i32) (local $off i32) (local $i i32)
|
|
470
528
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
471
529
|
(if (i32.or (i32.eqz (local.get $n)) (i32.eqz (local.get $len)))
|
|
472
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
530
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
473
531
|
(local.set $total (i32.mul (local.get $len) (local.get $n)))
|
|
474
532
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $total))))
|
|
475
533
|
(i32.store (local.get $off) (local.get $total))
|
|
@@ -486,34 +544,37 @@ export default (ctx) => {
|
|
|
486
544
|
(br $loop)))
|
|
487
545
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
488
546
|
|
|
489
|
-
// Coerce value to string: numbers → __ftoa,
|
|
490
|
-
|
|
491
|
-
|
|
547
|
+
// Coerce value to string: numbers → __ftoa, nullish → static strings,
|
|
548
|
+
// plain NaN → "NaN", arrays → join(","), other string-like pointers pass through.
|
|
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)))
|
|
492
552
|
;; Not NaN → number, convert
|
|
493
|
-
(if (f64.eq (local.get $
|
|
494
|
-
(then (return (call $__ftoa (local.get $
|
|
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))))))
|
|
495
559
|
(local.set $type (call $__ptr_type (local.get $val)))
|
|
496
560
|
;; Plain NaN (type=0) → "NaN" string
|
|
497
561
|
(if (i32.eqz (local.get $type))
|
|
498
|
-
(then (return (call $__static_str (i32.const 0)))))
|
|
562
|
+
(then (return (i64.reinterpret_f64 (call $__static_str (i32.const 0))))))
|
|
499
563
|
;; Array (type=1) → join(",") like JS Array.toString()
|
|
500
564
|
(if (i32.eq (local.get $type) (i32.const ${PTR.ARRAY}))
|
|
501
|
-
(then (return (call $__str_join (local.get $val)
|
|
502
|
-
(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))))))))
|
|
503
567
|
(local.get $val))`
|
|
504
568
|
|
|
505
569
|
// Copy bytes of a string (SSO or heap) into memory at dst. Uses memory.copy for
|
|
506
|
-
// heap strings (single native op); unpacks SSO
|
|
507
|
-
ctx.core.stdlib['__str_copy'] = `(func $__str_copy (param $src
|
|
508
|
-
(local $
|
|
509
|
-
(
|
|
510
|
-
(if (i32.eq
|
|
511
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $bits) (i64.const 47)) (i64.const 0xF)))
|
|
512
|
-
(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))
|
|
513
574
|
(then
|
|
514
575
|
;; SSO: up to 4 chars packed in low 32 bits (LE byte order). Unroll: write 1/2/3/4 bytes
|
|
515
576
|
;; depending on len. (len > 4 is rare/disallowed in practice — fallback handles up to 4.)
|
|
516
|
-
(local.set $w (i32.wrap_i64 (local.get $
|
|
577
|
+
(local.set $w (i32.wrap_i64 (local.get $src)))
|
|
517
578
|
(if (i32.ge_u (local.get $len) (i32.const 4))
|
|
518
579
|
(then (i32.store (local.get $dst) (local.get $w)))
|
|
519
580
|
(else
|
|
@@ -526,11 +587,107 @@ export default (ctx) => {
|
|
|
526
587
|
(else
|
|
527
588
|
;; Heap STRING: memory.copy directly from string data
|
|
528
589
|
(memory.copy (local.get $dst)
|
|
529
|
-
(i32.wrap_i64 (i64.and (local.get $
|
|
590
|
+
(i32.wrap_i64 (i64.and (local.get $src) (i64.const ${LAYOUT.OFFSET_MASK})))
|
|
530
591
|
(local.get $len)))))`
|
|
531
592
|
|
|
532
|
-
|
|
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)
|
|
533
689
|
(local $alen i32) (local $blen i32) (local $total i32) (local $off i32)
|
|
690
|
+
(local $ta i32) (local $aoff i32) (local $newHeap i32)
|
|
534
691
|
;; Coerce operands to strings if needed
|
|
535
692
|
(local.set $a (call $__to_str (local.get $a)))
|
|
536
693
|
(local.set $b (call $__to_str (local.get $b)))
|
|
@@ -538,7 +695,8 @@ export default (ctx) => {
|
|
|
538
695
|
(local.set $blen (call $__str_byteLen (local.get $b)))
|
|
539
696
|
(local.set $total (i32.add (local.get $alen) (local.get $blen)))
|
|
540
697
|
(if (i32.eqz (local.get $total))
|
|
541
|
-
(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}
|
|
542
700
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $total))))
|
|
543
701
|
(i32.store (local.get $off) (local.get $total))
|
|
544
702
|
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
@@ -546,13 +704,15 @@ export default (ctx) => {
|
|
|
546
704
|
(call $__str_copy (local.get $b) (i32.add (local.get $off) (local.get $alen)) (local.get $blen))
|
|
547
705
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
548
706
|
|
|
549
|
-
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)
|
|
550
708
|
(local $alen i32) (local $blen i32) (local $total i32) (local $off i32)
|
|
709
|
+
(local $ta i32) (local $aoff i32) (local $newHeap i32)
|
|
551
710
|
(local.set $alen (call $__str_byteLen (local.get $a)))
|
|
552
711
|
(local.set $blen (call $__str_byteLen (local.get $b)))
|
|
553
712
|
(local.set $total (i32.add (local.get $alen) (local.get $blen)))
|
|
554
713
|
(if (i32.eqz (local.get $total))
|
|
555
|
-
(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}
|
|
556
716
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $total))))
|
|
557
717
|
(i32.store (local.get $off) (local.get $total))
|
|
558
718
|
(local.set $off (i32.add (local.get $off) (i32.const 4)))
|
|
@@ -560,39 +720,39 @@ export default (ctx) => {
|
|
|
560
720
|
(call $__str_copy (local.get $b) (i32.add (local.get $off) (local.get $alen)) (local.get $blen))
|
|
561
721
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`
|
|
562
722
|
|
|
563
|
-
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)
|
|
564
724
|
(local $idx i32) (local $slen i32)
|
|
565
725
|
(local.set $idx (call $__str_indexof (local.get $str) (local.get $search) (i32.const 0)))
|
|
566
726
|
(if (result f64) (i32.lt_s (local.get $idx) (i32.const 0))
|
|
567
|
-
(then (local.get $str))
|
|
727
|
+
(then (f64.reinterpret_i64 (local.get $str)))
|
|
568
728
|
(else
|
|
569
729
|
(local.set $slen (call $__str_byteLen (local.get $search)))
|
|
570
730
|
(call $__str_concat
|
|
571
|
-
(call $__str_concat
|
|
572
|
-
(call $__str_slice (local.get $str) (i32.const 0) (local.get $idx))
|
|
573
|
-
(local.get $repl))
|
|
574
|
-
(call $__str_slice (local.get $str) (i32.add (local.get $idx) (local.get $slen))
|
|
575
|
-
(call $__str_byteLen (local.get $str)))))))`
|
|
576
|
-
|
|
577
|
-
ctx.core.stdlib['__str_replaceall'] = `(func $__str_replaceall (param $str
|
|
578
|
-
(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)
|
|
579
739
|
(local.set $slen (call $__str_byteLen (local.get $search)))
|
|
580
740
|
(local.set $result (local.get $str))
|
|
581
741
|
(local.set $pos (i32.const 0))
|
|
582
742
|
(block $done (loop $next
|
|
583
743
|
(local.set $idx (call $__str_indexof (local.get $result) (local.get $search) (local.get $pos)))
|
|
584
744
|
(br_if $done (i32.lt_s (local.get $idx) (i32.const 0)))
|
|
585
|
-
(local.set $result (call $__str_concat
|
|
586
|
-
(call $__str_concat
|
|
587
|
-
(call $__str_slice (local.get $result) (i32.const 0) (local.get $idx))
|
|
588
|
-
(local.get $repl))
|
|
589
|
-
(call $__str_slice (local.get $result) (i32.add (local.get $idx) (local.get $slen))
|
|
590
|
-
(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)))))))
|
|
591
751
|
(local.set $pos (i32.add (local.get $idx) (call $__str_byteLen (local.get $repl))))
|
|
592
752
|
(br $next)))
|
|
593
|
-
(local.get $result))`
|
|
753
|
+
(f64.reinterpret_i64 (local.get $result)))`
|
|
594
754
|
|
|
595
|
-
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)
|
|
596
756
|
(local $slen i32) (local $plen i32) (local $count i32)
|
|
597
757
|
(local $i i32) (local $j i32) (local $match i32)
|
|
598
758
|
(local $arr i32) (local $piece_start i32) (local $piece_idx i32)
|
|
@@ -648,32 +808,32 @@ export default (ctx) => {
|
|
|
648
808
|
(call $__str_slice (local.get $str) (local.get $piece_start) (local.get $slen)))
|
|
649
809
|
(call $__mkptr (i32.const 1) (i32.const 0) (local.get $arr)))`
|
|
650
810
|
|
|
651
|
-
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)
|
|
652
812
|
(local $off i32) (local $len i32) (local $i i32) (local $result f64)
|
|
653
813
|
(local.set $off (call $__ptr_offset (local.get $arr)))
|
|
654
814
|
(local.set $len (call $__len (local.get $arr)))
|
|
655
815
|
(if (i32.eqz (local.get $len))
|
|
656
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
657
|
-
(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)))))
|
|
658
818
|
(local.set $i (i32.const 1))
|
|
659
819
|
(block $done (loop $loop
|
|
660
820
|
(br_if $done (i32.ge_s (local.get $i) (local.get $len)))
|
|
661
|
-
(local.set $result (call $__str_concat (local.get $result) (local.get $sep)))
|
|
662
|
-
(local.set $result (call $__str_concat (local.get $result)
|
|
663
|
-
(
|
|
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))))))
|
|
664
824
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
665
825
|
(br $loop)))
|
|
666
826
|
(local.get $result))`
|
|
667
827
|
|
|
668
828
|
// Source string copied via __str_copy (handles SSO/heap with memory.copy where possible).
|
|
669
829
|
// Pad fill loops a single tile of pad bytes — hoist pad dispatch out of the byte loop.
|
|
670
|
-
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)
|
|
671
831
|
(local $slen i32) (local $plen i32) (local $fill i32) (local $off i32) (local $i i32)
|
|
672
832
|
(local $str_off i32) (local $pad_off i32)
|
|
673
833
|
(local $pbits i64) (local $poff i32) (local $psso i32)
|
|
674
834
|
(local.set $slen (call $__str_byteLen (local.get $str)))
|
|
675
835
|
(if (i32.ge_s (local.get $slen) (local.get $target))
|
|
676
|
-
(then (return (local.get $str))))
|
|
836
|
+
(then (return (f64.reinterpret_i64 (local.get $str)))))
|
|
677
837
|
(local.set $plen (call $__str_byteLen (local.get $pad)))
|
|
678
838
|
(local.set $fill (i32.sub (local.get $target) (local.get $slen)))
|
|
679
839
|
(local.set $off (call $__alloc (i32.add (i32.const 4) (local.get $target))))
|
|
@@ -682,11 +842,11 @@ export default (ctx) => {
|
|
|
682
842
|
(local.set $str_off (select (local.get $fill) (i32.const 0) (local.get $before)))
|
|
683
843
|
(local.set $pad_off (select (i32.const 0) (local.get $slen) (local.get $before)))
|
|
684
844
|
(call $__str_copy (local.get $str) (i32.add (local.get $off) (local.get $str_off)) (local.get $slen))
|
|
685
|
-
(local.set $pbits (
|
|
686
|
-
(local.set $poff (i32.wrap_i64 (i64.and (local.get $pbits) (i64.const
|
|
687
|
-
(local.set $psso (i32.
|
|
688
|
-
(i32.wrap_i64 (i64.
|
|
689
|
-
(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})))
|
|
690
850
|
(block $d2 (loop $l2
|
|
691
851
|
(br_if $d2 (i32.ge_s (local.get $i) (local.get $fill)))
|
|
692
852
|
(i32.store8 (i32.add (local.get $off) (i32.add (local.get $pad_off) (local.get $i)))
|
|
@@ -706,36 +866,51 @@ export default (ctx) => {
|
|
|
706
866
|
// === Method emitters ===
|
|
707
867
|
|
|
708
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
|
+
|
|
709
884
|
ctx.core.emit['.string:slice'] = (str, start, end) => {
|
|
710
885
|
inc('__str_slice')
|
|
711
|
-
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')
|
|
712
887
|
const t = temp('t')
|
|
713
888
|
return typed(['block', ['result', 'f64'],
|
|
714
889
|
['local.set', `$${t}`, asF64(emit(str))],
|
|
715
|
-
['call', '$__str_slice', ['local.get', `$${t}`], asI32(emit(start)),
|
|
716
|
-
['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')
|
|
717
892
|
}
|
|
718
893
|
|
|
719
894
|
ctx.core.emit['.string:indexOf'] = (str, search, from) => {
|
|
720
895
|
inc('__str_indexof')
|
|
721
|
-
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')
|
|
722
897
|
}
|
|
723
898
|
|
|
724
899
|
ctx.core.emit['.string:includes'] = (str, search) => {
|
|
725
900
|
inc('__str_indexof')
|
|
726
901
|
return typed(['f64.convert_i32_s',
|
|
727
|
-
['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')
|
|
728
903
|
}
|
|
729
904
|
|
|
730
905
|
// Generic (no collision)
|
|
731
906
|
ctx.core.emit['.substring'] = (str, start, end) => {
|
|
732
907
|
inc('__str_substring')
|
|
733
|
-
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')
|
|
734
909
|
const t = temp('t')
|
|
735
910
|
return typed(['block', ['result', 'f64'],
|
|
736
911
|
['local.set', `$${t}`, asF64(emit(str))],
|
|
737
|
-
['call', '$__str_substring', ['local.get', `$${t}`], asI32(emit(start)),
|
|
738
|
-
['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')
|
|
739
914
|
}
|
|
740
915
|
|
|
741
916
|
// Factory for simple str→call patterns: [emitKey, stdlibName, argCoercions, i32Result?]
|
|
@@ -746,44 +921,109 @@ export default (ctx) => {
|
|
|
746
921
|
return typed(i32Result ? ['f64.convert_i32_s', call] : call, 'f64')
|
|
747
922
|
}
|
|
748
923
|
|
|
749
|
-
//
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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'))
|
|
759
951
|
|
|
760
952
|
ctx.core.emit['.toUpperCase'] = (str) => {
|
|
761
953
|
inc('__str_case')
|
|
762
|
-
return typed(['call', '$__str_case',
|
|
954
|
+
return typed(['call', '$__str_case', asI64(emit(str)), ['i32.const', 97], ['i32.const', 122], ['i32.const', -32]], 'f64')
|
|
763
955
|
}
|
|
764
956
|
|
|
765
957
|
ctx.core.emit['.toLowerCase'] = (str) => {
|
|
766
958
|
inc('__str_case')
|
|
767
|
-
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
|
+
// Byte-wise variant of String.prototype.localeCompare. Returns -1/0/1 from
|
|
963
|
+
// an unsigned byte-by-byte compare with shorter-string-sorts-first tiebreak.
|
|
964
|
+
// NOT locale-aware: real localeCompare is ICU-driven (CLDR collation, case
|
|
965
|
+
// folding, accent ordering). For ASCII inputs the byte-wise result matches
|
|
966
|
+
// the spec exactly; for non-ASCII it follows UTF-8 byte order, which is
|
|
967
|
+
// codepoint order for well-formed strings — close enough for sort-stability
|
|
968
|
+
// use cases, wrong for human-language collation.
|
|
969
|
+
ctx.core.emit['.localeCompare'] = (str, other) => {
|
|
970
|
+
inc('__str_cmp')
|
|
971
|
+
return typed(['f64.convert_i32_s', ['call', '$__str_cmp', asI64(emit(str)), asI64(emit(other))]], 'f64')
|
|
768
972
|
}
|
|
769
973
|
|
|
770
974
|
ctx.core.emit['.string:concat'] = (str, ...others) => {
|
|
771
975
|
inc('__str_concat')
|
|
772
976
|
let result = asF64(emit(str))
|
|
773
|
-
for (const other of others) result = typed(['call', '$__str_concat', result,
|
|
977
|
+
for (const other of others) result = typed(['call', '$__str_concat', ['i64.reinterpret_f64', result], asI64(emit(other))], 'f64')
|
|
774
978
|
return result
|
|
775
979
|
}
|
|
776
980
|
|
|
981
|
+
ctx.core.emit['strcat'] = (...parts) => {
|
|
982
|
+
inc('__to_str', '__str_byteLen', '__alloc', '__mkptr', '__str_copy')
|
|
983
|
+
if (!parts.length) return mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT, 0)
|
|
984
|
+
if (parts.length === 1) return typed(['f64.reinterpret_i64', ['call', '$__to_str', asI64(emit(parts[0]))]], 'f64')
|
|
985
|
+
|
|
986
|
+
const vals = parts.map(() => temp('s'))
|
|
987
|
+
const lens = parts.map(() => tempI32('sl'))
|
|
988
|
+
const total = tempI32('st')
|
|
989
|
+
const off = tempI32('so')
|
|
990
|
+
const dst = tempI32('sd')
|
|
991
|
+
const ir = []
|
|
992
|
+
|
|
993
|
+
for (let i = 0; i < parts.length; i++) {
|
|
994
|
+
ir.push(['local.set', `$${vals[i]}`, ['f64.reinterpret_i64', ['call', '$__to_str', asI64(emit(parts[i]))]]])
|
|
995
|
+
ir.push(['local.set', `$${lens[i]}`, ['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${vals[i]}`]]]])
|
|
996
|
+
}
|
|
997
|
+
ir.push(['local.set', `$${total}`, ['i32.const', 0]])
|
|
998
|
+
for (const len of lens)
|
|
999
|
+
ir.push(['local.set', `$${total}`, ['i32.add', ['local.get', `$${total}`], ['local.get', `$${len}`]]])
|
|
1000
|
+
const alloc = [
|
|
1001
|
+
['local.set', `$${off}`, ['call', '$__alloc', ['i32.add', ['i32.const', 4], ['local.get', `$${total}`]]]],
|
|
1002
|
+
['i32.store', ['local.get', `$${off}`], ['local.get', `$${total}`]],
|
|
1003
|
+
['local.set', `$${off}`, ['i32.add', ['local.get', `$${off}`], ['i32.const', 4]]],
|
|
1004
|
+
['local.set', `$${dst}`, ['local.get', `$${off}`]],
|
|
1005
|
+
]
|
|
1006
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1007
|
+
alloc.push(['call', '$__str_copy', ['i64.reinterpret_f64', ['local.get', `$${vals[i]}`]], ['local.get', `$${dst}`], ['local.get', `$${lens[i]}`]])
|
|
1008
|
+
alloc.push(['local.set', `$${dst}`, ['i32.add', ['local.get', `$${dst}`], ['local.get', `$${lens[i]}`]]])
|
|
1009
|
+
}
|
|
1010
|
+
alloc.push(['call', '$__mkptr', ['i32.const', PTR.STRING], ['i32.const', 0], ['local.get', `$${off}`]])
|
|
1011
|
+
ir.push(['if', ['result', 'f64'], ['i32.eqz', ['local.get', `$${total}`]],
|
|
1012
|
+
['then', mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT, 0)],
|
|
1013
|
+
['else', ['block', ['result', 'f64'], ...alloc]]])
|
|
1014
|
+
return typed(['block', ['result', 'f64'], ...ir], 'f64')
|
|
1015
|
+
}
|
|
1016
|
+
|
|
777
1017
|
ctx.core.emit['.padStart'] = (str, len, pad) => {
|
|
778
1018
|
inc('__str_pad')
|
|
779
|
-
const vpad = pad != null ?
|
|
780
|
-
return typed(['call', '$__str_pad',
|
|
1019
|
+
const vpad = pad != null ? asI64(emit(pad)) : ['i64.reinterpret_f64', mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, 32)]
|
|
1020
|
+
return typed(['call', '$__str_pad', asI64(emit(str)), asI32(emit(len)), vpad, ['i32.const', 1]], 'f64')
|
|
781
1021
|
}
|
|
782
1022
|
|
|
783
1023
|
ctx.core.emit['.padEnd'] = (str, len, pad) => {
|
|
784
1024
|
inc('__str_pad')
|
|
785
|
-
const vpad = pad != null ?
|
|
786
|
-
return typed(['call', '$__str_pad',
|
|
1025
|
+
const vpad = pad != null ? asI64(emit(pad)) : ['i64.reinterpret_f64', mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, 32)]
|
|
1026
|
+
return typed(['call', '$__str_pad', asI64(emit(str)), asI32(emit(len)), vpad, ['i32.const', 0]], 'f64')
|
|
787
1027
|
}
|
|
788
1028
|
|
|
789
1029
|
// .charAt(i) → 1-char string from char code at index i
|
|
@@ -792,8 +1032,8 @@ export default (ctx) => {
|
|
|
792
1032
|
const t = tempI32('ch')
|
|
793
1033
|
// Get char code, create SSO string with 1 byte
|
|
794
1034
|
return typed(['block', ['result', 'f64'],
|
|
795
|
-
['local.set', `$${t}`, ['call', '$__char_at',
|
|
796
|
-
mkPtrIR(PTR.
|
|
1035
|
+
['local.set', `$${t}`, ['call', '$__char_at', asI64(emit(str)), asI32(emit(idx))]],
|
|
1036
|
+
mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, ['local.get', `$${t}`])], 'f64')
|
|
797
1037
|
}
|
|
798
1038
|
|
|
799
1039
|
// .charCodeAt(i) → integer char code (0..255 for ASCII bytes — unsigned, always
|
|
@@ -802,33 +1042,47 @@ export default (ctx) => {
|
|
|
802
1042
|
// `c - 48` arithmetic skip the per-iteration f64 widen + i32 trunc round-trip.
|
|
803
1043
|
ctx.core.emit['.charCodeAt'] = (str, idx) => {
|
|
804
1044
|
inc('__char_at')
|
|
805
|
-
return typed(['call', '$__char_at',
|
|
1045
|
+
return typed(['call', '$__char_at', asI64(emit(str)), asI32(emit(idx))], 'i32')
|
|
806
1046
|
}
|
|
807
1047
|
|
|
808
1048
|
// String.fromCharCode(code) → 1-char SSO string
|
|
809
|
-
ctx.core.emit['String
|
|
1049
|
+
ctx.core.emit['String'] = (value) => {
|
|
1050
|
+
if (value === undefined) return emit(['str', ''])
|
|
1051
|
+
if (valTypeOf(value) === VAL.STRING) return emit(value)
|
|
1052
|
+
if (valTypeOf(value) === VAL.NUMBER) {
|
|
1053
|
+
inc('__ftoa')
|
|
1054
|
+
return typed(['call', '$__ftoa', asF64(emit(value)), ['i32.const', 0], ['i32.const', 0]], 'f64')
|
|
1055
|
+
}
|
|
1056
|
+
inc('__to_str')
|
|
1057
|
+
return typed(['f64.reinterpret_i64', ['call', '$__to_str', asI64(emit(value))]], 'f64')
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
ctx.core.emit['String.fromCharCode'] = (code) => {
|
|
1061
|
+
if (code === undefined) return emit(['str', ''])
|
|
1062
|
+
return mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, asI32(emit(code)))
|
|
1063
|
+
}
|
|
810
1064
|
|
|
811
1065
|
// String.fromCodePoint(cp) → UTF-8 encoded string
|
|
812
1066
|
ctx.core.stdlib['__fromCodePoint'] = `(func $__fromCodePoint (param $cp i32) (result f64)
|
|
813
1067
|
(local $off i32) (local $len i32)
|
|
814
1068
|
;; ASCII: 1 byte SSO
|
|
815
1069
|
(if (i32.lt_u (local.get $cp) (i32.const 128))
|
|
816
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
1070
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 1}) (local.get $cp)))))
|
|
817
1071
|
;; 2-byte: 0x80-0x7FF → SSO
|
|
818
1072
|
(if (i32.lt_u (local.get $cp) (i32.const 0x800))
|
|
819
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
1073
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 2})
|
|
820
1074
|
(i32.or
|
|
821
1075
|
(i32.or (i32.const 0xC0) (i32.shr_u (local.get $cp) (i32.const 6)))
|
|
822
1076
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 8)))))))
|
|
823
1077
|
;; 3-byte: 0x800-0xFFFF → SSO (3 bytes fits)
|
|
824
1078
|
(if (i32.lt_u (local.get $cp) (i32.const 0x10000))
|
|
825
|
-
(then (return (call $__mkptr (i32.const ${PTR.
|
|
1079
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 3})
|
|
826
1080
|
(i32.or (i32.or
|
|
827
1081
|
(i32.or (i32.const 0xE0) (i32.shr_u (local.get $cp) (i32.const 12)))
|
|
828
1082
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 6)) (i32.const 0x3F))) (i32.const 8)))
|
|
829
1083
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 16)))))))
|
|
830
1084
|
;; 4-byte: 0x10000-0x10FFFF → SSO (4 bytes fits)
|
|
831
|
-
(return (call $__mkptr (i32.const ${PTR.
|
|
1085
|
+
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT | 4})
|
|
832
1086
|
(i32.or (i32.or (i32.or
|
|
833
1087
|
(i32.or (i32.const 0xF0) (i32.shr_u (local.get $cp) (i32.const 18)))
|
|
834
1088
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 12)) (i32.const 0x3F))) (i32.const 8)))
|
|
@@ -836,12 +1090,13 @@ export default (ctx) => {
|
|
|
836
1090
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 24))))))`
|
|
837
1091
|
|
|
838
1092
|
ctx.core.emit['String.fromCodePoint'] = (code) => {
|
|
1093
|
+
if (code === undefined) return emit(['str', ''])
|
|
839
1094
|
inc('__fromCodePoint')
|
|
840
1095
|
return typed(['call', '$__fromCodePoint', asI32(emit(code))], 'f64')
|
|
841
1096
|
}
|
|
842
1097
|
|
|
843
1098
|
// .at(i) → charAt with negative index support
|
|
844
|
-
ctx.core.emit['.at'] = (str, idx) => {
|
|
1099
|
+
ctx.core.emit['.string:at'] = (str, idx) => {
|
|
845
1100
|
inc('__char_at', '__str_byteLen')
|
|
846
1101
|
const t = tempI32('at'), s = temp('as')
|
|
847
1102
|
return typed(['block', ['result', 'f64'],
|
|
@@ -850,14 +1105,14 @@ export default (ctx) => {
|
|
|
850
1105
|
// Negative index: t += length
|
|
851
1106
|
['if', ['i32.lt_s', ['local.get', `$${t}`], ['i32.const', 0]],
|
|
852
1107
|
['then', ['local.set', `$${t}`, ['i32.add', ['local.get', `$${t}`],
|
|
853
|
-
['call', '$__str_byteLen', ['local.get', `$${s}`]]]]]],
|
|
854
|
-
mkPtrIR(PTR.
|
|
1108
|
+
['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]]]],
|
|
1109
|
+
mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | 1, ['call', '$__char_at', ['i64.reinterpret_f64', ['local.get', `$${s}`]], ['local.get', `$${t}`]])], 'f64')
|
|
855
1110
|
}
|
|
856
1111
|
|
|
857
1112
|
// .search(str) → indexOf (same as indexOf for string args)
|
|
858
1113
|
ctx.core.emit['.search'] = (str, search) => {
|
|
859
1114
|
inc('__str_indexof')
|
|
860
|
-
return typed(['f64.convert_i32_s', ['call', '$__str_indexof',
|
|
1115
|
+
return typed(['f64.convert_i32_s', ['call', '$__str_indexof', asI64(emit(str)), asI64(emit(search)), ['i32.const', 0]]], 'f64')
|
|
861
1116
|
}
|
|
862
1117
|
|
|
863
1118
|
// .match(str) → [match] array if found, or 0 (null) if not
|
|
@@ -869,24 +1124,25 @@ export default (ctx) => {
|
|
|
869
1124
|
return typed(['block', ['result', 'f64'],
|
|
870
1125
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
871
1126
|
['local.set', `$${q}`, asF64(emit(search))],
|
|
872
|
-
['local.set', `$${idx}`, ['call', '$__str_indexof', ['local.get', `$${s}`], ['local.get', `$${q}`], ['i32.const', 0]]],
|
|
1127
|
+
['local.set', `$${idx}`, ['call', '$__str_indexof', ['i64.reinterpret_f64', ['local.get', `$${s}`]], ['i64.reinterpret_f64', ['local.get', `$${q}`]], ['i32.const', 0]]],
|
|
873
1128
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${idx}`], ['i32.const', 0]],
|
|
874
1129
|
['then', ['f64.const', 0]], // null
|
|
875
1130
|
['else',
|
|
876
1131
|
// Build 1-element array containing the search string
|
|
877
1132
|
['call', '$__wrap1',
|
|
878
|
-
['
|
|
879
|
-
['local.get', `$${
|
|
880
|
-
|
|
1133
|
+
['i64.reinterpret_f64',
|
|
1134
|
+
['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${s}`]],
|
|
1135
|
+
['local.get', `$${idx}`],
|
|
1136
|
+
['i32.add', ['local.get', `$${idx}`], ['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${q}`]]]]]]]]]], 'f64')
|
|
881
1137
|
}
|
|
882
1138
|
|
|
883
|
-
// __wrap1(val:
|
|
884
|
-
ctx.core.stdlib['__wrap1'] = `(func $__wrap1 (param $val
|
|
1139
|
+
// __wrap1(val: i64) → f64 — create 1-element array [val]
|
|
1140
|
+
ctx.core.stdlib['__wrap1'] = `(func $__wrap1 (param $val i64) (result f64)
|
|
885
1141
|
(local $ptr i32)
|
|
886
1142
|
(local.set $ptr (call $__alloc (i32.const 16)))
|
|
887
1143
|
(i32.store (local.get $ptr) (i32.const 1))
|
|
888
1144
|
(i32.store (i32.add (local.get $ptr) (i32.const 4)) (i32.const 1))
|
|
889
|
-
(
|
|
1145
|
+
(i64.store (i32.add (local.get $ptr) (i32.const 8)) (local.get $val))
|
|
890
1146
|
(call $__mkptr (i32.const 1) (i32.const 0) (i32.add (local.get $ptr) (i32.const 8))))`
|
|
891
1147
|
|
|
892
1148
|
// TextEncoder() / TextDecoder() → dummy values (methods do the work)
|
|
@@ -895,7 +1151,7 @@ export default (ctx) => {
|
|
|
895
1151
|
|
|
896
1152
|
// .encode(str) → Uint8Array of string's UTF-8 bytes
|
|
897
1153
|
// Copies bytes from string (SSO or heap) into a new Uint8Array
|
|
898
|
-
ctx.core.stdlib['__str_encode'] = `(func $__str_encode (param $str
|
|
1154
|
+
ctx.core.stdlib['__str_encode'] = `(func $__str_encode (param $str i64) (result f64)
|
|
899
1155
|
(local $len i32) (local $dst i32)
|
|
900
1156
|
(local.set $len (call $__str_byteLen (local.get $str)))
|
|
901
1157
|
(local.set $dst (call $__alloc (i32.add (i32.const 8) (local.get $len))))
|
|
@@ -907,11 +1163,11 @@ export default (ctx) => {
|
|
|
907
1163
|
|
|
908
1164
|
ctx.core.emit['.encode'] = (obj, str) => {
|
|
909
1165
|
inc('__str_encode')
|
|
910
|
-
return typed(['call', '$__str_encode',
|
|
1166
|
+
return typed(['call', '$__str_encode', asI64(emit(str))], 'f64')
|
|
911
1167
|
}
|
|
912
1168
|
|
|
913
1169
|
// .decode(uint8arr) → string from byte data
|
|
914
|
-
ctx.core.stdlib['__bytes_decode'] = `(func $__bytes_decode (param $arr
|
|
1170
|
+
ctx.core.stdlib['__bytes_decode'] = `(func $__bytes_decode (param $arr i64) (result f64)
|
|
915
1171
|
(local $off i32) (local $len i32) (local $dst i32)
|
|
916
1172
|
(local.set $off (call $__ptr_offset (local.get $arr)))
|
|
917
1173
|
(local.set $len (call $__len (local.get $arr)))
|
|
@@ -923,6 +1179,6 @@ export default (ctx) => {
|
|
|
923
1179
|
|
|
924
1180
|
ctx.core.emit['.decode'] = (obj, arr) => {
|
|
925
1181
|
inc('__bytes_decode')
|
|
926
|
-
return typed(['call', '$__bytes_decode',
|
|
1182
|
+
return typed(['call', '$__bytes_decode', asI64(emit(arr))], 'f64')
|
|
927
1183
|
}
|
|
928
1184
|
}
|