Implement .table and .elem
This commit is contained in:
145
asm.js
145
asm.js
@@ -100,6 +100,10 @@ const State = Object.freeze({
|
||||
TYPE_NAME: 28,
|
||||
TYPE_PARAM: 29,
|
||||
TYPE_RESULT: 30,
|
||||
TABLE_NAME: 31,
|
||||
TABLE_SIZE: 32,
|
||||
ELEM_TABLE: 33,
|
||||
ELEM_ELEM: 34,
|
||||
});
|
||||
|
||||
const Action = Object.freeze({
|
||||
@@ -122,6 +126,8 @@ const Action = Object.freeze({
|
||||
EXIT: 17,
|
||||
ELSE: 18,
|
||||
TYPE: 19,
|
||||
TABLE: 20,
|
||||
ELEM: 21,
|
||||
});
|
||||
|
||||
const types = {
|
||||
@@ -214,6 +220,8 @@ class Parser {
|
||||
".align": State.ALIGN,
|
||||
".def": State.DEF_NAME,
|
||||
".type": State.TYPE_NAME,
|
||||
".table": State.TABLE_NAME,
|
||||
".elem": State.ELEM_TABLE,
|
||||
};
|
||||
this.blocks = new Set(["block", "loop", "if"]);
|
||||
this.handlers = {
|
||||
@@ -248,6 +256,10 @@ class Parser {
|
||||
[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),
|
||||
[State.TABLE_NAME]: (token) => this.token_table_name(token),
|
||||
[State.TABLE_SIZE]: (token) => this.token_table_size(token),
|
||||
[State.ELEM_TABLE]: (token) => this.token_elem_table(token),
|
||||
[State.ELEM_ELEM]: (token) => this.token_elem_elem(token),
|
||||
};
|
||||
|
||||
this.results = [];
|
||||
@@ -740,6 +752,73 @@ class Parser {
|
||||
this.type.results.push(type);
|
||||
}
|
||||
|
||||
token_table_name(token) {
|
||||
if (token == LINE_END) {
|
||||
console.error(
|
||||
"ERROR: Unexpected end of line in .table, expected name");
|
||||
this.state = State.TOP;
|
||||
return;
|
||||
}
|
||||
|
||||
this.table = { name: token };
|
||||
this.state = State.TABLE_SIZE;
|
||||
}
|
||||
|
||||
token_table_size(token) {
|
||||
if (token == LINE_END) {
|
||||
console.error(
|
||||
"ERROR: Unexpected end of line in .table, expected size");
|
||||
this.table = undefined;
|
||||
this.state = State.TOP;
|
||||
return;
|
||||
}
|
||||
|
||||
const size = this.integer(token);
|
||||
if (size == null) {
|
||||
console.error(
|
||||
`ERROR: Unexpected token ${token} in .table, expected size`);
|
||||
this.table = undefined;
|
||||
this.state = State.TOP;
|
||||
return;
|
||||
}
|
||||
|
||||
this.table.size = size;
|
||||
const action = { type: Action.TABLE, table: this.table };
|
||||
this.table = undefined;
|
||||
this.state = State.TOP;
|
||||
return action;
|
||||
}
|
||||
|
||||
token_elem_table(token) {
|
||||
if (token == LINE_END) {
|
||||
console.error(
|
||||
"ERROR: Unexpected end of line in .elem, expected "
|
||||
+ "table name");
|
||||
this.state = State.TOP;
|
||||
return;
|
||||
}
|
||||
|
||||
this.elem = { table: token };
|
||||
this.state = State.ELEM_ELEM;
|
||||
}
|
||||
|
||||
token_elem_elem(token) {
|
||||
if (token == LINE_END) {
|
||||
console.error(
|
||||
"ERROR: Unexpected end of line in .elem, expected "
|
||||
+ "element name");
|
||||
this.state = State.TOP;
|
||||
this.elem = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
this.elem.elem = token;
|
||||
const action = { type: Action.ELEM, elem: this.elem };
|
||||
this.elem = undefined
|
||||
this.state = State.TOP;
|
||||
return action;
|
||||
}
|
||||
|
||||
mem_action() {
|
||||
const action = {
|
||||
type: Action.MEM,
|
||||
@@ -771,9 +850,11 @@ const Section = Object.freeze({
|
||||
TYPE: 0x01,
|
||||
IMPORT: 0x02,
|
||||
FUNC: 0x03,
|
||||
TABLE: 0x04,
|
||||
MEM: 0x05,
|
||||
GLOBAL: 0x06,
|
||||
EXPORT: 0x07,
|
||||
ELEM: 0x09,
|
||||
CODE: 0x0a,
|
||||
DATA: 0x0b,
|
||||
});
|
||||
@@ -808,6 +889,8 @@ export class Assembler {
|
||||
[Action.EXIT]: (action) => this.action_exit(action),
|
||||
[Action.ELSE]: (action) => this.action_else(action),
|
||||
[Action.TYPE]: (action) => this.action_type(action),
|
||||
[Action.TABLE]: (action) => this.action_table(action),
|
||||
[Action.ELEM]: (action) => this.action_elem(action),
|
||||
};
|
||||
|
||||
this.exports = [];
|
||||
@@ -821,6 +904,7 @@ export class Assembler {
|
||||
this.blocks = [];
|
||||
this.types = [];
|
||||
this.type_bindings = {};
|
||||
this.tables = {};
|
||||
}
|
||||
|
||||
action_append(action) {
|
||||
@@ -959,6 +1043,23 @@ export class Assembler {
|
||||
this.type_bindings[action.the_type.name] = index;
|
||||
}
|
||||
|
||||
action_table(action) {
|
||||
this.tables[action.table.name] = {
|
||||
size: action.table.size,
|
||||
elems: [],
|
||||
};
|
||||
}
|
||||
|
||||
action_elem(action) {
|
||||
const table = this.tables[action.elem.table];
|
||||
const index = Object.keys(this.funcs).indexOf(action.elem.elem);
|
||||
if (index == -1) {
|
||||
console.error(`ERROR: ${action.elem.elem}: no such function`);
|
||||
return;
|
||||
}
|
||||
table.elems.push(index);
|
||||
}
|
||||
|
||||
push(chunk) {
|
||||
const text = this.decoder.decode(chunk, { stream: true });
|
||||
for (const action of this.parser.handle(text)) {
|
||||
@@ -1009,7 +1110,6 @@ export class Assembler {
|
||||
}
|
||||
|
||||
leb128(x) {
|
||||
const orig = x;
|
||||
const bytes = [];
|
||||
while (true) {
|
||||
const b = x & 0x7f;
|
||||
@@ -1022,6 +1122,18 @@ export class Assembler {
|
||||
}
|
||||
}
|
||||
|
||||
uleb128(x) {
|
||||
const bytes = [];
|
||||
do {
|
||||
const b = x & 0x7f;
|
||||
x >>= 7;
|
||||
if (x != 0)
|
||||
b |= 0x80;
|
||||
bytes.push(b);
|
||||
} while (x != 0);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
wasm_section_type() {
|
||||
if (this.types.length == 0) return null;
|
||||
return [ this.types.length ].concat(...this.types);
|
||||
@@ -1052,6 +1164,17 @@ export class Assembler {
|
||||
return [ count, ...types ];
|
||||
}
|
||||
|
||||
wasm_section_table() {
|
||||
const sizes = Object.values(this.tables).map(({size}) => size);
|
||||
if (sizes.length == 0) return null;
|
||||
const contents = sizes.map((size) => [
|
||||
types.funcref,
|
||||
0x00,
|
||||
this.uleb128(size),
|
||||
]);
|
||||
return [ sizes.length, contents ].flat(Infinity)
|
||||
}
|
||||
|
||||
wasm_section_mem() {
|
||||
const mems = Object.values(this.mems).filter(
|
||||
({imported}) => !imported);
|
||||
@@ -1085,6 +1208,24 @@ export class Assembler {
|
||||
return [ exports.length ].concat(...contents);
|
||||
}
|
||||
|
||||
wasm_section_elem() {
|
||||
const table_elems = Object.values(this.tables).map(
|
||||
({elems}) => elems);
|
||||
const count = table_elems.flat().length;
|
||||
if (count == 0) return null;
|
||||
const contents = table_elems.flatMap((elems, table) =>
|
||||
elems.flatMap((fn, index) => [
|
||||
table == 0 ? 0 : [ 2, table ],
|
||||
opcodes["i32.const"],
|
||||
index,
|
||||
opcodes["end"],
|
||||
1,
|
||||
fn,
|
||||
])
|
||||
);
|
||||
return [ count, contents ].flat();
|
||||
}
|
||||
|
||||
wasm_section_code() {
|
||||
const funcs = Object.values(this.funcs);
|
||||
if (funcs.length == 0) return null;
|
||||
@@ -1134,9 +1275,11 @@ export class Assembler {
|
||||
[ Section.TYPE, () => this.wasm_section_type() ],
|
||||
[ Section.IMPORT, () => this.wasm_section_import() ],
|
||||
[ Section.FUNC, () => this.wasm_section_func() ],
|
||||
[ Section.TABLE, () => this.wasm_section_table() ],
|
||||
[ Section.MEM, () => this.wasm_section_mem() ],
|
||||
[ Section.GLOBAL, () => this.wasm_section_global() ],
|
||||
[ Section.EXPORT, () => this.wasm_section_export() ],
|
||||
[ Section.ELEM, () => this.wasm_section_elem() ],
|
||||
[ Section.CODE, () => this.wasm_section_code() ],
|
||||
[ Section.DATA, () => this.wasm_section_data() ],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user