diff --git a/asm.js b/asm.js index 2c277fb..9297868 100644 --- a/asm.js +++ b/asm.js @@ -97,6 +97,9 @@ const State = Object.freeze({ DEF_VALUE: 25, BLOCK_NAME: 26, BLOCK_TYPE: 27, + TYPE_NAME: 28, + TYPE_PARAM: 29, + TYPE_RESULT: 30, }); const Action = Object.freeze({ @@ -118,12 +121,14 @@ const Action = Object.freeze({ ENTER: 16, EXIT: 17, ELSE: 18, + TYPE: 19, }); const types = { - "void": 0x40, - "func": 0x60, - "i32": 0x7f, + "void": 0x40, + "func": 0x60, + "funcref": 0x70, + "i32": 0x7f, }; const opcodes = { @@ -208,6 +213,7 @@ class Parser { ".utf8": State.UTF8, ".align": State.ALIGN, ".def": State.DEF_NAME, + ".type": State.TYPE_NAME, }; this.blocks = new Set(["block", "loop", "if"]); this.handlers = { @@ -239,6 +245,9 @@ class Parser { [State.DEF_VALUE]: (token) => this.token_def_value(token), [State.BLOCK_NAME]: (token) => this.token_block_name(token), [State.BLOCK_TYPE]: (token) => this.token_block_type(token), + [State.TYPE_NAME]: (token) => this.token_type_name(token), + [State.TYPE_PARAM]: (token) => this.token_type_param(token), + [State.TYPE_RESULT]: (token) => this.token_type_result(token), }; this.results = []; @@ -669,6 +678,68 @@ class Parser { return action; } + token_type_name(token) { + if (token == LINE_END) { + console.error( + "ERROR: Unexpected end of line in .type, expected name"); + this.state = State.TOP; + return; + } + + this.type = { name: token, params: [] }; + this.state = State.TYPE_PARAM; + } + + token_type_param(token) { + if (token == LINE_END) { + console.error( + "ERROR: Unexpected end of line in .type, expected " + + "parameter type"); + this.type = undefined; + this.state = State.TOP; + return; + } + + if (token == "result") { + this.type.results = []; + this.state = State.TYPE_RESULT; + return; + } + + const type = types[token]; + if (type == undefined) { + console.error( + `ERROR: Unexpected token ${token} in .type, expected ` + + "parameter type"); + this.type = undefined; + this.state = State.TOP; + return; + } + + this.type.params.push(type); + } + + token_type_result(token) { + if (token == LINE_END) { + const action = { type: Action.TYPE, the_type: this.type }; + this.type = undefined; + this.state = State.TOP; + return action; + } + + const type = types[token]; + if (type == undefined) { + console.error( + `ERROR: Unexpected token ${token} in .type, expected ` + + "result type"); + this.type = undefined; + this.state = State.TOP; + return; + } + + this.type.results.push(type); + } + mem_action() { const action = { type: Action.MEM, @@ -730,6 +801,7 @@ export class Assembler { [Action.ENTER]: (action) => this.action_enter(action), [Action.EXIT]: (action) => this.action_exit(action), [Action.ELSE]: (action) => this.action_else(action), + [Action.TYPE]: (action) => this.action_type(action), }; this.exports = []; @@ -742,6 +814,7 @@ export class Assembler { this.defs = {}; this.blocks = []; this.types = []; + this.type_bindings = {}; } action_append(action) { @@ -874,6 +947,12 @@ export class Assembler { this.blocks.push(undefined); } + action_type(action) { + const type = this.func_type(action.the_type); + const index = this.ensure_type(type); + this.type_bindings[action.the_type.name] = index; + } + push(chunk) { const text = this.decoder.decode(chunk, { stream: true }); for (const action of this.parser.handle(text)) @@ -1066,7 +1145,9 @@ export class Assembler { } func_type({ params, results }) { - const param_types = Object.values(params); + const param_types = params.length == undefined + ? Object.values(params) + : params; return [ types["func"], param_types.length,