jz 0.1.1 → 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 +257 -90
- package/cli.js +34 -8
- package/index.js +102 -13
- package/module/array.js +396 -133
- package/module/collection.js +455 -212
- package/module/console.js +169 -88
- package/module/core.js +246 -144
- package/module/date.js +691 -0
- package/module/function.js +81 -21
- package/module/index.js +2 -1
- package/module/json.js +483 -138
- package/module/math.js +79 -39
- package/module/number.js +188 -78
- package/module/object.js +227 -47
- package/module/regex.js +33 -30
- package/module/schema.js +14 -17
- package/module/string.js +434 -235
- package/module/symbol.js +4 -3
- package/module/timer.js +89 -31
- package/module/typedarray.js +174 -44
- package/package.json +3 -10
- package/src/analyze.js +626 -132
- package/src/autoload.js +14 -6
- package/src/compile.js +399 -70
- package/src/ctx.js +41 -12
- package/src/emit.js +634 -145
- package/src/host.js +244 -51
- package/src/ir.js +81 -31
- package/src/jzify.js +181 -24
- package/src/narrow.js +32 -4
- package/src/optimize.js +628 -42
- package/src/plan.js +1132 -4
- package/src/prepare.js +321 -75
- package/src/vectorize.js +1016 -0
- package/wasi.js +13 -0
- package/src/key.js +0 -73
- package/src/source.js +0 -76
package/module/object.js
CHANGED
|
@@ -7,36 +7,51 @@
|
|
|
7
7
|
* @module object
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { typed, asF64, temp, tempI32, allocPtr, needsDynShadow, mkPtrIR, extractF64Bits, appendStaticSlots, slotAddr } from '../src/ir.js'
|
|
10
|
+
import { typed, asF64, asI64, temp, tempI32, allocPtr, needsDynShadow, mkPtrIR, extractF64Bits, appendStaticSlots, slotAddr, elemStore } from '../src/ir.js'
|
|
11
11
|
import { emit } from '../src/emit.js'
|
|
12
|
-
import { valTypeOf, lookupValType, VAL, repOf, updateRep } from '../src/analyze.js'
|
|
13
|
-
import { ctx, err, inc, PTR } from '../src/ctx.js'
|
|
12
|
+
import { valTypeOf, lookupValType, VAL, repOf, updateRep, shapeOf } from '../src/analyze.js'
|
|
13
|
+
import { ctx, err, inc, PTR, LAYOUT } from '../src/ctx.js'
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
export default (ctx) => {
|
|
17
17
|
inc('__mkptr', '__alloc', '__alloc_hdr', '__ptr_offset', '__len', '__ptr_type')
|
|
18
18
|
|
|
19
|
-
// Object literal: {x: 1, y: 2} → allocate, fill, return pointer with schemaId
|
|
19
|
+
// Object literal: {x: 1, y: 2} → allocate, fill, return pointer with schemaId.
|
|
20
|
+
// OBJECT alloc uses __alloc_hdr (16-byte header at off-16) to enable per-object
|
|
21
|
+
// propsPtr — dyn property writes (e.g. `ctx.metadata = {}` in watr) hit the
|
|
22
|
+
// per-object hash directly, skipping the global __dyn_props probe. The
|
|
23
|
+
// header gate `off >= __heap_start` keeps static-segment objects on the
|
|
24
|
+
// global-hash path (their off-16 belongs to neighboring static slots).
|
|
20
25
|
ctx.core.emit['{}'] = (...rawProps) => {
|
|
21
|
-
if (rawProps.length === 0)
|
|
22
|
-
|
|
26
|
+
if (rawProps.length === 0) {
|
|
27
|
+
// Honor the literal target's autobox/merged schema so `let ctx = {}` followed
|
|
28
|
+
// by `ctx.meta = ...` allocates with the right cap. Otherwise the default
|
|
29
|
+
// cap=1 alloc overwrites the autobox preamble's wrapper, and subsequent
|
|
30
|
+
// schema-slot writes to offsets >= 8 land out-of-bounds.
|
|
31
|
+
const target = takeLiteralTarget()
|
|
32
|
+
const merged = target ? ctx.schema.resolve(target) : null
|
|
33
|
+
const schemaId = merged ? ctx.schema.idOf(target) : 0
|
|
34
|
+
const cap = merged ? Math.max(1, merged.length) : 1
|
|
35
|
+
return mkPtrIR(PTR.OBJECT, schemaId, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', cap], ['i32.const', 8]])
|
|
36
|
+
}
|
|
23
37
|
|
|
24
38
|
// Flatten comma-grouped props: [',', p1, p2] → [p1, p2]
|
|
25
39
|
const props = rawProps.length === 1 && Array.isArray(rawProps[0]) && rawProps[0][0] === ','
|
|
26
40
|
? rawProps[0].slice(1) : rawProps
|
|
27
41
|
|
|
42
|
+
const target = takeLiteralTarget()
|
|
43
|
+
|
|
28
44
|
// Object spread: {...a, x: 1, ...b} — merge schemas, copy props from sources
|
|
29
45
|
const hasSpreads = props.some(p => Array.isArray(p) && p[0] === '...')
|
|
30
|
-
if (hasSpreads) return emitObjectSpread(props)
|
|
46
|
+
if (hasSpreads) return emitObjectSpread(props, target)
|
|
31
47
|
|
|
32
48
|
const names = [], values = []
|
|
33
49
|
for (const p of props) {
|
|
34
50
|
if (Array.isArray(p) && p[0] === ':') { names.push(p[1]); values.push(p[2]) }
|
|
35
51
|
}
|
|
36
52
|
|
|
37
|
-
// Use variable's merged schema if available (from Object.assign inference), else register literal schema
|
|
53
|
+
// Use variable's merged schema if available (from Object.assign inference), else register literal schema.
|
|
38
54
|
let schemaId = ctx.schema.register(names)
|
|
39
|
-
const target = ctx.schema.targetStack.at(-1)
|
|
40
55
|
if (target) {
|
|
41
56
|
const merged = ctx.schema.resolve(target)
|
|
42
57
|
if (merged) schemaId = ctx.schema.idOf(target)
|
|
@@ -59,15 +74,15 @@ export default (ctx) => {
|
|
|
59
74
|
inc('__dyn_set')
|
|
60
75
|
const body = [['local.set', `$${ptr}`, staticPtr]]
|
|
61
76
|
for (let i = 0; i < schema.length; i++)
|
|
62
|
-
body.push(['drop', ['call', '$__dyn_set', ['local.get', `$${ptr}`],
|
|
63
|
-
emit(['str', String(schema[i])]),
|
|
77
|
+
body.push(['drop', ['call', '$__dyn_set', ['i64.reinterpret_f64', ['local.get', `$${ptr}`]],
|
|
78
|
+
asI64(emit(['str', String(schema[i])])), asI64(emitted[i])]])
|
|
64
79
|
body.push(['local.get', `$${ptr}`])
|
|
65
80
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
66
81
|
}
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
const body = [
|
|
70
|
-
['local.set', `$${t}`, ['call', '$
|
|
85
|
+
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', Math.max(1, schema.length)], ['i32.const', 8]]],
|
|
71
86
|
]
|
|
72
87
|
for (let i = 0; i < values.length; i++)
|
|
73
88
|
body.push(['f64.store', slotAddr(t, i), asF64(emit(values[i]))])
|
|
@@ -75,8 +90,8 @@ export default (ctx) => {
|
|
|
75
90
|
if (shadow) {
|
|
76
91
|
inc('__dyn_set')
|
|
77
92
|
for (let i = 0; i < schema.length; i++)
|
|
78
|
-
body.push(['drop', ['call', '$__dyn_set', ['local.get', `$${ptr}`], emit(['str', String(schema[i])]),
|
|
79
|
-
['
|
|
93
|
+
body.push(['drop', ['call', '$__dyn_set', ['i64.reinterpret_f64', ['local.get', `$${ptr}`]], asI64(emit(['str', String(schema[i])])),
|
|
94
|
+
['i64.load', slotAddr(t, i)]]])
|
|
80
95
|
}
|
|
81
96
|
body.push(['local.get', `$${ptr}`])
|
|
82
97
|
|
|
@@ -86,10 +101,37 @@ export default (ctx) => {
|
|
|
86
101
|
// === Object static methods ===
|
|
87
102
|
|
|
88
103
|
ctx.core.emit['Object.keys'] = (obj) => {
|
|
104
|
+
if (isHashTyped(obj)) return emitHashKeys(obj)
|
|
89
105
|
const schema = resolveSchema(obj)
|
|
90
|
-
if (
|
|
91
|
-
|
|
106
|
+
if (schema) return emitStringArray(schema)
|
|
107
|
+
// Receiver type unknown at compile time. Dispatch on ptr-type at
|
|
108
|
+
// runtime: HASH walks the probe table, anything else returns [].
|
|
109
|
+
return emitRuntimeKeys(obj)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Object.prototype.hasOwnProperty(key) — own-property presence check.
|
|
113
|
+
// Compile-time fold for literal keys against object literals or variables
|
|
114
|
+
// with known schemas; runtime path delegates to the `in` operator (same
|
|
115
|
+
// ptr-type dispatch + __hash_has for HASH, dyn_props probe for OBJECT).
|
|
116
|
+
ctx.core.emit['.hasOwnProperty'] = (obj, key) => {
|
|
117
|
+
const litKey = Array.isArray(key) && key[0] === 'str' ? String(key[1]) : null
|
|
118
|
+
if (litKey != null) {
|
|
119
|
+
if (Array.isArray(obj) && obj[0] === '{}') {
|
|
120
|
+
const has = obj.slice(1).some(p => Array.isArray(p) && p[0] === ':' && String(p[1]) === litKey)
|
|
121
|
+
return typed(['block', ['result', 'f64'],
|
|
122
|
+
['drop', asF64(emit(obj))],
|
|
123
|
+
['f64.const', has ? 1 : 0]], 'f64')
|
|
124
|
+
}
|
|
125
|
+
if (typeof obj === 'string' && ctx.schema.find?.(obj, litKey) >= 0)
|
|
126
|
+
return typed(['f64.const', 1], 'f64')
|
|
127
|
+
}
|
|
128
|
+
return typed(['f64.convert_i32_s', emit(['in', key, obj])], 'f64')
|
|
92
129
|
}
|
|
130
|
+
ctx.core.emit[`.${VAL.HASH}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
131
|
+
ctx.core.emit[`.${VAL.OBJECT}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
132
|
+
ctx.core.emit[`.${VAL.ARRAY}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
133
|
+
ctx.core.emit[`.${VAL.STRING}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
134
|
+
ctx.core.emit[`.${VAL.CLOSURE}:hasOwnProperty`] = ctx.core.emit['.hasOwnProperty']
|
|
93
135
|
|
|
94
136
|
ctx.core.emit['Object.values'] = (obj) => {
|
|
95
137
|
const schema = resolveSchema(obj)
|
|
@@ -99,7 +141,7 @@ export default (ctx) => {
|
|
|
99
141
|
const t = temp('ov'), base = tempI32('vb')
|
|
100
142
|
const out = allocPtr({ type: PTR.ARRAY, len: n, tag: 'oa' })
|
|
101
143
|
const body = [['local.set', `$${t}`, va], out.init,
|
|
102
|
-
['local.set', `$${base}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]]]
|
|
144
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
103
145
|
for (let i = 0; i < n; i++)
|
|
104
146
|
body.push(['f64.store', slotAddr(out.local, i), ['f64.load', slotAddr(base, i)]])
|
|
105
147
|
body.push(out.ptr)
|
|
@@ -114,7 +156,7 @@ export default (ctx) => {
|
|
|
114
156
|
const t = temp('oe'), pair = tempI32('op'), base = tempI32('eb')
|
|
115
157
|
const out = allocPtr({ type: PTR.ARRAY, len: n, tag: 'oa' })
|
|
116
158
|
const body = [['local.set', `$${t}`, va], out.init,
|
|
117
|
-
['local.set', `$${base}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]]]
|
|
159
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
118
160
|
for (let i = 0; i < n; i++) {
|
|
119
161
|
body.push(
|
|
120
162
|
['local.set', `$${pair}`, ['call', '$__alloc_hdr', ['i32.const', 2], ['i32.const', 2], ['i32.const', 8]]],
|
|
@@ -142,14 +184,14 @@ export default (ctx) => {
|
|
|
142
184
|
updateRep(target, { schemaId })
|
|
143
185
|
const t = tempI32('bx'), s = temp('bs')
|
|
144
186
|
const body = [
|
|
145
|
-
['local.set', `$${t}`, ['call', '$
|
|
187
|
+
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', Math.max(1, boxedSchema.length)], ['i32.const', 8]]],
|
|
146
188
|
['f64.store', ['local.get', `$${t}`], asF64(emit(target))],
|
|
147
189
|
]
|
|
148
190
|
const sBase = tempI32('sb')
|
|
149
191
|
for (const source of sources) {
|
|
150
192
|
const sSchema = resolveSchema(source)
|
|
151
193
|
body.push(['local.set', `$${s}`, asF64(emit(source))])
|
|
152
|
-
body.push(['local.set', `$${sBase}`, ['call', '$__ptr_offset', ['local.get', `$${s}`]]])
|
|
194
|
+
body.push(['local.set', `$${sBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]])
|
|
153
195
|
for (let si = 0; si < sSchema.length; si++) {
|
|
154
196
|
const ti = boxedSchema.indexOf(sSchema[si])
|
|
155
197
|
if (ti < 0) continue
|
|
@@ -167,12 +209,12 @@ export default (ctx) => {
|
|
|
167
209
|
const t = temp('at'), s = temp('as')
|
|
168
210
|
const tBase = tempI32('tb'), sBase2 = tempI32('sb')
|
|
169
211
|
const body = [['local.set', `$${t}`, asF64(emit(target))],
|
|
170
|
-
['local.set', `$${tBase}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]]]
|
|
212
|
+
['local.set', `$${tBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
171
213
|
for (const source of sources) {
|
|
172
214
|
const sSchema = resolveSchema(source)
|
|
173
215
|
if (!sSchema) err('Object.assign: source needs known schema')
|
|
174
216
|
body.push(['local.set', `$${s}`, asF64(emit(source))])
|
|
175
|
-
body.push(['local.set', `$${sBase2}`, ['call', '$__ptr_offset', ['local.get', `$${s}`]]])
|
|
217
|
+
body.push(['local.set', `$${sBase2}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]])
|
|
176
218
|
for (let si = 0; si < sSchema.length; si++) {
|
|
177
219
|
const ti = tSchema.indexOf(sSchema[si])
|
|
178
220
|
if (ti < 0) continue
|
|
@@ -193,18 +235,18 @@ export default (ctx) => {
|
|
|
193
235
|
const id = ctx.func.uniq++
|
|
194
236
|
return typed(['block', ['result', 'f64'],
|
|
195
237
|
['local.set', `$${t}`, ['call', '$__hash_new']],
|
|
196
|
-
['local.set', `$${ptr}`, ['call', '$__ptr_offset', va]],
|
|
197
|
-
['local.set', `$${len}`, ['call', '$__len', va]],
|
|
238
|
+
['local.set', `$${ptr}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', va]]],
|
|
239
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', va]]],
|
|
198
240
|
['local.set', `$${i}`, ['i32.const', 0]],
|
|
199
241
|
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
200
242
|
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
201
243
|
// Load pair (array of 2): pair = ptr_offset(arr[i])
|
|
202
|
-
['local.set', `$${pair}`, ['call', '$__ptr_offset',
|
|
203
|
-
['f64.load', ['i32.add', ['local.get', `$${ptr}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]]],
|
|
244
|
+
['local.set', `$${pair}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64',
|
|
245
|
+
['f64.load', ['i32.add', ['local.get', `$${ptr}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]]]],
|
|
204
246
|
// hash_set(result, pair[0], pair[1])
|
|
205
|
-
['local.set', `$${t}`, ['call', '$__hash_set', ['local.get', `$${t}`],
|
|
206
|
-
['
|
|
207
|
-
['
|
|
247
|
+
['local.set', `$${t}`, ['f64.reinterpret_i64', ['call', '$__hash_set', ['i64.reinterpret_f64', ['local.get', `$${t}`]],
|
|
248
|
+
['i64.load', ['local.get', `$${pair}`]],
|
|
249
|
+
['i64.load', ['i32.add', ['local.get', `$${pair}`], ['i32.const', 8]]]]]],
|
|
208
250
|
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
209
251
|
['br', `$loop${id}`]]],
|
|
210
252
|
['local.get', `$${t}`]], 'f64')
|
|
@@ -216,15 +258,25 @@ export default (ctx) => {
|
|
|
216
258
|
if (protoType === VAL.ARRAY) {
|
|
217
259
|
// Clone array data + link named-prop sidecar so for-in/bracket-name lookups
|
|
218
260
|
// keep working after Object.create (watr's ctx.local = Object.create(param) pattern).
|
|
261
|
+
// Header propsPtr lives at $off-16 (current ARRAY layout). We alias src's hash
|
|
262
|
+
// by copying the slot; __dyn_move covers the shifted-array case where props
|
|
263
|
+
// were migrated to the global __dyn_props.
|
|
219
264
|
inc('__arr_from', '__dyn_move', '__ptr_offset')
|
|
220
265
|
const src = temp('ocs')
|
|
221
266
|
const dst = temp('ocd')
|
|
267
|
+
const srcOff = tempI32('ocso')
|
|
268
|
+
const dstOff = tempI32('ocdo')
|
|
222
269
|
return typed(['block', ['result', 'f64'],
|
|
223
270
|
['local.set', `$${src}`, asF64(emit(proto))],
|
|
224
|
-
['local.set', `$${dst}`, ['call', '$__arr_from', ['local.get', `$${src}`]]],
|
|
271
|
+
['local.set', `$${dst}`, ['call', '$__arr_from', ['i64.reinterpret_f64', ['local.get', `$${src}`]]]],
|
|
272
|
+
['local.set', `$${srcOff}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${src}`]]]],
|
|
273
|
+
['local.set', `$${dstOff}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${dst}`]]]],
|
|
274
|
+
['f64.store',
|
|
275
|
+
['i32.sub', ['local.get', `$${dstOff}`], ['i32.const', 16]],
|
|
276
|
+
['f64.load', ['i32.sub', ['local.get', `$${srcOff}`], ['i32.const', 16]]]],
|
|
225
277
|
['call', '$__dyn_move',
|
|
226
|
-
['
|
|
227
|
-
['
|
|
278
|
+
['local.get', `$${srcOff}`],
|
|
279
|
+
['local.get', `$${dstOff}`]],
|
|
228
280
|
['local.get', `$${dst}`]], 'f64')
|
|
229
281
|
}
|
|
230
282
|
const schema = resolveSchema(proto)
|
|
@@ -233,15 +285,22 @@ export default (ctx) => {
|
|
|
233
285
|
const value = temp('ocr')
|
|
234
286
|
inc('__arr_from', '__dyn_move', '__ptr_offset')
|
|
235
287
|
const dst2 = temp('ocd')
|
|
288
|
+
const srcOff2 = tempI32('ocso')
|
|
289
|
+
const dstOff2 = tempI32('ocdo')
|
|
236
290
|
return typed(['block', ['result', 'f64'],
|
|
237
291
|
['local.set', `$${value}`, asF64(emit(proto))],
|
|
238
292
|
['if', ['result', 'f64'],
|
|
239
|
-
['i32.eq', ['call', '$__ptr_type', ['local.get', `$${value}`]], ['i32.const', PTR.ARRAY]],
|
|
293
|
+
['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${value}`]]], ['i32.const', PTR.ARRAY]],
|
|
240
294
|
['then', ['block', ['result', 'f64'],
|
|
241
|
-
['local.set', `$${dst2}`, ['call', '$__arr_from', ['local.get', `$${value}`]]],
|
|
295
|
+
['local.set', `$${dst2}`, ['call', '$__arr_from', ['i64.reinterpret_f64', ['local.get', `$${value}`]]]],
|
|
296
|
+
['local.set', `$${srcOff2}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${value}`]]]],
|
|
297
|
+
['local.set', `$${dstOff2}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${dst2}`]]]],
|
|
298
|
+
['f64.store',
|
|
299
|
+
['i32.sub', ['local.get', `$${dstOff2}`], ['i32.const', 16]],
|
|
300
|
+
['f64.load', ['i32.sub', ['local.get', `$${srcOff2}`], ['i32.const', 16]]]],
|
|
242
301
|
['call', '$__dyn_move',
|
|
243
|
-
['
|
|
244
|
-
['
|
|
302
|
+
['local.get', `$${srcOff2}`],
|
|
303
|
+
['local.get', `$${dstOff2}`]],
|
|
245
304
|
['local.get', `$${dst2}`]]],
|
|
246
305
|
['else', ['local.get', `$${value}`]]]] , 'f64')
|
|
247
306
|
}
|
|
@@ -253,8 +312,8 @@ export default (ctx) => {
|
|
|
253
312
|
const srcBase = tempI32('cb')
|
|
254
313
|
const body = [
|
|
255
314
|
['local.set', `$${s}`, asF64(emit(proto))],
|
|
256
|
-
['local.set', `$${t}`, ['call', '$
|
|
257
|
-
['local.set', `$${srcBase}`, ['call', '$__ptr_offset', ['local.get', `$${s}`]]],
|
|
315
|
+
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', Math.max(1, n)], ['i32.const', 8]]],
|
|
316
|
+
['local.set', `$${srcBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]],
|
|
258
317
|
]
|
|
259
318
|
// Copy all properties from proto
|
|
260
319
|
for (let i = 0; i < n; i++)
|
|
@@ -270,6 +329,10 @@ function resolveSchema(obj) {
|
|
|
270
329
|
if (typeof obj === 'string') return ctx.schema.resolve(obj)
|
|
271
330
|
if (Array.isArray(obj) && obj[0] === '{}')
|
|
272
331
|
return obj.slice(1).filter(p => Array.isArray(p) && p[0] === ':').map(p => p[1])
|
|
332
|
+
// JSON-shape inferred: JSON.parse(constStr) call or `.prop`/`[i]` chain
|
|
333
|
+
// resolving to a known OBJECT shape carries its key list as `names`.
|
|
334
|
+
const sh = shapeOf(obj)
|
|
335
|
+
if (sh?.vt === VAL.OBJECT && sh.names) return sh.names
|
|
273
336
|
return null
|
|
274
337
|
}
|
|
275
338
|
|
|
@@ -277,7 +340,16 @@ function resolveSchema(obj) {
|
|
|
277
340
|
* Emit object literal with spread: {...a, x: 1, ...b, y: 2}
|
|
278
341
|
* Merges schemas from all sources, allocates result, copies in order.
|
|
279
342
|
*/
|
|
280
|
-
function
|
|
343
|
+
function takeLiteralTarget() {
|
|
344
|
+
const frame = ctx.schema.targetStack.at(-1)
|
|
345
|
+
if (!frame) return null
|
|
346
|
+
if (typeof frame === 'string') return frame
|
|
347
|
+
if (!frame.active) return null
|
|
348
|
+
frame.active = false
|
|
349
|
+
return frame.name
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function emitObjectSpread(props, spreadTarget = takeLiteralTarget()) {
|
|
281
353
|
// Collect merged schema: union of all spread source schemas + explicit props
|
|
282
354
|
const allNames = []
|
|
283
355
|
const addName = n => { if (!allNames.includes(n)) allNames.push(n) }
|
|
@@ -301,7 +373,7 @@ function emitObjectSpread(props) {
|
|
|
301
373
|
const ptr = temp('objp')
|
|
302
374
|
const src = tempI32('osp')
|
|
303
375
|
|
|
304
|
-
const body = [['local.set', `$${t}`, ['call', '$
|
|
376
|
+
const body = [['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', Math.max(1, schema.length)], ['i32.const', 8]]]]
|
|
305
377
|
|
|
306
378
|
// Process props in order — later props override earlier (JS semantics)
|
|
307
379
|
let srcF
|
|
@@ -318,13 +390,13 @@ function emitObjectSpread(props) {
|
|
|
318
390
|
for (let i = 0; i < schema.length; i++) {
|
|
319
391
|
const slot = slotAddr(t, i)
|
|
320
392
|
body.push(['f64.store', slot,
|
|
321
|
-
['call', '$__dyn_get_or', ['local.get', `$${srcF}`],
|
|
322
|
-
emit(['str', String(schema[i])]),
|
|
323
|
-
['
|
|
393
|
+
['f64.reinterpret_i64', ['call', '$__dyn_get_or', ['i64.reinterpret_f64', ['local.get', `$${srcF}`]],
|
|
394
|
+
asI64(emit(['str', String(schema[i])])),
|
|
395
|
+
['i64.load', slot]]]])
|
|
324
396
|
}
|
|
325
397
|
continue
|
|
326
398
|
}
|
|
327
|
-
body.push(['local.set', `$${src}`, ['call', '$__ptr_offset', asF64(emit(p[1]))]])
|
|
399
|
+
body.push(['local.set', `$${src}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', asF64(emit(p[1]))]]])
|
|
328
400
|
for (let si = 0; si < sSchema.length; si++) {
|
|
329
401
|
const ti = schema.indexOf(sSchema[si])
|
|
330
402
|
if (ti < 0) continue
|
|
@@ -337,12 +409,11 @@ function emitObjectSpread(props) {
|
|
|
337
409
|
}
|
|
338
410
|
|
|
339
411
|
body.push(['local.set', `$${ptr}`, mkPtrIR(PTR.OBJECT, schemaId, ['local.get', `$${t}`])])
|
|
340
|
-
const spreadTarget = ctx.schema.targetStack.at(-1)
|
|
341
412
|
if (needsDynShadow(spreadTarget)) {
|
|
342
413
|
inc('__dyn_set')
|
|
343
414
|
for (let i = 0; i < schema.length; i++)
|
|
344
|
-
body.push(['drop', ['call', '$__dyn_set', ['local.get', `$${ptr}`], emit(['str', String(schema[i])]),
|
|
345
|
-
['
|
|
415
|
+
body.push(['drop', ['call', '$__dyn_set', ['i64.reinterpret_f64', ['local.get', `$${ptr}`]], asI64(emit(['str', String(schema[i])])),
|
|
416
|
+
['i64.load', slotAddr(t, i)]]])
|
|
346
417
|
}
|
|
347
418
|
body.push(['local.get', `$${ptr}`])
|
|
348
419
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
@@ -357,3 +428,112 @@ function emitStringArray(names) {
|
|
|
357
428
|
body.push(out.ptr)
|
|
358
429
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
359
430
|
}
|
|
431
|
+
|
|
432
|
+
// VAL.HASH covers both literal-typed bindings and JSON-shape inferred chains
|
|
433
|
+
// (e.g. JSON.parse('{...}') → walked via shapeOf for nested `.prop` access).
|
|
434
|
+
// Schema fallback only fires when the static path can't classify the receiver.
|
|
435
|
+
function isHashTyped(obj) {
|
|
436
|
+
if (typeof obj === 'string') return lookupValType(obj) === VAL.HASH
|
|
437
|
+
return valTypeOf(obj) === VAL.HASH
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// HASH layout: open-addressed probe table, each entry 24 bytes —
|
|
441
|
+
// [hash:f64][key:f64][value:f64]. Slot is empty when hash field == 0
|
|
442
|
+
// (tombstone == 1). __len exposes live entry count at off-8; __cap exposes
|
|
443
|
+
// slot count at off-4. Output array is pre-sized to __len; walk all cap
|
|
444
|
+
// slots and append occupied keys. Iteration order is hash-derived, matching
|
|
445
|
+
// jz's `for-in` over HASH — not the JS spec's insertion order.
|
|
446
|
+
function emitHashKeys(obj) {
|
|
447
|
+
const t = temp('hk')
|
|
448
|
+
return typed(['block', ['result', 'f64'],
|
|
449
|
+
['local.set', `$${t}`, asF64(emit(obj))],
|
|
450
|
+
hashKeysFromTemp(t)], 'f64')
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Inline body of the HASH walk against an already-bound f64 local. Shared by
|
|
454
|
+
// the static-HASH path and the runtime-dispatch path so both produce the same
|
|
455
|
+
// IR shape from the same source — only difference is whether they enter from
|
|
456
|
+
// a static type guard or a runtime ptr-type check.
|
|
457
|
+
function hashKeysFromTemp(t) {
|
|
458
|
+
inc('__ptr_offset', '__cap', '__len')
|
|
459
|
+
const off = tempI32('hko'), cap = tempI32('hkc'), n = tempI32('hkn')
|
|
460
|
+
const i = tempI32('hki'), o = tempI32('hkj'), slot = tempI32('hks')
|
|
461
|
+
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${n}`], tag: 'hka' })
|
|
462
|
+
const id = ctx.func.uniq++
|
|
463
|
+
return ['block', ['result', 'f64'],
|
|
464
|
+
['local.set', `$${n}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
465
|
+
out.init,
|
|
466
|
+
['local.set', `$${off}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
467
|
+
['local.set', `$${cap}`, ['call', '$__cap', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
468
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
469
|
+
['local.set', `$${o}`, ['i32.const', 0]],
|
|
470
|
+
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
471
|
+
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${cap}`]]],
|
|
472
|
+
['local.set', `$${slot}`, ['i32.add', ['local.get', `$${off}`],
|
|
473
|
+
['i32.mul', ['local.get', `$${i}`], ['i32.const', 24]]]],
|
|
474
|
+
['if', ['f64.ne', ['f64.load', ['local.get', `$${slot}`]], ['f64.const', 0]],
|
|
475
|
+
['then',
|
|
476
|
+
elemStore(out.local, o,
|
|
477
|
+
['f64.load', ['i32.add', ['local.get', `$${slot}`], ['i32.const', 8]]]),
|
|
478
|
+
['local.set', `$${o}`, ['i32.add', ['local.get', `$${o}`], ['i32.const', 1]]]]],
|
|
479
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
480
|
+
['br', `$loop${id}`]]],
|
|
481
|
+
out.ptr]
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Type-unknown receiver: bind the value, branch on ptr-type. HASH walks the
|
|
485
|
+
// probe table; OBJECT loads the schema's key array (registered statically at
|
|
486
|
+
// compile time or lazily at runtime by JSON.parse via __jp_schema_get); other
|
|
487
|
+
// types (ARRAY, nullish, primitives) return an empty array. The empty-array
|
|
488
|
+
// fallback is allocated in all arms for type uniformity at the if boundary.
|
|
489
|
+
function emitRuntimeKeys(obj) {
|
|
490
|
+
inc('__ptr_type')
|
|
491
|
+
// Ensure the schema table global exists even in programs that never use
|
|
492
|
+
// JSON.parse or compile-time schemas — the OBJECT arm reads it at runtime
|
|
493
|
+
// and the watr resolver requires the symbol to be declared.
|
|
494
|
+
if (!ctx.scope.globals.has('__schema_tbl'))
|
|
495
|
+
ctx.scope.globals.set('__schema_tbl', '(global $__schema_tbl (mut i32) (i32.const 0))')
|
|
496
|
+
const t = temp('rk'), tt = tempI32('rkt')
|
|
497
|
+
const empty = allocPtr({ type: PTR.ARRAY, len: 0, tag: 'rke' })
|
|
498
|
+
return typed(['block', ['result', 'f64'],
|
|
499
|
+
['local.set', `$${t}`, asF64(emit(obj))],
|
|
500
|
+
['local.set', `$${tt}`, ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
501
|
+
['if', ['result', 'f64'],
|
|
502
|
+
['i32.eq', ['local.get', `$${tt}`], ['i32.const', PTR.HASH]],
|
|
503
|
+
['then', hashKeysFromTemp(t)],
|
|
504
|
+
['else', ['if', ['result', 'f64'],
|
|
505
|
+
['i32.and',
|
|
506
|
+
['i32.eq', ['local.get', `$${tt}`], ['i32.const', PTR.OBJECT]],
|
|
507
|
+
['i32.ne', ['global.get', '$__schema_tbl'], ['i32.const', 0]]],
|
|
508
|
+
['then', objectKeysFromTemp(t)],
|
|
509
|
+
['else', ['block', ['result', 'f64'], empty.init, empty.ptr]]]]]], 'f64')
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Schema-keyed Object.keys: copy the schema's keys array (a jz Array of
|
|
513
|
+
// STRINGs registered in __schema_tbl[sid]) into a fresh ARRAY so callers can
|
|
514
|
+
// mutate without aliasing the schema substrate.
|
|
515
|
+
function objectKeysFromTemp(t) {
|
|
516
|
+
inc('__alloc_hdr')
|
|
517
|
+
const sid = tempI32('oks'), src = tempI32('oksrc'), n = tempI32('okn'), out = tempI32('oko'), i = tempI32('oki')
|
|
518
|
+
const id = ctx.func.uniq++
|
|
519
|
+
return ['block', ['result', 'f64'],
|
|
520
|
+
['local.set', `$${sid}`, ['i32.wrap_i64', ['i64.and',
|
|
521
|
+
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const', LAYOUT.AUX_SHIFT]],
|
|
522
|
+
['i64.const', LAYOUT.AUX_MASK]]]],
|
|
523
|
+
['local.set', `$${src}`, ['i32.wrap_i64', ['i64.and',
|
|
524
|
+
['i64.load', ['i32.add', ['global.get', '$__schema_tbl'], ['i32.shl', ['local.get', `$${sid}`], ['i32.const', 3]]]],
|
|
525
|
+
['i64.const', LAYOUT.OFFSET_MASK]]]],
|
|
526
|
+
['local.set', `$${n}`, ['i32.load', ['i32.sub', ['local.get', `$${src}`], ['i32.const', 8]]]],
|
|
527
|
+
['local.set', `$${out}`, ['call', '$__alloc_hdr',
|
|
528
|
+
['local.get', `$${n}`], ['local.get', `$${n}`], ['i32.const', 8]]],
|
|
529
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
530
|
+
['block', `$kbrk${id}`, ['loop', `$kloop${id}`,
|
|
531
|
+
['br_if', `$kbrk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${n}`]]],
|
|
532
|
+
['i64.store',
|
|
533
|
+
['i32.add', ['local.get', `$${out}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]],
|
|
534
|
+
['i64.load',
|
|
535
|
+
['i32.add', ['local.get', `$${src}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]],
|
|
536
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
537
|
+
['br', `$kloop${id}`]]],
|
|
538
|
+
mkPtrIR(PTR.ARRAY, 0, ['local.get', `$${out}`])]
|
|
539
|
+
}
|
package/module/regex.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* @module regex
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { typed, asF64, UNDEF_NAN, mkPtrIR, temp, tempI32 } from '../src/ir.js'
|
|
10
|
+
import { typed, asF64, asI64, UNDEF_NAN, mkPtrIR, temp, tempI32 } from '../src/ir.js'
|
|
11
11
|
import { emit } from '../src/emit.js'
|
|
12
|
-
import { err, inc, PTR } from '../src/ctx.js'
|
|
12
|
+
import { err, inc, PTR, LAYOUT } from '../src/ctx.js'
|
|
13
13
|
|
|
14
14
|
// Build IR that constructs a match array: [full, cap1, cap2, ...]
|
|
15
15
|
// strLocal, msLocal, meLocal are local names (i32 for ms/me, f64 for str).
|
|
@@ -23,7 +23,7 @@ const buildMatchArr = (strLocal, msLocal, meLocal, nGroups) => {
|
|
|
23
23
|
['i32.store', ['local.get', `$${arr}`], ['i32.const', N]],
|
|
24
24
|
['i32.store', ['i32.add', ['local.get', `$${arr}`], ['i32.const', 4]], ['i32.const', N]],
|
|
25
25
|
['f64.store', ['i32.add', ['local.get', `$${arr}`], ['i32.const', 8]],
|
|
26
|
-
['call', '$__str_slice', ['local.get', `$${strLocal}`],
|
|
26
|
+
['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${strLocal}`]],
|
|
27
27
|
['local.get', `$${msLocal}`], ['local.get', `$${meLocal}`]]],
|
|
28
28
|
]
|
|
29
29
|
for (let i = 1; i <= nGroups; i++) {
|
|
@@ -31,7 +31,7 @@ const buildMatchArr = (strLocal, msLocal, meLocal, nGroups) => {
|
|
|
31
31
|
['if', ['result', 'f64'],
|
|
32
32
|
['i32.lt_s', ['global.get', `$__re_g${i}_start`], ['i32.const', 0]],
|
|
33
33
|
['then', ['f64.const', `nan:${UNDEF_NAN}`]],
|
|
34
|
-
['else', ['call', '$__str_slice', ['local.get', `$${strLocal}`],
|
|
34
|
+
['else', ['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${strLocal}`]],
|
|
35
35
|
['global.get', `$__re_g${i}_start`], ['global.get', `$__re_g${i}_end`]]]]])
|
|
36
36
|
}
|
|
37
37
|
stmts.push(mkPtrIR(PTR.ARRAY, 0, ['i32.add', ['local.get', `$${arr}`], ['i32.const', 8]]))
|
|
@@ -654,14 +654,16 @@ export default (ctx) => {
|
|
|
654
654
|
|
|
655
655
|
ctx.runtime.regex = { count: 0, vars: new Map(), compiled: new Map(), groups: new Map() }
|
|
656
656
|
|
|
657
|
-
// SSO → heap normalizer: returns data offset (i32) for direct byte access
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
(
|
|
657
|
+
// SSO → heap normalizer: returns data offset (i32) for direct byte access.
|
|
658
|
+
// Heap STRING: aux bit SSO_BIT is 0 → offset already points at bytes.
|
|
659
|
+
// SSO STRING: aux bit SSO_BIT is 1 → bytes are packed in offset; spill to heap.
|
|
660
|
+
ctx.core.stdlib['__str_to_buf'] = `(func $__str_to_buf (param $ptr i64) (result i32)
|
|
661
|
+
(local $aux i32) (local $off i32) (local $len i32) (local $buf i32) (local $i i32)
|
|
662
|
+
(local.set $aux (call $__ptr_aux (local.get $ptr)))
|
|
663
|
+
(if (i32.eqz (i32.and (local.get $aux) (i32.const ${LAYOUT.SSO_BIT})))
|
|
662
664
|
(then (return (call $__ptr_offset (local.get $ptr)))))
|
|
663
665
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
664
|
-
(local.set $len (
|
|
666
|
+
(local.set $len (i32.and (local.get $aux) (i32.const 7)))
|
|
665
667
|
(local.set $buf (call $__alloc (local.get $len)))
|
|
666
668
|
(local.set $i (i32.const 0))
|
|
667
669
|
(block $done (loop $next
|
|
@@ -691,7 +693,7 @@ export default (ctx) => {
|
|
|
691
693
|
|
|
692
694
|
// Search wrapper: tries match at each position, returns (match_start, match_end) via locals
|
|
693
695
|
const searchName = `__regex_search_${id}`
|
|
694
|
-
ctx.core.stdlib[searchName] = `(func $${searchName} (param $str
|
|
696
|
+
ctx.core.stdlib[searchName] = `(func $${searchName} (param $str i64) (result i32 i32)
|
|
695
697
|
(local $off i32) (local $len i32) (local $pos i32) (local $result i32)
|
|
696
698
|
(local.set $off (call $__str_to_buf (local.get $str)))
|
|
697
699
|
(local.set $len (call $__str_byteLen (local.get $str)))
|
|
@@ -735,7 +737,7 @@ export default (ctx) => {
|
|
|
735
737
|
return typed(['block', ['result', 'f64'],
|
|
736
738
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
737
739
|
['local.set', `$${mstart}`, ['local.set', `$${mend}`,
|
|
738
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
740
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
739
741
|
// search returns (start, end) multi-value; capture both
|
|
740
742
|
['if', ['result', 'f64'], ['i32.ge_s', ['local.get', `$${mstart}`], ['i32.const', 0]],
|
|
741
743
|
['then', ['f64.const', 1]],
|
|
@@ -751,7 +753,7 @@ export default (ctx) => {
|
|
|
751
753
|
return typed(['block', ['result', 'f64'],
|
|
752
754
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
753
755
|
['local.set', `$${ms}`, ['local.set', `$${me}`,
|
|
754
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
756
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
755
757
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${ms}`], ['i32.const', 0]],
|
|
756
758
|
['then', ['f64.const', 0]],
|
|
757
759
|
['else', buildMatchArr(s, ms, me, nGroups)]]], 'f64')
|
|
@@ -763,13 +765,13 @@ export default (ctx) => {
|
|
|
763
765
|
if (id == null) {
|
|
764
766
|
// Fall back to string search (indexOf)
|
|
765
767
|
inc('__str_indexof')
|
|
766
|
-
return typed(['f64.convert_i32_s', ['call', '$__str_indexof',
|
|
768
|
+
return typed(['f64.convert_i32_s', ['call', '$__str_indexof', asI64(emit(str)), asI64(emit(search)), ['i32.const', 0]]], 'f64')
|
|
767
769
|
}
|
|
768
770
|
const s = temp('ss'), ms = tempI32('ssms'), me = tempI32('ssme')
|
|
769
771
|
return typed(['block', ['result', 'f64'],
|
|
770
772
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
771
773
|
['local.set', `$${ms}`, ['local.set', `$${me}`,
|
|
772
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
774
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
773
775
|
['f64.convert_i32_s', ['local.get', `$${ms}`]]], 'f64')
|
|
774
776
|
}
|
|
775
777
|
|
|
@@ -783,21 +785,22 @@ export default (ctx) => {
|
|
|
783
785
|
return typed(['block', ['result', 'f64'],
|
|
784
786
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
785
787
|
['local.set', `$${q}`, asF64(emit(search))],
|
|
786
|
-
['local.set', `$${idx}`, ['call', '$__str_indexof', ['local.get', `$${s}`], ['local.get', `$${q}`]]],
|
|
788
|
+
['local.set', `$${idx}`, ['call', '$__str_indexof', ['i64.reinterpret_f64', ['local.get', `$${s}`]], ['i64.reinterpret_f64', ['local.get', `$${q}`]], ['i32.const', 0]]],
|
|
787
789
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${idx}`], ['i32.const', 0]],
|
|
788
790
|
['then', ['f64.const', 0]],
|
|
789
791
|
['else',
|
|
790
792
|
['call', '$__wrap1',
|
|
791
|
-
['
|
|
792
|
-
['local.get', `$${
|
|
793
|
-
|
|
793
|
+
['i64.reinterpret_f64',
|
|
794
|
+
['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${s}`]],
|
|
795
|
+
['local.get', `$${idx}`],
|
|
796
|
+
['i32.add', ['local.get', `$${idx}`], ['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${q}`]]]]]]]]]], 'f64')
|
|
794
797
|
}
|
|
795
798
|
const nGroups = ctx.runtime.regex.groups.get(id) || 0
|
|
796
799
|
const s = temp('sm'), ms = tempI32('smms'), me = tempI32('smme')
|
|
797
800
|
return typed(['block', ['result', 'f64'],
|
|
798
801
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
799
802
|
['local.set', `$${ms}`, ['local.set', `$${me}`,
|
|
800
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
803
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
801
804
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${ms}`], ['i32.const', 0]],
|
|
802
805
|
['then', ['f64.const', 0]],
|
|
803
806
|
['else', buildMatchArr(s, ms, me, nGroups)]]], 'f64')
|
|
@@ -809,7 +812,7 @@ export default (ctx) => {
|
|
|
809
812
|
if (id == null) {
|
|
810
813
|
// Fall back to string replace
|
|
811
814
|
inc('__str_replace')
|
|
812
|
-
return typed(['call', '$__str_replace',
|
|
815
|
+
return typed(['call', '$__str_replace', asI64(emit(str)), asI64(emit(search)), asI64(emit(repl))], 'f64')
|
|
813
816
|
}
|
|
814
817
|
inc('__str_slice', '__str_concat', '__str_byteLen')
|
|
815
818
|
const s = temp('sr'), r = temp('srr'), ms = tempI32('srms'), me = tempI32('srme')
|
|
@@ -817,16 +820,16 @@ export default (ctx) => {
|
|
|
817
820
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
818
821
|
['local.set', `$${r}`, asF64(emit(repl))],
|
|
819
822
|
['local.set', `$${ms}`, ['local.set', `$${me}`,
|
|
820
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
823
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
821
824
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${ms}`], ['i32.const', 0]],
|
|
822
825
|
['then', ['local.get', `$${s}`]],
|
|
823
826
|
['else',
|
|
824
827
|
['call', '$__str_concat',
|
|
825
|
-
['call', '$__str_concat',
|
|
826
|
-
['call', '$__str_slice', ['local.get', `$${s}`], ['i32.const', 0], ['local.get', `$${ms}`]],
|
|
827
|
-
['local.get', `$${r}`]],
|
|
828
|
-
['call', '$__str_slice', ['local.get', `$${s}`], ['local.get', `$${me}`],
|
|
829
|
-
['call', '$__str_byteLen', ['local.get', `$${s}`]]]]]]], 'f64')
|
|
828
|
+
['i64.reinterpret_f64', ['call', '$__str_concat',
|
|
829
|
+
['i64.reinterpret_f64', ['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${s}`]], ['i32.const', 0], ['local.get', `$${ms}`]]],
|
|
830
|
+
['i64.reinterpret_f64', ['local.get', `$${r}`]]]],
|
|
831
|
+
['i64.reinterpret_f64', ['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${s}`]], ['local.get', `$${me}`],
|
|
832
|
+
['call', '$__str_byteLen', ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]]]]]], 'f64')
|
|
830
833
|
}
|
|
831
834
|
|
|
832
835
|
// str.split(/re/) → array of substrings
|
|
@@ -835,14 +838,14 @@ export default (ctx) => {
|
|
|
835
838
|
if (id == null) {
|
|
836
839
|
// Fall back to string split
|
|
837
840
|
inc('__str_split')
|
|
838
|
-
return typed(['call', '$__str_split',
|
|
841
|
+
return typed(['call', '$__str_split', asI64(emit(str)), asI64(emit(sep))], 'f64')
|
|
839
842
|
}
|
|
840
843
|
|
|
841
844
|
// Generate a split-by-regex WAT function for this regex
|
|
842
845
|
const splitName = `__regex_split_${id}`
|
|
843
846
|
if (!ctx.core.stdlib[splitName]) {
|
|
844
847
|
inc('__str_to_buf', '__str_slice', '__alloc')
|
|
845
|
-
ctx.core.stdlib[splitName] = `(func $${splitName} (param $str
|
|
848
|
+
ctx.core.stdlib[splitName] = `(func $${splitName} (param $str i64) (result f64)
|
|
846
849
|
(local $off i32) (local $len i32) (local $pos i32) (local $result i32)
|
|
847
850
|
(local $mstart i32) (local $mend i32) (local $prevEnd i32)
|
|
848
851
|
(local $arrOff i32) (local $count i32) (local $cap i32)
|
|
@@ -909,6 +912,6 @@ export default (ctx) => {
|
|
|
909
912
|
inc(splitName)
|
|
910
913
|
}
|
|
911
914
|
|
|
912
|
-
return typed(['call', `$${splitName}`,
|
|
915
|
+
return typed(['call', `$${splitName}`, asI64(emit(str))], 'f64')
|
|
913
916
|
}
|
|
914
917
|
}
|