watr 2.0.0 → 2.1.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/package.json +1 -1
- package/src/compile.js +71 -53
package/package.json
CHANGED
package/src/compile.js
CHANGED
|
@@ -46,7 +46,7 @@ export default (nodes) => {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// code must be compiled after all definitions
|
|
49
|
-
for (let cb of postcall) cb
|
|
49
|
+
for (let cb of postcall) cb?.()
|
|
50
50
|
|
|
51
51
|
// 3. build binary
|
|
52
52
|
for (let name in sections) {
|
|
@@ -66,31 +66,10 @@ const build = {
|
|
|
66
66
|
// (type $name? (func (param $x i32) (param i64 i32) (result i32 i64)))
|
|
67
67
|
// signature part is identical to function
|
|
68
68
|
// FIXME: handle non-function types
|
|
69
|
-
type([, typeName,
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (kind === 'func') {
|
|
74
|
-
// collect params
|
|
75
|
-
while (sig[0]?.[0] === 'param') {
|
|
76
|
-
let [, ...types] = sig.shift()
|
|
77
|
-
if (types[0]?.[0] === '$') params[types.shift()] = params.length
|
|
78
|
-
params.push(...types.map(t => TYPE[t]))
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// collect result type
|
|
82
|
-
if (sig[0]?.[0] === 'result') result = sig.shift().slice(1).map(t => TYPE[t])
|
|
83
|
-
|
|
84
|
-
// reuse existing type or register new one
|
|
85
|
-
bytes = [TYPE.func, ...uleb(params.length), ...params, ...uleb(result.length), ...result]
|
|
86
|
-
|
|
87
|
-
idx = ctx.type.findIndex((prevType) => prevType.every((byte, i) => byte === bytes[i]))
|
|
88
|
-
if (idx < 0) idx = ctx.type.push(bytes) - 1
|
|
89
|
-
}
|
|
90
|
-
|
|
69
|
+
type([, typeName, [kind, ...sig]], ctx) {
|
|
70
|
+
if (kind !== 'func') err(`Unknown type kind '${kind}'`)
|
|
71
|
+
const [idx] = consumeType(sig, ctx)
|
|
91
72
|
if (typeName) ctx.type[typeName] = idx
|
|
92
|
-
|
|
93
|
-
return [idx, params, result]
|
|
94
73
|
},
|
|
95
74
|
|
|
96
75
|
// (func $name? ...params result ...body)
|
|
@@ -104,10 +83,10 @@ const build = {
|
|
|
104
83
|
// export binding
|
|
105
84
|
if (body[0]?.[0] === 'export') build.export([...body.shift(), ['func', ctx.func.length]], ctx)
|
|
106
85
|
|
|
107
|
-
// register type
|
|
108
|
-
let [typeIdx, params, result] =
|
|
109
|
-
|
|
110
|
-
|
|
86
|
+
// register/consume type info
|
|
87
|
+
let [typeIdx, params, result] = consumeType(body, ctx)
|
|
88
|
+
|
|
89
|
+
// register new function
|
|
111
90
|
ctx.func.push([typeIdx])
|
|
112
91
|
|
|
113
92
|
// collect locals
|
|
@@ -182,40 +161,54 @@ const build = {
|
|
|
182
161
|
immed = [0]
|
|
183
162
|
}
|
|
184
163
|
|
|
185
|
-
// (if
|
|
186
|
-
else if (opCode
|
|
164
|
+
// (if ...), (block ...), (loop ...)
|
|
165
|
+
else if (opCode > 1 && opCode < 5) {
|
|
187
166
|
blocks.push(opCode)
|
|
188
|
-
let [, type] = args[0][0] === 'result' ? args.shift() : [, 'void']
|
|
189
|
-
immed = [TYPE[type]] // FIXME: what if that's multiple values return?
|
|
190
167
|
|
|
191
|
-
// (
|
|
168
|
+
// (block $x) (loop $y)
|
|
169
|
+
if (opCode < 4 && args[0]?.[0] === '$') (blocks[args.shift()] = blocks.length)
|
|
170
|
+
|
|
171
|
+
// get type
|
|
172
|
+
// (result i32)
|
|
173
|
+
if (args[0]?.[0] === 'result') {
|
|
174
|
+
if (args[0].length < 3) {
|
|
175
|
+
let [, type] = args.shift()
|
|
176
|
+
immed = [TYPE[type]]
|
|
177
|
+
}
|
|
178
|
+
// (result i32 i32)
|
|
179
|
+
else {
|
|
180
|
+
let [typeId] = consumeType(args, ctx)
|
|
181
|
+
immed = [typeId]
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else immed = [TYPE.void]
|
|
185
|
+
|
|
192
186
|
if (group) {
|
|
193
187
|
nodes.unshift('end')
|
|
194
|
-
|
|
195
|
-
|
|
188
|
+
|
|
189
|
+
// (block xxx) -> block xxx end
|
|
190
|
+
if (opCode < 4) while (args.length) nodes.unshift(args.pop())
|
|
191
|
+
|
|
192
|
+
// (if cond a) -> cond if a end
|
|
193
|
+
else if (args.length < 3) nodes.unshift(args.pop())
|
|
194
|
+
// (if cond (then a) (else b)) -> `cond if a else b end`
|
|
195
|
+
else {
|
|
196
|
+
nodes.unshift(args.pop())
|
|
197
|
+
// (if cond a b) -> (if cond a else b)
|
|
198
|
+
if (nodes[0][0] !== 'else') nodes.unshift('else')
|
|
199
|
+
// (if a b (else)) -> (if a b)
|
|
200
|
+
else if (nodes[0].length < 2) nodes.shift()
|
|
201
|
+
nodes.unshift(args.pop())
|
|
202
|
+
}
|
|
196
203
|
}
|
|
197
204
|
}
|
|
205
|
+
|
|
198
206
|
// (else)
|
|
199
207
|
else if (opCode === 5) {
|
|
200
|
-
// ignore empty else
|
|
201
|
-
if (!args.length && nodes[0] === 'end') return
|
|
202
208
|
// (else xxx) -> else xxx
|
|
203
209
|
if (group) while (args.length) nodes.unshift(args.pop())
|
|
204
210
|
}
|
|
205
211
|
|
|
206
|
-
// (block ...), (loop ...)
|
|
207
|
-
else if (opCode == 2 || opCode == 3) {
|
|
208
|
-
blocks.push(opCode)
|
|
209
|
-
if (args[0]?.[0] === '$') (blocks[args.shift()] = blocks.length)
|
|
210
|
-
let [, type] = args[0]?.[0] === 'result' ? args.shift() : [, 'void']
|
|
211
|
-
immed = [TYPE[type]]
|
|
212
|
-
// (block xxx) -> block xxx end
|
|
213
|
-
if (group) {
|
|
214
|
-
nodes.unshift('end')
|
|
215
|
-
while (args.length) nodes.unshift(args.pop())
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
212
|
// (end)
|
|
220
213
|
else if (opCode == 0x0b) blocks.pop()
|
|
221
214
|
|
|
@@ -304,7 +297,7 @@ const build = {
|
|
|
304
297
|
if (kind === 'func') {
|
|
305
298
|
// we track imported funcs in func section to share namespace, and skip them on final build
|
|
306
299
|
if (name) ctx.func[name] = ctx.func.length
|
|
307
|
-
let [typeIdx] =
|
|
300
|
+
let [typeIdx] = consumeType(parts, ctx)
|
|
308
301
|
ctx.func.push(details = uleb(typeIdx))
|
|
309
302
|
ctx.func.importc = (ctx.func.importc || 0) + 1
|
|
310
303
|
}
|
|
@@ -358,8 +351,33 @@ const str = str => {
|
|
|
358
351
|
return res
|
|
359
352
|
}
|
|
360
353
|
|
|
361
|
-
|
|
362
354
|
// build range/limits sequence (non-consuming)
|
|
363
355
|
const range = ([min, max, shared]) => isNaN(parseInt(max)) ? [0, ...uleb(min)] : [shared === 'shared' ? 3 : 1, ...uleb(min), ...uleb(max)]
|
|
364
356
|
|
|
357
|
+
// get type info from (params) (result) nodes sequence (consumes nodes)
|
|
358
|
+
// returns registered (reused) type idx, params bytes, result bytes
|
|
359
|
+
// eg. (type $return_i32 (func (result i32)))
|
|
360
|
+
const consumeType = (nodes, ctx) => {
|
|
361
|
+
let params = [], result = [], idx, bytes
|
|
362
|
+
|
|
363
|
+
// collect params
|
|
364
|
+
while (nodes[0]?.[0] === 'param') {
|
|
365
|
+
let [, ...types] = nodes.shift()
|
|
366
|
+
if (types[0]?.[0] === '$') params[types.shift()] = params.length
|
|
367
|
+
params.push(...types.map(t => TYPE[t]))
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// collect result type
|
|
371
|
+
if (nodes[0]?.[0] === 'result') result = nodes.shift().slice(1).map(t => TYPE[t])
|
|
372
|
+
|
|
373
|
+
// reuse existing type or register new one
|
|
374
|
+
bytes = [TYPE.func, ...uleb(params.length), ...params, ...uleb(result.length), ...result]
|
|
375
|
+
idx = ctx.type.findIndex((t) => t.every((byte, i) => byte === bytes[i]))
|
|
376
|
+
|
|
377
|
+
// register new type, if not found
|
|
378
|
+
if (idx < 0) idx = ctx.type.push(bytes) - 1
|
|
379
|
+
|
|
380
|
+
return [idx, params, result]
|
|
381
|
+
}
|
|
382
|
+
|
|
365
383
|
const err = text => { throw Error(text) }
|