jz 0.9.0 → 0.9.2
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 +19 -7
- package/bench/bench.svg +18 -18
- package/dist/interop.js +1 -1
- package/dist/jz.js +2026 -1429
- package/interop.js +73 -6
- package/jzify/index.js +7 -1
- package/jzify/transform.js +21 -2
- package/jzify/webrt.js +145 -0
- package/layout.js +13 -3
- package/module/array.js +49 -42
- package/module/collection.js +222 -111
- package/module/console.js +4 -0
- package/module/core.js +86 -7
- package/module/crypto.js +124 -0
- package/module/index.js +3 -1
- package/module/math.js +5 -0
- package/module/navigator.js +26 -0
- package/module/schema.js +11 -3
- package/module/string.js +433 -28
- package/module/timer.js +42 -1
- package/module/typedarray.js +370 -60
- package/package.json +3 -3
- package/src/abi/array.js +24 -16
- package/src/abi/index.js +2 -2
- package/src/abi/object.js +17 -0
- package/src/ast.js +53 -0
- package/src/autoload.js +19 -3
- package/src/compile/analyze.js +190 -21
- package/src/compile/emit-assign.js +111 -7
- package/src/compile/emit.js +84 -20
- package/src/compile/index.js +37 -4
- package/src/compile/inplace-store.js +10 -9
- package/src/compile/narrow.js +71 -1
- package/src/compile/plan/index.js +5 -0
- package/src/compile/plan/literals.js +11 -2
- package/src/ctx.js +14 -0
- package/src/ir.js +26 -1
- package/src/optimize/index.js +1 -0
- package/src/optimize/vectorize.js +80 -6
- package/src/prepare/index.js +6 -0
- package/src/static.js +31 -0
- package/src/type.js +356 -47
package/module/string.js
CHANGED
|
@@ -186,7 +186,7 @@ export default (ctx) => {
|
|
|
186
186
|
__str_split: ['__str_slice', '__str_byteLen', '__char_at', '__alloc'],
|
|
187
187
|
__str_idx: ['__char1byte'],
|
|
188
188
|
__sso_norm: [],
|
|
189
|
-
__bytes_decode: ['
|
|
189
|
+
__bytes_decode: ['__typed_data', '__len', '__alloc', '__mkptr', '__sso_norm'],
|
|
190
190
|
__str_eq: ['__str_eq_cold'],
|
|
191
191
|
__str_eq_cold: ['__char_at', '__str_byteLen'],
|
|
192
192
|
__str_cmp: ['__char_at', '__str_byteLen'],
|
|
@@ -198,7 +198,22 @@ export default (ctx) => {
|
|
|
198
198
|
__str_encode: ['__str_byteLen', '__str_copy'],
|
|
199
199
|
__encodeURIComponent: ['__to_str', '__str_byteLen', '__char_at', '__alloc', '__mkptr', '__sso_norm'],
|
|
200
200
|
__decodeURIComponent: ['__to_str', '__str_byteLen', '__char_at', '__alloc', '__mkptr', '__uri_hex', '__sso_norm'],
|
|
201
|
+
__encodeURI: ['__to_str', '__str_byteLen', '__char_at', '__alloc', '__mkptr', '__sso_norm'],
|
|
202
|
+
__decodeURI: ['__to_str', '__str_byteLen', '__char_at', '__alloc', '__mkptr', '__uri_hex', '__sso_norm'],
|
|
201
203
|
__uri_hex: [],
|
|
204
|
+
// base64/hex codec family (Uint8Array.fromBase64/toBase64/…, atob/btoa)
|
|
205
|
+
__b64_enc: ['__alloc', '__mkptr', '__sso_norm'],
|
|
206
|
+
__b64_dec_raw: ['__str_byteLen', '__char_at'],
|
|
207
|
+
__btoa: ['__to_str', '__str_byteLen', '__str_copy', '__alloc', '__b64_enc'],
|
|
208
|
+
__atob: ['__to_str', '__str_byteLen', '__alloc', '__b64_dec_raw', '__mkptr', '__sso_norm'],
|
|
209
|
+
__b64_from: ['__to_str', '__str_byteLen', '__alloc', '__b64_dec_raw', '__mkptr'],
|
|
210
|
+
__b64_set: ['__b64_dec_raw', '__u8_data', '__len'],
|
|
211
|
+
__hex_enc: ['__alloc', '__mkptr', '__sso_norm'],
|
|
212
|
+
__hex_dec_raw: ['__str_byteLen', '__char_at', '__uri_hex'],
|
|
213
|
+
__hex_from: ['__to_str', '__str_byteLen', '__alloc', '__hex_dec_raw', '__mkptr'],
|
|
214
|
+
__hex_set: ['__hex_dec_raw', '__u8_data', '__len'],
|
|
215
|
+
__u8_data: ['__ptr_type', '__ptr_aux', '__typed_data'],
|
|
216
|
+
__str_encode_into: ['__str_byteLen', '__char_at', '__str_copy', '__len', '__typed_data', '__ptr_type', '__ptr_aux'],
|
|
202
217
|
__to_str: ['__ftoa', '__static_str', '__str_join', '__mkptr'],
|
|
203
218
|
__str_byteLen: ['__ptr_type', '__ptr_aux', '__str_len'],
|
|
204
219
|
})
|
|
@@ -2147,19 +2162,18 @@ export default (ctx) => {
|
|
|
2147
2162
|
return r
|
|
2148
2163
|
})
|
|
2149
2164
|
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
(
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
(if (i32.or
|
|
2165
|
+
// Percent-codec char classes (ECMA-262 19.2.2–19.2.5). Both encoders share one
|
|
2166
|
+
// loop shape and differ only in the always-unescaped set: encodeURIComponent
|
|
2167
|
+
// passes only the unreserved marks; encodeURI additionally leaves the URI
|
|
2168
|
+
// reserved set `; / ? : @ & = + $ , #` intact. Same split decoding: decode-
|
|
2169
|
+
// URIComponent decodes every escape, decodeURI copies the original `%XX`
|
|
2170
|
+
// triplet through for reserved bytes (case-preserving). Generated kernels —
|
|
2171
|
+
// the unused variant dead-strips like any stdlib body.
|
|
2172
|
+
const URI_RESERVED = [59, 47, 63, 58, 64, 38, 61, 43, 36, 44, 35] // ; / ? : @ & = + $ , #
|
|
2173
|
+
const uriReservedTest = URI_RESERVED
|
|
2174
|
+
.map(c => `(i32.eq (local.get $c) (i32.const ${c}))`)
|
|
2175
|
+
.reduce((a, b) => `(i32.or ${a} ${b})`)
|
|
2176
|
+
const uriSafeTest = `(i32.or
|
|
2163
2177
|
(i32.or
|
|
2164
2178
|
(i32.or
|
|
2165
2179
|
(i32.and (i32.ge_u (local.get $c) (i32.const 65)) (i32.le_u (local.get $c) (i32.const 90)))
|
|
@@ -2173,7 +2187,21 @@ export default (ctx) => {
|
|
|
2173
2187
|
(i32.or (i32.eq (local.get $c) (i32.const 126)) (i32.eq (local.get $c) (i32.const 42)))
|
|
2174
2188
|
(i32.or
|
|
2175
2189
|
(i32.eq (local.get $c) (i32.const 39))
|
|
2176
|
-
(i32.or (i32.eq (local.get $c) (i32.const 40)) (i32.eq (local.get $c) (i32.const 41)))))))
|
|
2190
|
+
(i32.or (i32.eq (local.get $c) (i32.const 40)) (i32.eq (local.get $c) (i32.const 41)))))))`
|
|
2191
|
+
|
|
2192
|
+
const uriEncodeKernel = (name, keepReserved) => `(func $${name} (param $val i64) (result f64)
|
|
2193
|
+
(local $str i64) (local $slen i32) (local $base i32) (local $out i32)
|
|
2194
|
+
(local $i i32) (local $j i32) (local $c i32) (local $hi i32) (local $lo i32)
|
|
2195
|
+
(local.set $str (call $__to_str (local.get $val)))
|
|
2196
|
+
(local.set $slen (call $__str_byteLen (local.get $str)))
|
|
2197
|
+
(if (i32.eqz (local.get $slen))
|
|
2198
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
2199
|
+
(local.set $base (call $__alloc (i32.add (i32.const 4) (i32.mul (local.get $slen) (i32.const 3)))))
|
|
2200
|
+
(local.set $out (i32.add (local.get $base) (i32.const 4)))
|
|
2201
|
+
(block $done (loop $loop
|
|
2202
|
+
(br_if $done (i32.ge_u (local.get $i) (local.get $slen)))
|
|
2203
|
+
(local.set $c (call $__char_at (local.get $str) (local.get $i)))
|
|
2204
|
+
(if ${keepReserved ? `(i32.or ${uriSafeTest} ${uriReservedTest})` : uriSafeTest}
|
|
2177
2205
|
(then
|
|
2178
2206
|
(i32.store8 (i32.add (local.get $out) (local.get $j)) (local.get $c))
|
|
2179
2207
|
(local.set $j (i32.add (local.get $j) (i32.const 1))))
|
|
@@ -2189,13 +2217,18 @@ export default (ctx) => {
|
|
|
2189
2217
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
2190
2218
|
(br $loop)))
|
|
2191
2219
|
(i32.store (local.get $base) (local.get $j))
|
|
2192
|
-
(call $__sso_norm (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $out))))`
|
|
2220
|
+
(call $__sso_norm (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $out))))`
|
|
2221
|
+
|
|
2222
|
+
wat('__encodeURIComponent', uriEncodeKernel('__encodeURIComponent', false))
|
|
2223
|
+
wat('__encodeURI', uriEncodeKernel('__encodeURI', true))
|
|
2193
2224
|
|
|
2194
|
-
|
|
2195
|
-
inc(
|
|
2225
|
+
const uriEncodeBind = (kernel) => (value) => {
|
|
2226
|
+
inc(kernel)
|
|
2196
2227
|
const input = value === undefined ? ['i64.const', UNDEF_NAN] : asI64(emit(value))
|
|
2197
|
-
return typed(['call',
|
|
2198
|
-
}
|
|
2228
|
+
return typed(['call', `$${kernel}`, input], 'f64')
|
|
2229
|
+
}
|
|
2230
|
+
bind('encodeURIComponent', uriEncodeBind('__encodeURIComponent'))
|
|
2231
|
+
bind('encodeURI', uriEncodeBind('__encodeURI'))
|
|
2199
2232
|
|
|
2200
2233
|
wat('__uri_hex', `(func $__uri_hex (param $c i32) (result i32)
|
|
2201
2234
|
(if (result i32) (i32.and (i32.ge_u (local.get $c) (i32.const 48)) (i32.le_u (local.get $c) (i32.const 57)))
|
|
@@ -2206,7 +2239,19 @@ export default (ctx) => {
|
|
|
2206
2239
|
(then (i32.sub (local.get $c) (i32.const 87)))
|
|
2207
2240
|
(else (i32.const -1))))))))`)
|
|
2208
2241
|
|
|
2209
|
-
|
|
2242
|
+
// Reserved-byte passthrough for decodeURI: at this point $i still sits on the
|
|
2243
|
+
// `%`, the two hex chars are validated, and $c < 128 for every reserved code —
|
|
2244
|
+
// copy the ORIGINAL triplet (case-preserving per spec) and skip the store.
|
|
2245
|
+
const uriKeepReserved = `(if ${uriReservedTest} (then
|
|
2246
|
+
(i32.store8 (i32.add (local.get $dst) (local.get $outLen)) (i32.const 37))
|
|
2247
|
+
(i32.store8 (i32.add (local.get $dst) (i32.add (local.get $outLen) (i32.const 1)))
|
|
2248
|
+
(call $__char_at (local.get $s) (i32.add (local.get $i) (i32.const 1))))
|
|
2249
|
+
(i32.store8 (i32.add (local.get $dst) (i32.add (local.get $outLen) (i32.const 2)))
|
|
2250
|
+
(call $__char_at (local.get $s) (i32.add (local.get $i) (i32.const 2))))
|
|
2251
|
+
(local.set $outLen (i32.add (local.get $outLen) (i32.const 3)))
|
|
2252
|
+
(local.set $stored (i32.const 1))))`
|
|
2253
|
+
|
|
2254
|
+
const uriDecodeKernel = (name, keepReserved) => `(func $${name} (param $v i64) (result f64)
|
|
2210
2255
|
(local $s i64) (local $len i32) (local $i i32)
|
|
2211
2256
|
(local $base i32) (local $dst i32) (local $outLen i32)
|
|
2212
2257
|
(local $c i32) (local $hi i32) (local $lo i32)
|
|
@@ -2228,6 +2273,7 @@ export default (ctx) => {
|
|
|
2228
2273
|
(if (i32.or (i32.lt_s (local.get $hi) (i32.const 0)) (i32.lt_s (local.get $lo) (i32.const 0)))
|
|
2229
2274
|
(then (throw $__jz_err (f64.const 0))))
|
|
2230
2275
|
(local.set $c (i32.or (i32.shl (local.get $hi) (i32.const 4)) (local.get $lo)))
|
|
2276
|
+
${keepReserved ? uriKeepReserved : ''}
|
|
2231
2277
|
(local.set $i (i32.add (local.get $i) (i32.const 3)))
|
|
2232
2278
|
(if (i32.ge_u (local.get $c) (i32.const 128))
|
|
2233
2279
|
(then
|
|
@@ -2282,14 +2328,19 @@ export default (ctx) => {
|
|
|
2282
2328
|
(local.set $outLen (i32.add (local.get $outLen) (i32.const 1)))))
|
|
2283
2329
|
(br $loop)))
|
|
2284
2330
|
(i32.store (local.get $base) (local.get $outLen))
|
|
2285
|
-
(call $__sso_norm (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $dst))))`
|
|
2331
|
+
(call $__sso_norm (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $dst))))`
|
|
2332
|
+
|
|
2333
|
+
wat('__decodeURIComponent', uriDecodeKernel('__decodeURIComponent', false))
|
|
2334
|
+
wat('__decodeURI', uriDecodeKernel('__decodeURI', true))
|
|
2286
2335
|
|
|
2287
|
-
|
|
2336
|
+
const uriDecodeBind = (kernel) => (value) => {
|
|
2288
2337
|
ctx.runtime.throws = true
|
|
2289
|
-
inc(
|
|
2290
|
-
return typed(['call',
|
|
2338
|
+
inc(kernel)
|
|
2339
|
+
return typed(['call', `$${kernel}`,
|
|
2291
2340
|
value === undefined ? ['i64.const', UNDEF_NAN] : asI64(emit(value))], 'f64')
|
|
2292
|
-
}
|
|
2341
|
+
}
|
|
2342
|
+
bind('decodeURIComponent', uriDecodeBind('__decodeURIComponent'))
|
|
2343
|
+
bind('decodeURI', uriDecodeBind('__decodeURI'))
|
|
2293
2344
|
|
|
2294
2345
|
// .at(i) → 1-char string at index i with negative-index support, or undefined
|
|
2295
2346
|
// when out of range (JS spec: `i += length` for negative, then OOB → undefined).
|
|
@@ -2344,8 +2395,26 @@ export default (ctx) => {
|
|
|
2344
2395
|
(call $__mkptr (i32.const 1) (i32.const 0) (i32.add (local.get $ptr) (i32.const 8))))`)
|
|
2345
2396
|
|
|
2346
2397
|
// TextEncoder() / TextDecoder() → dummy values (methods do the work)
|
|
2398
|
+
// UTF-8 is the only encoding jz ships (encoding tables are the same
|
|
2399
|
+
// multi-KB cost Intl was refused for) — any other label would silently
|
|
2400
|
+
// decode UTF-8, so reject it at compile time.
|
|
2401
|
+
const decoderLabel = (label) => {
|
|
2402
|
+
if (label == null) return // no arg (a no-arg call pads a null slot)
|
|
2403
|
+
const lit = Array.isArray(label) && label[0] === 'str' ? String(label[1]).trim().toLowerCase() : null
|
|
2404
|
+
if (lit == null) err(`TextDecoder label must be a literal string — jz resolves encodings at compile time`)
|
|
2405
|
+
else if (lit !== 'utf-8' && lit !== 'utf8' && lit !== 'unicode-1-1-utf-8')
|
|
2406
|
+
err(`TextDecoder: only 'utf-8' is supported — other encodings need tables jz doesn't ship`)
|
|
2407
|
+
}
|
|
2408
|
+
// A chained receiver (`new TextDecoder(l).decode(x)`) is never emitted — the
|
|
2409
|
+
// methods ignore the dummy value — so validate the label at the receiver too.
|
|
2410
|
+
const decoderRecvCheck = (node) => {
|
|
2411
|
+
if (Array.isArray(node) && node[0] === '()' && node[1] === 'TextDecoder') decoderLabel(node[2])
|
|
2412
|
+
}
|
|
2347
2413
|
bind('TextEncoder', () => typed(['f64.const', 1], 'f64'))
|
|
2348
|
-
bind('TextDecoder', () =>
|
|
2414
|
+
bind('TextDecoder', (label) => {
|
|
2415
|
+
decoderLabel(label)
|
|
2416
|
+
return typed(['f64.const', 2], 'f64')
|
|
2417
|
+
})
|
|
2349
2418
|
|
|
2350
2419
|
// .encode(str) → Uint8Array of string's UTF-8 bytes
|
|
2351
2420
|
// Copies bytes from string (SSO or heap) into a new Uint8Array
|
|
@@ -2372,7 +2441,9 @@ export default (ctx) => {
|
|
|
2372
2441
|
// .decode(uint8arr) → string from byte data
|
|
2373
2442
|
wat('__bytes_decode', `(func $__bytes_decode (param $arr i64) (result f64)
|
|
2374
2443
|
(local $off i32) (local $len i32) (local $dst i32)
|
|
2375
|
-
|
|
2444
|
+
;; __typed_data, not __ptr_offset — a VIEW's offset is its descriptor, and
|
|
2445
|
+
;; decoding it read the descriptor bytes as text (silent garbage)
|
|
2446
|
+
(local.set $off (call $__typed_data (local.get $arr)))
|
|
2376
2447
|
(local.set $len (call $__len (local.get $arr)))
|
|
2377
2448
|
(local.set $dst (call $__alloc (i32.add (i32.const 4) (local.get $len))))
|
|
2378
2449
|
(i32.store (local.get $dst) (local.get $len))
|
|
@@ -2384,7 +2455,341 @@ export default (ctx) => {
|
|
|
2384
2455
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $dst)))`)
|
|
2385
2456
|
|
|
2386
2457
|
bind('.decode', (obj, arr) => {
|
|
2458
|
+
decoderRecvCheck(obj)
|
|
2387
2459
|
inc('__bytes_decode')
|
|
2388
2460
|
return typed(['call', '$__bytes_decode', asI64(emit(arr))], 'f64')
|
|
2389
2461
|
})
|
|
2462
|
+
|
|
2463
|
+
// .encodeInto(str, u8) → { read, written }. jz strings are UTF-8 bytes, so
|
|
2464
|
+
// read counts bytes (== written), not UTF-16 units — same divergence class as
|
|
2465
|
+
// `"中".length`. Never splits a multi-byte sequence: a truncated copy backs
|
|
2466
|
+
// off past continuation bytes to the previous boundary (spec: whole code
|
|
2467
|
+
// points only). Result is a dictionary object (HASH) — a one-shot small
|
|
2468
|
+
// record, not a hot-path value.
|
|
2469
|
+
wat('__str_encode_into', `(func $__str_encode_into (param $s i64) (param $dst i64) (result i32)
|
|
2470
|
+
(local $slen i32) (local $n i32)
|
|
2471
|
+
;; receiver must be a Uint8Array — anything else is a TypeError in JS
|
|
2472
|
+
(if (i32.or
|
|
2473
|
+
(i32.ne (call $__ptr_type (local.get $dst)) (i32.const ${PTR.TYPED}))
|
|
2474
|
+
(i32.ne (i32.and (call $__ptr_aux (local.get $dst)) (i32.const 7)) (i32.const 1)))
|
|
2475
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
2476
|
+
(local.set $slen (call $__str_byteLen (local.get $s)))
|
|
2477
|
+
(local.set $n (call $__len (local.get $dst)))
|
|
2478
|
+
(if (i32.ge_s (local.get $n) (local.get $slen))
|
|
2479
|
+
(then (local.set $n (local.get $slen)))
|
|
2480
|
+
(else ;; truncated: back off to a UTF-8 boundary (exclude the split sequence)
|
|
2481
|
+
(block $b (loop $l
|
|
2482
|
+
(br_if $b (i32.eqz (local.get $n)))
|
|
2483
|
+
(br_if $b (i32.ne
|
|
2484
|
+
(i32.and (call $__char_at (local.get $s) (local.get $n)) (i32.const 0xC0))
|
|
2485
|
+
(i32.const 0x80)))
|
|
2486
|
+
(local.set $n (i32.sub (local.get $n) (i32.const 1)))
|
|
2487
|
+
(br $l)))))
|
|
2488
|
+
(call $__str_copy (local.get $s) (call $__typed_data (local.get $dst)) (local.get $n))
|
|
2489
|
+
(local.get $n))`)
|
|
2490
|
+
|
|
2491
|
+
bind('.encodeInto', (obj, str, dst) => {
|
|
2492
|
+
ctx.runtime.throws = true
|
|
2493
|
+
ctx.features.typedarray = true
|
|
2494
|
+
inc('__str_encode_into', '__hash_new', '__hash_set')
|
|
2495
|
+
const n = tempI32('ein'), h = temp('eih')
|
|
2496
|
+
const hI64 = ['i64.reinterpret_f64', ['local.get', `$${h}`]]
|
|
2497
|
+
const nVal = ['i64.reinterpret_f64', ['f64.convert_i32_s', ['local.get', `$${n}`]]]
|
|
2498
|
+
return typed(['block', ['result', 'f64'],
|
|
2499
|
+
['local.set', `$${n}`, ['call', '$__str_encode_into', asI64(emit(str)), asI64(emit(dst))]],
|
|
2500
|
+
['local.set', `$${h}`, ['call', '$__hash_new']],
|
|
2501
|
+
['local.set', `$${h}`, ['f64.reinterpret_i64',
|
|
2502
|
+
['call', '$__hash_set', hI64, asI64(emit(['str', 'read'])), nVal]]],
|
|
2503
|
+
['local.set', `$${h}`, ['f64.reinterpret_i64',
|
|
2504
|
+
['call', '$__hash_set', hI64, asI64(emit(['str', 'written'])), nVal]]],
|
|
2505
|
+
['local.get', `$${h}`]], 'f64')
|
|
2506
|
+
})
|
|
2507
|
+
|
|
2508
|
+
// === base64 / hex codecs ===
|
|
2509
|
+
// One encode loop and one decode loop serve the whole family: the ES2026
|
|
2510
|
+
// Uint8Array.fromBase64/toBase64/fromHex/toHex/setFrom* methods (emitters in
|
|
2511
|
+
// module/typedarray.js) and the legacy atob/btoa pair. Decode follows WHATWG
|
|
2512
|
+
// forgiving-base64 == TC39 lastChunkHandling:'loose': ASCII whitespace
|
|
2513
|
+
// skipped, padding optional but validated when present, len%4==1 rejected,
|
|
2514
|
+
// extra trailing bits ignored. Alphabet (std/url) and padding ride as i32
|
|
2515
|
+
// flags — resolved compile-time from literal options, so no per-call parsing.
|
|
2516
|
+
|
|
2517
|
+
// put one 6-bit value ($v pre-masked) as a base64 char at $out+$j
|
|
2518
|
+
const b64put = (bits) => `(local.set $v (i32.and ${bits} (i32.const 63)))
|
|
2519
|
+
(i32.store8 (i32.add (local.get $out) (local.get $j))
|
|
2520
|
+
(select (i32.add (local.get $v) (i32.const 65))
|
|
2521
|
+
(select (i32.add (local.get $v) (i32.const 71))
|
|
2522
|
+
(select (i32.add (local.get $v) (i32.const -4))
|
|
2523
|
+
(select (select (i32.const 45) (i32.const 43) (local.get $url))
|
|
2524
|
+
(select (i32.const 95) (i32.const 47) (local.get $url))
|
|
2525
|
+
(i32.eq (local.get $v) (i32.const 62)))
|
|
2526
|
+
(i32.lt_u (local.get $v) (i32.const 62)))
|
|
2527
|
+
(i32.lt_u (local.get $v) (i32.const 52)))
|
|
2528
|
+
(i32.lt_u (local.get $v) (i32.const 26))))
|
|
2529
|
+
(local.set $j (i32.add (local.get $j) (i32.const 1)))`
|
|
2530
|
+
|
|
2531
|
+
wat('__b64_enc', `(func $__b64_enc (param $src i32) (param $len i32) (param $url i32) (param $pad i32) (result f64)
|
|
2532
|
+
(local $g3 i32) (local $rem i32) (local $outLen i32) (local $base i32) (local $out i32)
|
|
2533
|
+
(local $i i32) (local $j i32) (local $w i32) (local $v i32)
|
|
2534
|
+
(local.set $g3 (i32.mul (i32.div_u (local.get $len) (i32.const 3)) (i32.const 3)))
|
|
2535
|
+
(local.set $rem (i32.sub (local.get $len) (local.get $g3)))
|
|
2536
|
+
(local.set $outLen (i32.mul (i32.div_u (local.get $len) (i32.const 3)) (i32.const 4)))
|
|
2537
|
+
(if (local.get $rem)
|
|
2538
|
+
(then (local.set $outLen (i32.add (local.get $outLen)
|
|
2539
|
+
(select (i32.const 4) (i32.add (local.get $rem) (i32.const 1)) (local.get $pad))))))
|
|
2540
|
+
(local.set $base (call $__alloc (i32.add (i32.const 4) (local.get $outLen))))
|
|
2541
|
+
(local.set $out (i32.add (local.get $base) (i32.const 4)))
|
|
2542
|
+
(block $gdone (loop $gloop
|
|
2543
|
+
(br_if $gdone (i32.ge_u (local.get $i) (local.get $g3)))
|
|
2544
|
+
(local.set $w (i32.or (i32.or
|
|
2545
|
+
(i32.shl (i32.load8_u (i32.add (local.get $src) (local.get $i))) (i32.const 16))
|
|
2546
|
+
(i32.shl (i32.load8_u (i32.add (local.get $src) (i32.add (local.get $i) (i32.const 1)))) (i32.const 8)))
|
|
2547
|
+
(i32.load8_u (i32.add (local.get $src) (i32.add (local.get $i) (i32.const 2))))))
|
|
2548
|
+
${b64put('(i32.shr_u (local.get $w) (i32.const 18))')}
|
|
2549
|
+
${b64put('(i32.shr_u (local.get $w) (i32.const 12))')}
|
|
2550
|
+
${b64put('(i32.shr_u (local.get $w) (i32.const 6))')}
|
|
2551
|
+
${b64put('(local.get $w)')}
|
|
2552
|
+
(local.set $i (i32.add (local.get $i) (i32.const 3)))
|
|
2553
|
+
(br $gloop)))
|
|
2554
|
+
(if (i32.eq (local.get $rem) (i32.const 1))
|
|
2555
|
+
(then
|
|
2556
|
+
(local.set $w (i32.shl (i32.load8_u (i32.add (local.get $src) (local.get $i))) (i32.const 16)))
|
|
2557
|
+
${b64put('(i32.shr_u (local.get $w) (i32.const 18))')}
|
|
2558
|
+
${b64put('(i32.shr_u (local.get $w) (i32.const 12))')}
|
|
2559
|
+
(if (local.get $pad) (then
|
|
2560
|
+
(i32.store8 (i32.add (local.get $out) (local.get $j)) (i32.const 61))
|
|
2561
|
+
(i32.store8 (i32.add (local.get $out) (i32.add (local.get $j) (i32.const 1))) (i32.const 61))
|
|
2562
|
+
(local.set $j (i32.add (local.get $j) (i32.const 2)))))))
|
|
2563
|
+
(if (i32.eq (local.get $rem) (i32.const 2))
|
|
2564
|
+
(then
|
|
2565
|
+
(local.set $w (i32.or
|
|
2566
|
+
(i32.shl (i32.load8_u (i32.add (local.get $src) (local.get $i))) (i32.const 16))
|
|
2567
|
+
(i32.shl (i32.load8_u (i32.add (local.get $src) (i32.add (local.get $i) (i32.const 1)))) (i32.const 8))))
|
|
2568
|
+
${b64put('(i32.shr_u (local.get $w) (i32.const 18))')}
|
|
2569
|
+
${b64put('(i32.shr_u (local.get $w) (i32.const 12))')}
|
|
2570
|
+
${b64put('(i32.shr_u (local.get $w) (i32.const 6))')}
|
|
2571
|
+
(if (local.get $pad) (then
|
|
2572
|
+
(i32.store8 (i32.add (local.get $out) (local.get $j)) (i32.const 61))
|
|
2573
|
+
(local.set $j (i32.add (local.get $j) (i32.const 1)))))))
|
|
2574
|
+
(i32.store (local.get $base) (local.get $j))
|
|
2575
|
+
(call $__sso_norm (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $out))))`)
|
|
2576
|
+
|
|
2577
|
+
// Forgiving decode. Returns (read << 32) | written; read = chars consumed
|
|
2578
|
+
// (== full length unless the $cap byte budget stopped a chunk; then the
|
|
2579
|
+
// stopped chunk's start). Throws on: a non-alphabet char, len%4==1,
|
|
2580
|
+
// misplaced/unterminated padding, content after complete padding.
|
|
2581
|
+
wat('__b64_dec_raw', `(func $__b64_dec_raw (param $s i64) (param $dst i32) (param $cap i32) (param $url i32) (result i64)
|
|
2582
|
+
(local $slen i32) (local $i i32) (local $c i32) (local $v i32)
|
|
2583
|
+
(local $acc i32) (local $cnt i32) (local $pads i32) (local $done i32)
|
|
2584
|
+
(local $mark i32) (local $written i32) (local $stopped i32) (local $n i32)
|
|
2585
|
+
(local.set $slen (call $__str_byteLen (local.get $s)))
|
|
2586
|
+
(block $stop (loop $loop
|
|
2587
|
+
(br_if $stop (i32.ge_s (local.get $i) (local.get $slen)))
|
|
2588
|
+
(local.set $c (call $__char_at (local.get $s) (local.get $i)))
|
|
2589
|
+
;; ASCII whitespace (tab LF FF CR space) skipped everywhere
|
|
2590
|
+
(if (i32.or
|
|
2591
|
+
(i32.or (i32.eq (local.get $c) (i32.const 9)) (i32.eq (local.get $c) (i32.const 10)))
|
|
2592
|
+
(i32.or
|
|
2593
|
+
(i32.or (i32.eq (local.get $c) (i32.const 12)) (i32.eq (local.get $c) (i32.const 13)))
|
|
2594
|
+
(i32.eq (local.get $c) (i32.const 32))))
|
|
2595
|
+
(then
|
|
2596
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
2597
|
+
(br $loop)))
|
|
2598
|
+
;; after complete padding only whitespace may follow
|
|
2599
|
+
(if (local.get $done) (then (throw $__jz_err (f64.const 0))))
|
|
2600
|
+
(if (i32.eq (local.get $c) (i32.const 61)) ;; '='
|
|
2601
|
+
(then
|
|
2602
|
+
(if (i32.lt_s (local.get $cnt) (i32.const 2)) (then (throw $__jz_err (f64.const 0))))
|
|
2603
|
+
(local.set $pads (i32.add (local.get $pads) (i32.const 1)))
|
|
2604
|
+
(if (i32.eq (i32.add (local.get $cnt) (local.get $pads)) (i32.const 4))
|
|
2605
|
+
(then ;; flush the padded partial chunk: 2 chars → 1 byte, 3 → 2
|
|
2606
|
+
(local.set $n (i32.sub (local.get $cnt) (i32.const 1)))
|
|
2607
|
+
(if (i32.gt_s (i32.add (local.get $written) (local.get $n)) (local.get $cap))
|
|
2608
|
+
(then (local.set $stopped (i32.const 1)) (br $stop)))
|
|
2609
|
+
(i32.store8 (i32.add (local.get $dst) (local.get $written))
|
|
2610
|
+
(i32.and (i32.shr_u (local.get $acc)
|
|
2611
|
+
(select (i32.const 10) (i32.const 4) (i32.eq (local.get $cnt) (i32.const 3)))) (i32.const 255)))
|
|
2612
|
+
(if (i32.eq (local.get $cnt) (i32.const 3))
|
|
2613
|
+
(then (i32.store8 (i32.add (local.get $dst) (i32.add (local.get $written) (i32.const 1)))
|
|
2614
|
+
(i32.and (i32.shr_u (local.get $acc) (i32.const 2)) (i32.const 255)))))
|
|
2615
|
+
(local.set $written (i32.add (local.get $written) (local.get $n)))
|
|
2616
|
+
(local.set $mark (i32.add (local.get $i) (i32.const 1)))
|
|
2617
|
+
(local.set $cnt (i32.const 0))
|
|
2618
|
+
(local.set $acc (i32.const 0))
|
|
2619
|
+
(local.set $done (i32.const 1)))))
|
|
2620
|
+
(else
|
|
2621
|
+
;; a value char while padding is open ("AB=C") is malformed
|
|
2622
|
+
(if (local.get $pads) (then (throw $__jz_err (f64.const 0))))
|
|
2623
|
+
(local.set $v (i32.const -1))
|
|
2624
|
+
(if (i32.and (i32.ge_u (local.get $c) (i32.const 65)) (i32.le_u (local.get $c) (i32.const 90)))
|
|
2625
|
+
(then (local.set $v (i32.sub (local.get $c) (i32.const 65)))))
|
|
2626
|
+
(if (i32.and (i32.ge_u (local.get $c) (i32.const 97)) (i32.le_u (local.get $c) (i32.const 122)))
|
|
2627
|
+
(then (local.set $v (i32.sub (local.get $c) (i32.const 71)))))
|
|
2628
|
+
(if (i32.and (i32.ge_u (local.get $c) (i32.const 48)) (i32.le_u (local.get $c) (i32.const 57)))
|
|
2629
|
+
(then (local.set $v (i32.add (local.get $c) (i32.const 4)))))
|
|
2630
|
+
(if (local.get $url)
|
|
2631
|
+
(then
|
|
2632
|
+
(if (i32.eq (local.get $c) (i32.const 45)) (then (local.set $v (i32.const 62))))
|
|
2633
|
+
(if (i32.eq (local.get $c) (i32.const 95)) (then (local.set $v (i32.const 63)))))
|
|
2634
|
+
(else
|
|
2635
|
+
(if (i32.eq (local.get $c) (i32.const 43)) (then (local.set $v (i32.const 62))))
|
|
2636
|
+
(if (i32.eq (local.get $c) (i32.const 47)) (then (local.set $v (i32.const 63))))))
|
|
2637
|
+
(if (i32.lt_s (local.get $v) (i32.const 0)) (then (throw $__jz_err (f64.const 0))))
|
|
2638
|
+
(local.set $acc (i32.or (i32.shl (local.get $acc) (i32.const 6)) (local.get $v)))
|
|
2639
|
+
(local.set $cnt (i32.add (local.get $cnt) (i32.const 1)))
|
|
2640
|
+
(if (i32.eq (local.get $cnt) (i32.const 4))
|
|
2641
|
+
(then
|
|
2642
|
+
(if (i32.gt_s (i32.add (local.get $written) (i32.const 3)) (local.get $cap))
|
|
2643
|
+
(then (local.set $stopped (i32.const 1)) (br $stop)))
|
|
2644
|
+
(i32.store8 (i32.add (local.get $dst) (local.get $written))
|
|
2645
|
+
(i32.shr_u (local.get $acc) (i32.const 16)))
|
|
2646
|
+
(i32.store8 (i32.add (local.get $dst) (i32.add (local.get $written) (i32.const 1)))
|
|
2647
|
+
(i32.and (i32.shr_u (local.get $acc) (i32.const 8)) (i32.const 255)))
|
|
2648
|
+
(i32.store8 (i32.add (local.get $dst) (i32.add (local.get $written) (i32.const 2)))
|
|
2649
|
+
(i32.and (local.get $acc) (i32.const 255)))
|
|
2650
|
+
(local.set $written (i32.add (local.get $written) (i32.const 3)))
|
|
2651
|
+
(local.set $mark (i32.add (local.get $i) (i32.const 1)))
|
|
2652
|
+
(local.set $cnt (i32.const 0))
|
|
2653
|
+
(local.set $acc (i32.const 0))))))
|
|
2654
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
2655
|
+
(br $loop)))
|
|
2656
|
+
;; EOF: unterminated padding ("AA=") is malformed; a padless partial chunk
|
|
2657
|
+
;; is the loose case — 1 leftover char is len%4==1 (malformed), 2 → 1 byte,
|
|
2658
|
+
;; 3 → 2 bytes, extra bits ignored.
|
|
2659
|
+
(if (i32.eqz (local.get $stopped))
|
|
2660
|
+
(then
|
|
2661
|
+
(if (i32.and (i32.ne (local.get $pads) (i32.const 0)) (i32.eqz (local.get $done)))
|
|
2662
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
2663
|
+
(if (i32.eq (local.get $cnt) (i32.const 1)) (then (throw $__jz_err (f64.const 0))))
|
|
2664
|
+
(if (i32.gt_s (local.get $cnt) (i32.const 1))
|
|
2665
|
+
(then
|
|
2666
|
+
(local.set $n (i32.sub (local.get $cnt) (i32.const 1)))
|
|
2667
|
+
(if (i32.gt_s (i32.add (local.get $written) (local.get $n)) (local.get $cap))
|
|
2668
|
+
(then (local.set $stopped (i32.const 1)))
|
|
2669
|
+
(else
|
|
2670
|
+
(i32.store8 (i32.add (local.get $dst) (local.get $written))
|
|
2671
|
+
(i32.and (i32.shr_u (local.get $acc)
|
|
2672
|
+
(select (i32.const 10) (i32.const 4) (i32.eq (local.get $cnt) (i32.const 3)))) (i32.const 255)))
|
|
2673
|
+
(if (i32.eq (local.get $cnt) (i32.const 3))
|
|
2674
|
+
(then (i32.store8 (i32.add (local.get $dst) (i32.add (local.get $written) (i32.const 1)))
|
|
2675
|
+
(i32.and (i32.shr_u (local.get $acc) (i32.const 2)) (i32.const 255)))))
|
|
2676
|
+
(local.set $written (i32.add (local.get $written) (local.get $n)))
|
|
2677
|
+
(local.set $mark (local.get $slen))))))))
|
|
2678
|
+
(i64.or
|
|
2679
|
+
(i64.shl (i64.extend_i32_u (select (local.get $mark) (local.get $slen) (local.get $stopped))) (i64.const 32))
|
|
2680
|
+
(i64.extend_i32_u (local.get $written))))`)
|
|
2681
|
+
|
|
2682
|
+
// Uint8Array receiver guard → data offset. u8-only per spec (TypeError otherwise).
|
|
2683
|
+
wat('__u8_data', `(func $__u8_data (param $ptr i64) (result i32)
|
|
2684
|
+
(if (i32.or
|
|
2685
|
+
(i32.ne (call $__ptr_type (local.get $ptr)) (i32.const ${PTR.TYPED}))
|
|
2686
|
+
(i32.ne (i32.and (call $__ptr_aux (local.get $ptr)) (i32.const 7)) (i32.const 1)))
|
|
2687
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
2688
|
+
(call $__typed_data (local.get $ptr)))`)
|
|
2689
|
+
|
|
2690
|
+
wat('__btoa', `(func $__btoa (param $v i64) (result f64)
|
|
2691
|
+
(local $s i64) (local $len i32) (local $buf i32)
|
|
2692
|
+
(local.set $s (call $__to_str (local.get $v)))
|
|
2693
|
+
(local.set $len (call $__str_byteLen (local.get $s)))
|
|
2694
|
+
(local.set $buf (call $__alloc (local.get $len)))
|
|
2695
|
+
(call $__str_copy (local.get $s) (local.get $buf) (local.get $len))
|
|
2696
|
+
(call $__b64_enc (local.get $buf) (local.get $len) (i32.const 0) (i32.const 1)))`)
|
|
2697
|
+
|
|
2698
|
+
wat('__atob', `(func $__atob (param $v i64) (result f64)
|
|
2699
|
+
(local $s i64) (local $max i32) (local $base i32) (local $rw i64)
|
|
2700
|
+
(local.set $s (call $__to_str (local.get $v)))
|
|
2701
|
+
(local.set $max (i32.add (i32.mul (i32.add (i32.shr_u (call $__str_byteLen (local.get $s)) (i32.const 2)) (i32.const 1)) (i32.const 3)) (i32.const 3)))
|
|
2702
|
+
(local.set $base (call $__alloc (i32.add (i32.const 4) (local.get $max))))
|
|
2703
|
+
(local.set $rw (call $__b64_dec_raw (local.get $s) (i32.add (local.get $base) (i32.const 4)) (i32.const 2147483647) (i32.const 0)))
|
|
2704
|
+
(i32.store (local.get $base) (i32.wrap_i64 (local.get $rw)))
|
|
2705
|
+
(call $__sso_norm (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (i32.add (local.get $base) (i32.const 4)))))`)
|
|
2706
|
+
|
|
2707
|
+
wat('__b64_from', `(func $__b64_from (param $v i64) (param $url i32) (result f64)
|
|
2708
|
+
(local $s i64) (local $max i32) (local $base i32) (local $rw i64) (local $n i32)
|
|
2709
|
+
(local.set $s (call $__to_str (local.get $v)))
|
|
2710
|
+
(local.set $max (i32.add (i32.mul (i32.add (i32.shr_u (call $__str_byteLen (local.get $s)) (i32.const 2)) (i32.const 1)) (i32.const 3)) (i32.const 3)))
|
|
2711
|
+
(local.set $base (call $__alloc (i32.add (i32.const 8) (local.get $max))))
|
|
2712
|
+
(local.set $rw (call $__b64_dec_raw (local.get $s) (i32.add (local.get $base) (i32.const 8)) (i32.const 2147483647) (local.get $url)))
|
|
2713
|
+
(local.set $n (i32.wrap_i64 (local.get $rw)))
|
|
2714
|
+
(i32.store (local.get $base) (local.get $n))
|
|
2715
|
+
(i32.store (i32.add (local.get $base) (i32.const 4)) (local.get $n))
|
|
2716
|
+
(call $__mkptr (i32.const ${PTR.TYPED}) (i32.const 1) (i32.add (local.get $base) (i32.const 8))))`)
|
|
2717
|
+
|
|
2718
|
+
wat('__b64_set', `(func $__b64_set (param $dst i64) (param $s i64) (param $url i32) (result i64)
|
|
2719
|
+
(call $__b64_dec_raw (local.get $s)
|
|
2720
|
+
(call $__u8_data (local.get $dst))
|
|
2721
|
+
(call $__len (local.get $dst))
|
|
2722
|
+
(local.get $url)))`)
|
|
2723
|
+
|
|
2724
|
+
wat('__hex_enc', `(func $__hex_enc (param $src i32) (param $len i32) (result f64)
|
|
2725
|
+
(local $base i32) (local $out i32) (local $i i32) (local $b i32) (local $n i32)
|
|
2726
|
+
(local.set $base (call $__alloc (i32.add (i32.const 4) (i32.shl (local.get $len) (i32.const 1)))))
|
|
2727
|
+
(local.set $out (i32.add (local.get $base) (i32.const 4)))
|
|
2728
|
+
(block $d (loop $l
|
|
2729
|
+
(br_if $d (i32.ge_u (local.get $i) (local.get $len)))
|
|
2730
|
+
(local.set $b (i32.load8_u (i32.add (local.get $src) (local.get $i))))
|
|
2731
|
+
(local.set $n (i32.shr_u (local.get $b) (i32.const 4)))
|
|
2732
|
+
(i32.store8 (i32.add (local.get $out) (i32.shl (local.get $i) (i32.const 1)))
|
|
2733
|
+
(select (i32.add (local.get $n) (i32.const 87)) (i32.add (local.get $n) (i32.const 48)) (i32.gt_u (local.get $n) (i32.const 9))))
|
|
2734
|
+
(local.set $n (i32.and (local.get $b) (i32.const 15)))
|
|
2735
|
+
(i32.store8 (i32.add (i32.add (local.get $out) (i32.shl (local.get $i) (i32.const 1))) (i32.const 1))
|
|
2736
|
+
(select (i32.add (local.get $n) (i32.const 87)) (i32.add (local.get $n) (i32.const 48)) (i32.gt_u (local.get $n) (i32.const 9))))
|
|
2737
|
+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
2738
|
+
(br $l)))
|
|
2739
|
+
(i32.store (local.get $base) (i32.shl (local.get $len) (i32.const 1)))
|
|
2740
|
+
(call $__sso_norm (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $out))))`)
|
|
2741
|
+
|
|
2742
|
+
// Hex decode: strict per spec — no whitespace, both nibble cases, odd length
|
|
2743
|
+
// or a non-hex char throw. Stops at $cap whole bytes (setFromHex).
|
|
2744
|
+
wat('__hex_dec_raw', `(func $__hex_dec_raw (param $s i64) (param $dst i32) (param $cap i32) (result i64)
|
|
2745
|
+
(local $slen i32) (local $i i32) (local $hi i32) (local $lo i32) (local $written i32)
|
|
2746
|
+
(local.set $slen (call $__str_byteLen (local.get $s)))
|
|
2747
|
+
(if (i32.and (local.get $slen) (i32.const 1)) (then (throw $__jz_err (f64.const 0))))
|
|
2748
|
+
(block $stop (loop $l
|
|
2749
|
+
(br_if $stop (i32.ge_s (local.get $i) (local.get $slen)))
|
|
2750
|
+
(br_if $stop (i32.ge_s (local.get $written) (local.get $cap)))
|
|
2751
|
+
(local.set $hi (call $__uri_hex (call $__char_at (local.get $s) (local.get $i))))
|
|
2752
|
+
(local.set $lo (call $__uri_hex (call $__char_at (local.get $s) (i32.add (local.get $i) (i32.const 1)))))
|
|
2753
|
+
(if (i32.or (i32.lt_s (local.get $hi) (i32.const 0)) (i32.lt_s (local.get $lo) (i32.const 0)))
|
|
2754
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
2755
|
+
(i32.store8 (i32.add (local.get $dst) (local.get $written))
|
|
2756
|
+
(i32.or (i32.shl (local.get $hi) (i32.const 4)) (local.get $lo)))
|
|
2757
|
+
(local.set $written (i32.add (local.get $written) (i32.const 1)))
|
|
2758
|
+
(local.set $i (i32.add (local.get $i) (i32.const 2)))
|
|
2759
|
+
(br $l)))
|
|
2760
|
+
(i64.or (i64.shl (i64.extend_i32_u (local.get $i)) (i64.const 32))
|
|
2761
|
+
(i64.extend_i32_u (local.get $written))))`)
|
|
2762
|
+
|
|
2763
|
+
wat('__hex_from', `(func $__hex_from (param $v i64) (result f64)
|
|
2764
|
+
(local $s i64) (local $base i32) (local $rw i64) (local $n i32)
|
|
2765
|
+
(local.set $s (call $__to_str (local.get $v)))
|
|
2766
|
+
(local.set $base (call $__alloc (i32.add (i32.const 8) (i32.shr_u (call $__str_byteLen (local.get $s)) (i32.const 1)))))
|
|
2767
|
+
(local.set $rw (call $__hex_dec_raw (local.get $s) (i32.add (local.get $base) (i32.const 8)) (i32.const 2147483647)))
|
|
2768
|
+
(local.set $n (i32.wrap_i64 (local.get $rw)))
|
|
2769
|
+
(i32.store (local.get $base) (local.get $n))
|
|
2770
|
+
(i32.store (i32.add (local.get $base) (i32.const 4)) (local.get $n))
|
|
2771
|
+
(call $__mkptr (i32.const ${PTR.TYPED}) (i32.const 1) (i32.add (local.get $base) (i32.const 8))))`)
|
|
2772
|
+
|
|
2773
|
+
wat('__hex_set', `(func $__hex_set (param $dst i64) (param $s i64) (result i64)
|
|
2774
|
+
(call $__hex_dec_raw (local.get $s)
|
|
2775
|
+
(call $__u8_data (local.get $dst))
|
|
2776
|
+
(call $__len (local.get $dst))))`)
|
|
2777
|
+
|
|
2778
|
+
// btoa(s): base64 of the string's bytes. jz strings ARE bytes, so every char
|
|
2779
|
+
// qualifies — JS's InvalidCharacterError for >0xFF code units cannot arise;
|
|
2780
|
+
// non-ASCII text encodes its UTF-8 bytes (documented byte-string divergence).
|
|
2781
|
+
bind('btoa', (value) => {
|
|
2782
|
+
inc('__btoa')
|
|
2783
|
+
return typed(['call', '$__btoa',
|
|
2784
|
+
value === undefined ? ['i64.const', UNDEF_NAN] : asI64(emit(value))], 'f64')
|
|
2785
|
+
})
|
|
2786
|
+
|
|
2787
|
+
// atob(b64): decoded bytes as a byte-string — charCodeAt(i) reads the byte,
|
|
2788
|
+
// exactly what JS binary-string consumers observe.
|
|
2789
|
+
bind('atob', (value) => {
|
|
2790
|
+
ctx.runtime.throws = true
|
|
2791
|
+
inc('__atob')
|
|
2792
|
+
return typed(['call', '$__atob',
|
|
2793
|
+
value === undefined ? ['i64.const', UNDEF_NAN] : asI64(emit(value))], 'f64')
|
|
2794
|
+
})
|
|
2390
2795
|
}
|
package/module/timer.js
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
import { typed, asF64, asI64, UNDEF_NAN, MAX_CLOSURE_ARITY, temp, tempI64 } from '../src/ir.js'
|
|
30
30
|
import { emit, deps, hostImport } from '../src/bridge.js'
|
|
31
|
-
import { inc, PTR, LAYOUT, declGlobal } from '../src/ctx.js'
|
|
31
|
+
import { inc, err, PTR, LAYOUT, declGlobal } from '../src/ctx.js'
|
|
32
32
|
|
|
33
33
|
const MAX_TIMERS = 64
|
|
34
34
|
const ENTRY_SIZE = 40
|
|
@@ -46,6 +46,19 @@ const invokeClosureFn = (exported) => `(func $__invoke_closure${exported ? ' (ex
|
|
|
46
46
|
(i64.shr_u (local.get $clos) (i64.const ${LAYOUT.AUX_SHIFT}))
|
|
47
47
|
(i64.const ${LAYOUT.AUX_MASK})))))`
|
|
48
48
|
|
|
49
|
+
// One-arg variant: first $ftN slot carries a real f64 (the rAF timestamp),
|
|
50
|
+
// the rest pad UNDEF_NAN. Exported so the host frame loop can pass the
|
|
51
|
+
// DOMHighResTimeStamp through to the callback.
|
|
52
|
+
const invokeClosure1Fn = (exported) => `(func $__invoke_closure1${exported ? ' (export "__invoke_closure1")' : ''} (param $clos i64) (param $a0 f64) (result f64)
|
|
53
|
+
(call_indirect (type \$ftN)
|
|
54
|
+
(f64.reinterpret_i64 (local.get $clos))
|
|
55
|
+
(i32.const 0)
|
|
56
|
+
(local.get $a0)
|
|
57
|
+
${Array.from({length: MAX_CLOSURE_ARITY - 1}, () => `(f64.const nan:${UNDEF_NAN})`).join('\n ')}
|
|
58
|
+
(i32.wrap_i64 (i64.and
|
|
59
|
+
(i64.shr_u (local.get $clos) (i64.const ${LAYOUT.AUX_SHIFT}))
|
|
60
|
+
(i64.const ${LAYOUT.AUX_MASK})))))`
|
|
61
|
+
|
|
49
62
|
const setupWasi = (ctx) => {
|
|
50
63
|
// Always include init + tick + loop when timer module loads (structural, not per-emitter)
|
|
51
64
|
inc('__timer_init', '__timer_tick', '__timer_loop')
|
|
@@ -263,6 +276,14 @@ const setupWasi = (ctx) => {
|
|
|
263
276
|
inc('__timer_cancel')
|
|
264
277
|
return typed(['call', '$__timer_cancel', asF64(emit(idExpr))], 'f64')
|
|
265
278
|
}
|
|
279
|
+
|
|
280
|
+
// rAF is a display-loop service — it has no WASI meaning (and warning +
|
|
281
|
+
// declaring env.* here would break the one-import-namespace shape). Reject
|
|
282
|
+
// with the architecture that DOES work standalone: the embedder owns the
|
|
283
|
+
// frame loop and calls an exported step per frame.
|
|
284
|
+
const noWasiRaf = () => err(`requestAnimationFrame needs a JS host (host:'js') — under wasi export a per-frame function and drive it from the embedder's loop`)
|
|
285
|
+
ctx.core.emit['requestAnimationFrame'] = noWasiRaf
|
|
286
|
+
ctx.core.emit['cancelAnimationFrame'] = noWasiRaf
|
|
266
287
|
}
|
|
267
288
|
|
|
268
289
|
const setupJsHost = (ctx) => {
|
|
@@ -298,6 +319,26 @@ const setupJsHost = (ctx) => {
|
|
|
298
319
|
}
|
|
299
320
|
ctx.core.emit['clearTimeout'] = emitClear
|
|
300
321
|
ctx.core.emit['clearInterval'] = emitClear
|
|
322
|
+
|
|
323
|
+
// requestAnimationFrame(cb) / cancelAnimationFrame(id) — the same env-service
|
|
324
|
+
// shape as setTimeout: the host schedules (real rAF in browsers, a 16 ms
|
|
325
|
+
// timer fallback elsewhere — interop.js) and fires the callback through the
|
|
326
|
+
// exported __invoke_closure1 trampoline with the frame timestamp.
|
|
327
|
+
const needRaf = () => hostImport('env', 'requestAnimationFrame',
|
|
328
|
+
['func', '$__raf', ['param', 'i64'], ['result', 'f64']])
|
|
329
|
+
const needCancelRaf = () => hostImport('env', 'cancelAnimationFrame',
|
|
330
|
+
['func', '$__craf', ['param', 'f64'], ['result', 'f64']])
|
|
331
|
+
ctx.core.stdlib['__invoke_closure1'] = invokeClosure1Fn(true)
|
|
332
|
+
|
|
333
|
+
ctx.core.emit['requestAnimationFrame'] = (cbExpr) => {
|
|
334
|
+
needRaf()
|
|
335
|
+
inc('__invoke_closure1')
|
|
336
|
+
return typed(['call', '$__raf', ['i64.reinterpret_f64', asF64(emit(cbExpr))]], 'f64')
|
|
337
|
+
}
|
|
338
|
+
ctx.core.emit['cancelAnimationFrame'] = (idExpr) => {
|
|
339
|
+
needCancelRaf()
|
|
340
|
+
return typed(['call', '$__craf', asF64(emit(idExpr))], 'f64')
|
|
341
|
+
}
|
|
301
342
|
}
|
|
302
343
|
|
|
303
344
|
export default (ctx) => {
|