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_NAME: 28,
|
||||||
TYPE_PARAM: 29,
|
TYPE_PARAM: 29,
|
||||||
TYPE_RESULT: 30,
|
TYPE_RESULT: 30,
|
||||||
|
TABLE_NAME: 31,
|
||||||
|
TABLE_SIZE: 32,
|
||||||
|
ELEM_TABLE: 33,
|
||||||
|
ELEM_ELEM: 34,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Action = Object.freeze({
|
const Action = Object.freeze({
|
||||||
@@ -122,6 +126,8 @@ const Action = Object.freeze({
|
|||||||
EXIT: 17,
|
EXIT: 17,
|
||||||
ELSE: 18,
|
ELSE: 18,
|
||||||
TYPE: 19,
|
TYPE: 19,
|
||||||
|
TABLE: 20,
|
||||||
|
ELEM: 21,
|
||||||
});
|
});
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
@@ -214,6 +220,8 @@ class Parser {
|
|||||||
".align": State.ALIGN,
|
".align": State.ALIGN,
|
||||||
".def": State.DEF_NAME,
|
".def": State.DEF_NAME,
|
||||||
".type": State.TYPE_NAME,
|
".type": State.TYPE_NAME,
|
||||||
|
".table": State.TABLE_NAME,
|
||||||
|
".elem": State.ELEM_TABLE,
|
||||||
};
|
};
|
||||||
this.blocks = new Set(["block", "loop", "if"]);
|
this.blocks = new Set(["block", "loop", "if"]);
|
||||||
this.handlers = {
|
this.handlers = {
|
||||||
@@ -248,6 +256,10 @@ class Parser {
|
|||||||
[State.TYPE_NAME]: (token) => this.token_type_name(token),
|
[State.TYPE_NAME]: (token) => this.token_type_name(token),
|
||||||
[State.TYPE_PARAM]: (token) => this.token_type_param(token),
|
[State.TYPE_PARAM]: (token) => this.token_type_param(token),
|
||||||
[State.TYPE_RESULT]: (token) => this.token_type_result(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 = [];
|
this.results = [];
|
||||||
@@ -740,6 +752,73 @@ class Parser {
|
|||||||
this.type.results.push(type);
|
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() {
|
mem_action() {
|
||||||
const action = {
|
const action = {
|
||||||
type: Action.MEM,
|
type: Action.MEM,
|
||||||
@@ -771,9 +850,11 @@ const Section = Object.freeze({
|
|||||||
TYPE: 0x01,
|
TYPE: 0x01,
|
||||||
IMPORT: 0x02,
|
IMPORT: 0x02,
|
||||||
FUNC: 0x03,
|
FUNC: 0x03,
|
||||||
|
TABLE: 0x04,
|
||||||
MEM: 0x05,
|
MEM: 0x05,
|
||||||
GLOBAL: 0x06,
|
GLOBAL: 0x06,
|
||||||
EXPORT: 0x07,
|
EXPORT: 0x07,
|
||||||
|
ELEM: 0x09,
|
||||||
CODE: 0x0a,
|
CODE: 0x0a,
|
||||||
DATA: 0x0b,
|
DATA: 0x0b,
|
||||||
});
|
});
|
||||||
@@ -808,6 +889,8 @@ export class Assembler {
|
|||||||
[Action.EXIT]: (action) => this.action_exit(action),
|
[Action.EXIT]: (action) => this.action_exit(action),
|
||||||
[Action.ELSE]: (action) => this.action_else(action),
|
[Action.ELSE]: (action) => this.action_else(action),
|
||||||
[Action.TYPE]: (action) => this.action_type(action),
|
[Action.TYPE]: (action) => this.action_type(action),
|
||||||
|
[Action.TABLE]: (action) => this.action_table(action),
|
||||||
|
[Action.ELEM]: (action) => this.action_elem(action),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.exports = [];
|
this.exports = [];
|
||||||
@@ -821,6 +904,7 @@ export class Assembler {
|
|||||||
this.blocks = [];
|
this.blocks = [];
|
||||||
this.types = [];
|
this.types = [];
|
||||||
this.type_bindings = {};
|
this.type_bindings = {};
|
||||||
|
this.tables = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
action_append(action) {
|
action_append(action) {
|
||||||
@@ -959,6 +1043,23 @@ export class Assembler {
|
|||||||
this.type_bindings[action.the_type.name] = index;
|
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) {
|
push(chunk) {
|
||||||
const text = this.decoder.decode(chunk, { stream: true });
|
const text = this.decoder.decode(chunk, { stream: true });
|
||||||
for (const action of this.parser.handle(text)) {
|
for (const action of this.parser.handle(text)) {
|
||||||
@@ -1009,7 +1110,6 @@ export class Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
leb128(x) {
|
leb128(x) {
|
||||||
const orig = x;
|
|
||||||
const bytes = [];
|
const bytes = [];
|
||||||
while (true) {
|
while (true) {
|
||||||
const b = x & 0x7f;
|
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() {
|
wasm_section_type() {
|
||||||
if (this.types.length == 0) return null;
|
if (this.types.length == 0) return null;
|
||||||
return [ this.types.length ].concat(...this.types);
|
return [ this.types.length ].concat(...this.types);
|
||||||
@@ -1052,6 +1164,17 @@ export class Assembler {
|
|||||||
return [ count, ...types ];
|
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() {
|
wasm_section_mem() {
|
||||||
const mems = Object.values(this.mems).filter(
|
const mems = Object.values(this.mems).filter(
|
||||||
({imported}) => !imported);
|
({imported}) => !imported);
|
||||||
@@ -1085,6 +1208,24 @@ export class Assembler {
|
|||||||
return [ exports.length ].concat(...contents);
|
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() {
|
wasm_section_code() {
|
||||||
const funcs = Object.values(this.funcs);
|
const funcs = Object.values(this.funcs);
|
||||||
if (funcs.length == 0) return null;
|
if (funcs.length == 0) return null;
|
||||||
@@ -1134,9 +1275,11 @@ export class Assembler {
|
|||||||
[ Section.TYPE, () => this.wasm_section_type() ],
|
[ Section.TYPE, () => this.wasm_section_type() ],
|
||||||
[ Section.IMPORT, () => this.wasm_section_import() ],
|
[ Section.IMPORT, () => this.wasm_section_import() ],
|
||||||
[ Section.FUNC, () => this.wasm_section_func() ],
|
[ Section.FUNC, () => this.wasm_section_func() ],
|
||||||
|
[ Section.TABLE, () => this.wasm_section_table() ],
|
||||||
[ Section.MEM, () => this.wasm_section_mem() ],
|
[ Section.MEM, () => this.wasm_section_mem() ],
|
||||||
[ Section.GLOBAL, () => this.wasm_section_global() ],
|
[ Section.GLOBAL, () => this.wasm_section_global() ],
|
||||||
[ Section.EXPORT, () => this.wasm_section_export() ],
|
[ Section.EXPORT, () => this.wasm_section_export() ],
|
||||||
|
[ Section.ELEM, () => this.wasm_section_elem() ],
|
||||||
[ Section.CODE, () => this.wasm_section_code() ],
|
[ Section.CODE, () => this.wasm_section_code() ],
|
||||||
[ Section.DATA, () => this.wasm_section_data() ],
|
[ Section.DATA, () => this.wasm_section_data() ],
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user