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/object.js
CHANGED
|
@@ -7,34 +7,51 @@
|
|
|
7
7
|
* @module object
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { typed, asF64, asI64, temp, tempI32, allocPtr, needsDynShadow, mkPtrIR, extractF64Bits, appendStaticSlots, slotAddr, elemStore } from '../src/ir.js'
|
|
11
|
+
import { emit } from '../src/emit.js'
|
|
12
|
+
import { valTypeOf, lookupValType, VAL, repOf, updateRep, shapeOf } from '../src/analyze.js'
|
|
13
|
+
import { ctx, err, inc, PTR, LAYOUT } from '../src/ctx.js'
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
export default (ctx) => {
|
|
15
17
|
inc('__mkptr', '__alloc', '__alloc_hdr', '__ptr_offset', '__len', '__ptr_type')
|
|
16
18
|
|
|
17
|
-
// 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).
|
|
18
25
|
ctx.core.emit['{}'] = (...rawProps) => {
|
|
19
|
-
if (rawProps.length === 0)
|
|
20
|
-
|
|
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
|
+
}
|
|
21
37
|
|
|
22
38
|
// Flatten comma-grouped props: [',', p1, p2] → [p1, p2]
|
|
23
39
|
const props = rawProps.length === 1 && Array.isArray(rawProps[0]) && rawProps[0][0] === ','
|
|
24
40
|
? rawProps[0].slice(1) : rawProps
|
|
25
41
|
|
|
42
|
+
const target = takeLiteralTarget()
|
|
43
|
+
|
|
26
44
|
// Object spread: {...a, x: 1, ...b} — merge schemas, copy props from sources
|
|
27
45
|
const hasSpreads = props.some(p => Array.isArray(p) && p[0] === '...')
|
|
28
|
-
if (hasSpreads) return emitObjectSpread(props)
|
|
46
|
+
if (hasSpreads) return emitObjectSpread(props, target)
|
|
29
47
|
|
|
30
48
|
const names = [], values = []
|
|
31
49
|
for (const p of props) {
|
|
32
50
|
if (Array.isArray(p) && p[0] === ':') { names.push(p[1]); values.push(p[2]) }
|
|
33
51
|
}
|
|
34
52
|
|
|
35
|
-
// 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.
|
|
36
54
|
let schemaId = ctx.schema.register(names)
|
|
37
|
-
const target = ctx.schema.targetStack.at(-1)
|
|
38
55
|
if (target) {
|
|
39
56
|
const merged = ctx.schema.resolve(target)
|
|
40
57
|
if (merged) schemaId = ctx.schema.idOf(target)
|
|
@@ -57,15 +74,15 @@ export default (ctx) => {
|
|
|
57
74
|
inc('__dyn_set')
|
|
58
75
|
const body = [['local.set', `$${ptr}`, staticPtr]]
|
|
59
76
|
for (let i = 0; i < schema.length; i++)
|
|
60
|
-
body.push(['drop', ['call', '$__dyn_set', ['local.get', `$${ptr}`],
|
|
61
|
-
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])]])
|
|
62
79
|
body.push(['local.get', `$${ptr}`])
|
|
63
80
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
64
81
|
}
|
|
65
82
|
}
|
|
66
83
|
|
|
67
84
|
const body = [
|
|
68
|
-
['local.set', `$${t}`, ['call', '$
|
|
85
|
+
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', Math.max(1, schema.length)], ['i32.const', 8]]],
|
|
69
86
|
]
|
|
70
87
|
for (let i = 0; i < values.length; i++)
|
|
71
88
|
body.push(['f64.store', slotAddr(t, i), asF64(emit(values[i]))])
|
|
@@ -73,8 +90,8 @@ export default (ctx) => {
|
|
|
73
90
|
if (shadow) {
|
|
74
91
|
inc('__dyn_set')
|
|
75
92
|
for (let i = 0; i < schema.length; i++)
|
|
76
|
-
body.push(['drop', ['call', '$__dyn_set', ['local.get', `$${ptr}`], emit(['str', String(schema[i])]),
|
|
77
|
-
['
|
|
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)]]])
|
|
78
95
|
}
|
|
79
96
|
body.push(['local.get', `$${ptr}`])
|
|
80
97
|
|
|
@@ -84,10 +101,37 @@ export default (ctx) => {
|
|
|
84
101
|
// === Object static methods ===
|
|
85
102
|
|
|
86
103
|
ctx.core.emit['Object.keys'] = (obj) => {
|
|
104
|
+
if (isHashTyped(obj)) return emitHashKeys(obj)
|
|
87
105
|
const schema = resolveSchema(obj)
|
|
88
|
-
if (
|
|
89
|
-
|
|
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')
|
|
90
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']
|
|
91
135
|
|
|
92
136
|
ctx.core.emit['Object.values'] = (obj) => {
|
|
93
137
|
const schema = resolveSchema(obj)
|
|
@@ -97,7 +141,7 @@ export default (ctx) => {
|
|
|
97
141
|
const t = temp('ov'), base = tempI32('vb')
|
|
98
142
|
const out = allocPtr({ type: PTR.ARRAY, len: n, tag: 'oa' })
|
|
99
143
|
const body = [['local.set', `$${t}`, va], out.init,
|
|
100
|
-
['local.set', `$${base}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]]]
|
|
144
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
101
145
|
for (let i = 0; i < n; i++)
|
|
102
146
|
body.push(['f64.store', slotAddr(out.local, i), ['f64.load', slotAddr(base, i)]])
|
|
103
147
|
body.push(out.ptr)
|
|
@@ -112,7 +156,7 @@ export default (ctx) => {
|
|
|
112
156
|
const t = temp('oe'), pair = tempI32('op'), base = tempI32('eb')
|
|
113
157
|
const out = allocPtr({ type: PTR.ARRAY, len: n, tag: 'oa' })
|
|
114
158
|
const body = [['local.set', `$${t}`, va], out.init,
|
|
115
|
-
['local.set', `$${base}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]]]
|
|
159
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
116
160
|
for (let i = 0; i < n; i++) {
|
|
117
161
|
body.push(
|
|
118
162
|
['local.set', `$${pair}`, ['call', '$__alloc_hdr', ['i32.const', 2], ['i32.const', 2], ['i32.const', 8]]],
|
|
@@ -140,14 +184,14 @@ export default (ctx) => {
|
|
|
140
184
|
updateRep(target, { schemaId })
|
|
141
185
|
const t = tempI32('bx'), s = temp('bs')
|
|
142
186
|
const body = [
|
|
143
|
-
['local.set', `$${t}`, ['call', '$
|
|
187
|
+
['local.set', `$${t}`, ['call', '$__alloc_hdr', ['i32.const', 0], ['i32.const', Math.max(1, boxedSchema.length)], ['i32.const', 8]]],
|
|
144
188
|
['f64.store', ['local.get', `$${t}`], asF64(emit(target))],
|
|
145
189
|
]
|
|
146
190
|
const sBase = tempI32('sb')
|
|
147
191
|
for (const source of sources) {
|
|
148
192
|
const sSchema = resolveSchema(source)
|
|
149
193
|
body.push(['local.set', `$${s}`, asF64(emit(source))])
|
|
150
|
-
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}`]]]])
|
|
151
195
|
for (let si = 0; si < sSchema.length; si++) {
|
|
152
196
|
const ti = boxedSchema.indexOf(sSchema[si])
|
|
153
197
|
if (ti < 0) continue
|
|
@@ -165,12 +209,12 @@ export default (ctx) => {
|
|
|
165
209
|
const t = temp('at'), s = temp('as')
|
|
166
210
|
const tBase = tempI32('tb'), sBase2 = tempI32('sb')
|
|
167
211
|
const body = [['local.set', `$${t}`, asF64(emit(target))],
|
|
168
|
-
['local.set', `$${tBase}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]]]
|
|
212
|
+
['local.set', `$${tBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]
|
|
169
213
|
for (const source of sources) {
|
|
170
214
|
const sSchema = resolveSchema(source)
|
|
171
215
|
if (!sSchema) err('Object.assign: source needs known schema')
|
|
172
216
|
body.push(['local.set', `$${s}`, asF64(emit(source))])
|
|
173
|
-
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}`]]]])
|
|
174
218
|
for (let si = 0; si < sSchema.length; si++) {
|
|
175
219
|
const ti = tSchema.indexOf(sSchema[si])
|
|
176
220
|
if (ti < 0) continue
|
|
@@ -191,18 +235,18 @@ export default (ctx) => {
|
|
|
191
235
|
const id = ctx.func.uniq++
|
|
192
236
|
return typed(['block', ['result', 'f64'],
|
|
193
237
|
['local.set', `$${t}`, ['call', '$__hash_new']],
|
|
194
|
-
['local.set', `$${ptr}`, ['call', '$__ptr_offset', va]],
|
|
195
|
-
['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]]],
|
|
196
240
|
['local.set', `$${i}`, ['i32.const', 0]],
|
|
197
241
|
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
198
242
|
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
199
243
|
// Load pair (array of 2): pair = ptr_offset(arr[i])
|
|
200
|
-
['local.set', `$${pair}`, ['call', '$__ptr_offset',
|
|
201
|
-
['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]]]]]]],
|
|
202
246
|
// hash_set(result, pair[0], pair[1])
|
|
203
|
-
['local.set', `$${t}`, ['call', '$__hash_set', ['local.get', `$${t}`],
|
|
204
|
-
['
|
|
205
|
-
['
|
|
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]]]]]],
|
|
206
250
|
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
207
251
|
['br', `$loop${id}`]]],
|
|
208
252
|
['local.get', `$${t}`]], 'f64')
|
|
@@ -214,15 +258,25 @@ export default (ctx) => {
|
|
|
214
258
|
if (protoType === VAL.ARRAY) {
|
|
215
259
|
// Clone array data + link named-prop sidecar so for-in/bracket-name lookups
|
|
216
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.
|
|
217
264
|
inc('__arr_from', '__dyn_move', '__ptr_offset')
|
|
218
265
|
const src = temp('ocs')
|
|
219
266
|
const dst = temp('ocd')
|
|
267
|
+
const srcOff = tempI32('ocso')
|
|
268
|
+
const dstOff = tempI32('ocdo')
|
|
220
269
|
return typed(['block', ['result', 'f64'],
|
|
221
270
|
['local.set', `$${src}`, asF64(emit(proto))],
|
|
222
|
-
['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]]]],
|
|
223
277
|
['call', '$__dyn_move',
|
|
224
|
-
['
|
|
225
|
-
['
|
|
278
|
+
['local.get', `$${srcOff}`],
|
|
279
|
+
['local.get', `$${dstOff}`]],
|
|
226
280
|
['local.get', `$${dst}`]], 'f64')
|
|
227
281
|
}
|
|
228
282
|
const schema = resolveSchema(proto)
|
|
@@ -231,15 +285,22 @@ export default (ctx) => {
|
|
|
231
285
|
const value = temp('ocr')
|
|
232
286
|
inc('__arr_from', '__dyn_move', '__ptr_offset')
|
|
233
287
|
const dst2 = temp('ocd')
|
|
288
|
+
const srcOff2 = tempI32('ocso')
|
|
289
|
+
const dstOff2 = tempI32('ocdo')
|
|
234
290
|
return typed(['block', ['result', 'f64'],
|
|
235
291
|
['local.set', `$${value}`, asF64(emit(proto))],
|
|
236
292
|
['if', ['result', 'f64'],
|
|
237
|
-
['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]],
|
|
238
294
|
['then', ['block', ['result', 'f64'],
|
|
239
|
-
['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]]]],
|
|
240
301
|
['call', '$__dyn_move',
|
|
241
|
-
['
|
|
242
|
-
['
|
|
302
|
+
['local.get', `$${srcOff2}`],
|
|
303
|
+
['local.get', `$${dstOff2}`]],
|
|
243
304
|
['local.get', `$${dst2}`]]],
|
|
244
305
|
['else', ['local.get', `$${value}`]]]] , 'f64')
|
|
245
306
|
}
|
|
@@ -251,8 +312,8 @@ export default (ctx) => {
|
|
|
251
312
|
const srcBase = tempI32('cb')
|
|
252
313
|
const body = [
|
|
253
314
|
['local.set', `$${s}`, asF64(emit(proto))],
|
|
254
|
-
['local.set', `$${t}`, ['call', '$
|
|
255
|
-
['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}`]]]],
|
|
256
317
|
]
|
|
257
318
|
// Copy all properties from proto
|
|
258
319
|
for (let i = 0; i < n; i++)
|
|
@@ -268,6 +329,10 @@ function resolveSchema(obj) {
|
|
|
268
329
|
if (typeof obj === 'string') return ctx.schema.resolve(obj)
|
|
269
330
|
if (Array.isArray(obj) && obj[0] === '{}')
|
|
270
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
|
|
271
336
|
return null
|
|
272
337
|
}
|
|
273
338
|
|
|
@@ -275,7 +340,16 @@ function resolveSchema(obj) {
|
|
|
275
340
|
* Emit object literal with spread: {...a, x: 1, ...b, y: 2}
|
|
276
341
|
* Merges schemas from all sources, allocates result, copies in order.
|
|
277
342
|
*/
|
|
278
|
-
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()) {
|
|
279
353
|
// Collect merged schema: union of all spread source schemas + explicit props
|
|
280
354
|
const allNames = []
|
|
281
355
|
const addName = n => { if (!allNames.includes(n)) allNames.push(n) }
|
|
@@ -299,7 +373,7 @@ function emitObjectSpread(props) {
|
|
|
299
373
|
const ptr = temp('objp')
|
|
300
374
|
const src = tempI32('osp')
|
|
301
375
|
|
|
302
|
-
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]]]]
|
|
303
377
|
|
|
304
378
|
// Process props in order — later props override earlier (JS semantics)
|
|
305
379
|
let srcF
|
|
@@ -316,13 +390,13 @@ function emitObjectSpread(props) {
|
|
|
316
390
|
for (let i = 0; i < schema.length; i++) {
|
|
317
391
|
const slot = slotAddr(t, i)
|
|
318
392
|
body.push(['f64.store', slot,
|
|
319
|
-
['call', '$__dyn_get_or', ['local.get', `$${srcF}`],
|
|
320
|
-
emit(['str', String(schema[i])]),
|
|
321
|
-
['
|
|
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]]]])
|
|
322
396
|
}
|
|
323
397
|
continue
|
|
324
398
|
}
|
|
325
|
-
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]))]]])
|
|
326
400
|
for (let si = 0; si < sSchema.length; si++) {
|
|
327
401
|
const ti = schema.indexOf(sSchema[si])
|
|
328
402
|
if (ti < 0) continue
|
|
@@ -335,12 +409,11 @@ function emitObjectSpread(props) {
|
|
|
335
409
|
}
|
|
336
410
|
|
|
337
411
|
body.push(['local.set', `$${ptr}`, mkPtrIR(PTR.OBJECT, schemaId, ['local.get', `$${t}`])])
|
|
338
|
-
const spreadTarget = ctx.schema.targetStack.at(-1)
|
|
339
412
|
if (needsDynShadow(spreadTarget)) {
|
|
340
413
|
inc('__dyn_set')
|
|
341
414
|
for (let i = 0; i < schema.length; i++)
|
|
342
|
-
body.push(['drop', ['call', '$__dyn_set', ['local.get', `$${ptr}`], emit(['str', String(schema[i])]),
|
|
343
|
-
['
|
|
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)]]])
|
|
344
417
|
}
|
|
345
418
|
body.push(['local.get', `$${ptr}`])
|
|
346
419
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
@@ -355,3 +428,112 @@ function emitStringArray(names) {
|
|
|
355
428
|
body.push(out.ptr)
|
|
356
429
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
357
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,8 +7,9 @@
|
|
|
7
7
|
* @module regex
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { typed, asF64, asI64, UNDEF_NAN, mkPtrIR, temp, tempI32 } from '../src/ir.js'
|
|
11
|
+
import { emit } from '../src/emit.js'
|
|
12
|
+
import { err, inc, PTR, LAYOUT } from '../src/ctx.js'
|
|
12
13
|
|
|
13
14
|
// Build IR that constructs a match array: [full, cap1, cap2, ...]
|
|
14
15
|
// strLocal, msLocal, meLocal are local names (i32 for ms/me, f64 for str).
|
|
@@ -22,7 +23,7 @@ const buildMatchArr = (strLocal, msLocal, meLocal, nGroups) => {
|
|
|
22
23
|
['i32.store', ['local.get', `$${arr}`], ['i32.const', N]],
|
|
23
24
|
['i32.store', ['i32.add', ['local.get', `$${arr}`], ['i32.const', 4]], ['i32.const', N]],
|
|
24
25
|
['f64.store', ['i32.add', ['local.get', `$${arr}`], ['i32.const', 8]],
|
|
25
|
-
['call', '$__str_slice', ['local.get', `$${strLocal}`],
|
|
26
|
+
['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${strLocal}`]],
|
|
26
27
|
['local.get', `$${msLocal}`], ['local.get', `$${meLocal}`]]],
|
|
27
28
|
]
|
|
28
29
|
for (let i = 1; i <= nGroups; i++) {
|
|
@@ -30,7 +31,7 @@ const buildMatchArr = (strLocal, msLocal, meLocal, nGroups) => {
|
|
|
30
31
|
['if', ['result', 'f64'],
|
|
31
32
|
['i32.lt_s', ['global.get', `$__re_g${i}_start`], ['i32.const', 0]],
|
|
32
33
|
['then', ['f64.const', `nan:${UNDEF_NAN}`]],
|
|
33
|
-
['else', ['call', '$__str_slice', ['local.get', `$${strLocal}`],
|
|
34
|
+
['else', ['call', '$__str_slice', ['i64.reinterpret_f64', ['local.get', `$${strLocal}`]],
|
|
34
35
|
['global.get', `$__re_g${i}_start`], ['global.get', `$__re_g${i}_end`]]]]])
|
|
35
36
|
}
|
|
36
37
|
stmts.push(mkPtrIR(PTR.ARRAY, 0, ['i32.add', ['local.get', `$${arr}`], ['i32.const', 8]]))
|
|
@@ -653,14 +654,16 @@ export default (ctx) => {
|
|
|
653
654
|
|
|
654
655
|
ctx.runtime.regex = { count: 0, vars: new Map(), compiled: new Map(), groups: new Map() }
|
|
655
656
|
|
|
656
|
-
// SSO → heap normalizer: returns data offset (i32) for direct byte access
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
(
|
|
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})))
|
|
661
664
|
(then (return (call $__ptr_offset (local.get $ptr)))))
|
|
662
665
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
663
|
-
(local.set $len (
|
|
666
|
+
(local.set $len (i32.and (local.get $aux) (i32.const 7)))
|
|
664
667
|
(local.set $buf (call $__alloc (local.get $len)))
|
|
665
668
|
(local.set $i (i32.const 0))
|
|
666
669
|
(block $done (loop $next
|
|
@@ -690,7 +693,7 @@ export default (ctx) => {
|
|
|
690
693
|
|
|
691
694
|
// Search wrapper: tries match at each position, returns (match_start, match_end) via locals
|
|
692
695
|
const searchName = `__regex_search_${id}`
|
|
693
|
-
ctx.core.stdlib[searchName] = `(func $${searchName} (param $str
|
|
696
|
+
ctx.core.stdlib[searchName] = `(func $${searchName} (param $str i64) (result i32 i32)
|
|
694
697
|
(local $off i32) (local $len i32) (local $pos i32) (local $result i32)
|
|
695
698
|
(local.set $off (call $__str_to_buf (local.get $str)))
|
|
696
699
|
(local.set $len (call $__str_byteLen (local.get $str)))
|
|
@@ -734,7 +737,7 @@ export default (ctx) => {
|
|
|
734
737
|
return typed(['block', ['result', 'f64'],
|
|
735
738
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
736
739
|
['local.set', `$${mstart}`, ['local.set', `$${mend}`,
|
|
737
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
740
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
738
741
|
// search returns (start, end) multi-value; capture both
|
|
739
742
|
['if', ['result', 'f64'], ['i32.ge_s', ['local.get', `$${mstart}`], ['i32.const', 0]],
|
|
740
743
|
['then', ['f64.const', 1]],
|
|
@@ -750,7 +753,7 @@ export default (ctx) => {
|
|
|
750
753
|
return typed(['block', ['result', 'f64'],
|
|
751
754
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
752
755
|
['local.set', `$${ms}`, ['local.set', `$${me}`,
|
|
753
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
756
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
754
757
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${ms}`], ['i32.const', 0]],
|
|
755
758
|
['then', ['f64.const', 0]],
|
|
756
759
|
['else', buildMatchArr(s, ms, me, nGroups)]]], 'f64')
|
|
@@ -762,13 +765,13 @@ export default (ctx) => {
|
|
|
762
765
|
if (id == null) {
|
|
763
766
|
// Fall back to string search (indexOf)
|
|
764
767
|
inc('__str_indexof')
|
|
765
|
-
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')
|
|
766
769
|
}
|
|
767
770
|
const s = temp('ss'), ms = tempI32('ssms'), me = tempI32('ssme')
|
|
768
771
|
return typed(['block', ['result', 'f64'],
|
|
769
772
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
770
773
|
['local.set', `$${ms}`, ['local.set', `$${me}`,
|
|
771
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
774
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
772
775
|
['f64.convert_i32_s', ['local.get', `$${ms}`]]], 'f64')
|
|
773
776
|
}
|
|
774
777
|
|
|
@@ -782,21 +785,22 @@ export default (ctx) => {
|
|
|
782
785
|
return typed(['block', ['result', 'f64'],
|
|
783
786
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
784
787
|
['local.set', `$${q}`, asF64(emit(search))],
|
|
785
|
-
['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]]],
|
|
786
789
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${idx}`], ['i32.const', 0]],
|
|
787
790
|
['then', ['f64.const', 0]],
|
|
788
791
|
['else',
|
|
789
792
|
['call', '$__wrap1',
|
|
790
|
-
['
|
|
791
|
-
['local.get', `$${
|
|
792
|
-
|
|
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')
|
|
793
797
|
}
|
|
794
798
|
const nGroups = ctx.runtime.regex.groups.get(id) || 0
|
|
795
799
|
const s = temp('sm'), ms = tempI32('smms'), me = tempI32('smme')
|
|
796
800
|
return typed(['block', ['result', 'f64'],
|
|
797
801
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
798
802
|
['local.set', `$${ms}`, ['local.set', `$${me}`,
|
|
799
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
803
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
800
804
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${ms}`], ['i32.const', 0]],
|
|
801
805
|
['then', ['f64.const', 0]],
|
|
802
806
|
['else', buildMatchArr(s, ms, me, nGroups)]]], 'f64')
|
|
@@ -808,7 +812,7 @@ export default (ctx) => {
|
|
|
808
812
|
if (id == null) {
|
|
809
813
|
// Fall back to string replace
|
|
810
814
|
inc('__str_replace')
|
|
811
|
-
return typed(['call', '$__str_replace',
|
|
815
|
+
return typed(['call', '$__str_replace', asI64(emit(str)), asI64(emit(search)), asI64(emit(repl))], 'f64')
|
|
812
816
|
}
|
|
813
817
|
inc('__str_slice', '__str_concat', '__str_byteLen')
|
|
814
818
|
const s = temp('sr'), r = temp('srr'), ms = tempI32('srms'), me = tempI32('srme')
|
|
@@ -816,16 +820,16 @@ export default (ctx) => {
|
|
|
816
820
|
['local.set', `$${s}`, asF64(emit(str))],
|
|
817
821
|
['local.set', `$${r}`, asF64(emit(repl))],
|
|
818
822
|
['local.set', `$${ms}`, ['local.set', `$${me}`,
|
|
819
|
-
['call', `$__regex_search_${id}`, ['local.get', `$${s}`]]]],
|
|
823
|
+
['call', `$__regex_search_${id}`, ['i64.reinterpret_f64', ['local.get', `$${s}`]]]]],
|
|
820
824
|
['if', ['result', 'f64'], ['i32.lt_s', ['local.get', `$${ms}`], ['i32.const', 0]],
|
|
821
825
|
['then', ['local.get', `$${s}`]],
|
|
822
826
|
['else',
|
|
823
827
|
['call', '$__str_concat',
|
|
824
|
-
['call', '$__str_concat',
|
|
825
|
-
['call', '$__str_slice', ['local.get', `$${s}`], ['i32.const', 0], ['local.get', `$${ms}`]],
|
|
826
|
-
['local.get', `$${r}`]],
|
|
827
|
-
['call', '$__str_slice', ['local.get', `$${s}`], ['local.get', `$${me}`],
|
|
828
|
-
['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')
|
|
829
833
|
}
|
|
830
834
|
|
|
831
835
|
// str.split(/re/) → array of substrings
|
|
@@ -834,14 +838,14 @@ export default (ctx) => {
|
|
|
834
838
|
if (id == null) {
|
|
835
839
|
// Fall back to string split
|
|
836
840
|
inc('__str_split')
|
|
837
|
-
return typed(['call', '$__str_split',
|
|
841
|
+
return typed(['call', '$__str_split', asI64(emit(str)), asI64(emit(sep))], 'f64')
|
|
838
842
|
}
|
|
839
843
|
|
|
840
844
|
// Generate a split-by-regex WAT function for this regex
|
|
841
845
|
const splitName = `__regex_split_${id}`
|
|
842
846
|
if (!ctx.core.stdlib[splitName]) {
|
|
843
847
|
inc('__str_to_buf', '__str_slice', '__alloc')
|
|
844
|
-
ctx.core.stdlib[splitName] = `(func $${splitName} (param $str
|
|
848
|
+
ctx.core.stdlib[splitName] = `(func $${splitName} (param $str i64) (result f64)
|
|
845
849
|
(local $off i32) (local $len i32) (local $pos i32) (local $result i32)
|
|
846
850
|
(local $mstart i32) (local $mend i32) (local $prevEnd i32)
|
|
847
851
|
(local $arrOff i32) (local $count i32) (local $cap i32)
|
|
@@ -908,6 +912,6 @@ export default (ctx) => {
|
|
|
908
912
|
inc(splitName)
|
|
909
913
|
}
|
|
910
914
|
|
|
911
|
-
return typed(['call', `$${splitName}`,
|
|
915
|
+
return typed(['call', `$${splitName}`, asI64(emit(str))], 'f64')
|
|
912
916
|
}
|
|
913
917
|
}
|