commit 796bb740fdbdc05fc863d42e13c42f706a9ba46b
parent 528833572f1915d74cc42c5bc2ffdf5e1a54f97b
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sun, 17 May 2026 21:42:42 -0700
Complete Wasm frontend reader validation
Diffstat:
14 files changed, 1669 insertions(+), 219 deletions(-)
diff --git a/doc/WASM.md b/doc/WASM.md
@@ -757,12 +757,19 @@ This checklist tracks the path from the initial Wasm/WAT frontend subset to a
complete implementation. Keep each item tied to a small named fixture or
targeted test target.
+Current checked reader/encoder and validation items describe the
+`lang/wasm` frontend implementation: it can parse, validate, preserve, and
+re-encode the listed module metadata. Metadata that is not lowered yet
+(`import`, `table`, `global`, `elem`, and `start`) is accepted by the
+reader/encoder and rejected before native CG emission with explicit
+diagnostics.
+
### Frontend Source and Driver
- [x] Add `CFREE_LANG_WASM` and suffix inference for `.wat`/`.wasm`.
- [x] Register the Wasm frontend in driver-created compilers and pipelines.
- [x] Add `make test-wasm-front` and a small WAT-to-Wasm test helper.
-- [ ] Add explicit negative frontend tests for malformed WAT, malformed Wasm,
+- [x] Add explicit negative frontend tests for malformed WAT, malformed Wasm,
bad indices, stack underflow, unsupported sections, and unsupported opcodes.
- [ ] Decide stdin language selection for WAT input instead of treating `-` as
C-only.
@@ -777,10 +784,10 @@ targeted test target.
- [x] Parse standard WAT string escapes and byte escapes.
- [x] Parse integer literals with signs, underscores, hex notation, and
boundary diagnostics.
-- [ ] Parse float literals for `f32.const` and `f64.const`.
-- [ ] Parse module-level type definitions and `(func (type N) ...)`.
+- [x] Parse float literals for `f32.const` and `f64.const`.
+- [x] Parse module-level type definitions and `(func (type N) ...)`.
- [x] Parse memories and active data segments for the frontend subset.
-- [ ] Parse imports, tables, globals, elements, start, and custom/name
+- [x] Parse imports, tables, globals, elements, start, and custom/name
sections.
- [ ] Preserve source locations through validation and lowering diagnostics.
@@ -790,14 +797,16 @@ targeted test target.
export, code, locals, constants, calls, local ops, and integer ops.
- [x] Reject `linking` custom sections as frontend input.
- [ ] Move shared binary mechanics into `src/wasm` with decode/encode contexts.
-- [ ] Validate section length, ordering, count, and index-space edge cases with
+- [x] Validate section length, ordering, count, and index-space edge cases with
direct format tests.
- [x] Decode and encode memories and active data segments for the frontend
subset.
-- [ ] Decode and encode imports, tables, globals, elements, start,
- custom/name sections, and target-feature metadata.
-- [ ] Preserve unknown custom sections when the caller requests preservation.
-- [ ] Add deterministic fixtures for malformed LEB128 and truncated bodies.
+- [x] Decode and encode imports, tables, globals, elements, start, and
+ custom/name sections; target-feature custom sections are preserved as raw
+ metadata.
+- [x] Preserve unknown custom sections in the frontend WAT-to-Wasm/binary
+ module path.
+- [x] Add deterministic fixtures for malformed LEB128 and truncated bodies.
### Validation
@@ -805,12 +814,12 @@ targeted test target.
integer-only locals/params for the current subset.
- [x] Replace depth-only validation with typed operand and control stacks for
the accepted frontend subset.
-- [ ] Validate exact function result stack shape, unreachable polymorphism, and
+- [x] Validate exact function result stack shape, unreachable polymorphism, and
fallthrough after `return`/`unreachable`.
- [x] Validate blocks, loops, if/else, and branch depths for no-result
control blocks.
-- [ ] Validate branch result arity and `br_table`.
-- [ ] Validate memory/table/global/data/element indices, limits, active/passive
+- [x] Validate branch result arity and `br_table`.
+- [x] Validate memory/table/global/data/element indices, limits, active/passive
segment rules, and start function signature.
- [ ] Centralize feature gating in a `WasmFeatureSet`.
- [ ] Add clear diagnostics for every deferred proposal: SIMD, threads,
diff --git a/lang/wasm/wasm.c b/lang/wasm/wasm.c
@@ -13,6 +13,8 @@ typedef enum WasmValType {
WASM_VAL_I64 = 0x7e,
WASM_VAL_F32 = 0x7d,
WASM_VAL_F64 = 0x7c,
+ WASM_VAL_FUNCREF = 0x70,
+ WASM_VAL_EXTERNREF = 0x6f,
} WasmValType;
typedef enum WasmInsnKind {
@@ -175,6 +177,11 @@ typedef struct WasmInsn {
typedef struct WasmFunc {
char *name;
+ uint32_t typeidx;
+ int has_typeidx;
+ int is_import;
+ char *import_module;
+ char *import_name;
WasmValType params[16];
uint32_t nparams;
WasmValType locals[32];
@@ -188,21 +195,97 @@ typedef struct WasmFunc {
uint32_t cap_insns;
} WasmFunc;
+typedef struct WasmFuncType {
+ char *name;
+ WasmValType params[16];
+ uint32_t nparams;
+ WasmValType results[1];
+ uint32_t nresults;
+} WasmFuncType;
+
typedef struct WasmMemory {
+ char *name;
uint32_t min_pages;
uint32_t max_pages;
int has_max;
+ int is_import;
+ char *import_module;
+ char *import_name;
+ char *export_name;
uint8_t *data;
uint32_t data_len;
} WasmMemory;
+typedef struct WasmTable {
+ char *name;
+ WasmValType elem_type;
+ uint32_t min;
+ uint32_t max;
+ int has_max;
+ int is_import;
+ char *import_module;
+ char *import_name;
+ char *export_name;
+} WasmTable;
+
+typedef struct WasmGlobal {
+ char *name;
+ WasmValType type;
+ uint8_t mutable_;
+ WasmInsn init;
+ int is_import;
+ char *import_module;
+ char *import_name;
+ char *export_name;
+} WasmGlobal;
+
+typedef struct WasmElemSegment {
+ uint32_t tableidx;
+ int64_t offset;
+ uint32_t funcs[64];
+ uint32_t nfuncs;
+} WasmElemSegment;
+
+typedef struct WasmExport {
+ char *name;
+ uint8_t kind;
+ uint32_t index;
+} WasmExport;
+
+typedef struct WasmCustom {
+ char *name;
+ uint8_t *data;
+ uint32_t len;
+} WasmCustom;
+
typedef struct WasmModule {
CfreeHeap *heap;
+ WasmFuncType *types;
+ uint32_t ntypes;
+ uint32_t cap_types;
WasmFunc *funcs;
uint32_t nfuncs;
uint32_t cap_funcs;
WasmMemory memory;
int has_memory;
+ WasmTable *tables;
+ uint32_t ntables;
+ uint32_t cap_tables;
+ WasmGlobal *globals;
+ uint32_t nglobals;
+ uint32_t cap_globals;
+ WasmElemSegment *elems;
+ uint32_t nelems;
+ uint32_t cap_elems;
+ WasmExport *exports;
+ uint32_t nexports;
+ uint32_t cap_exports;
+ WasmCustom *customs;
+ uint32_t ncustoms;
+ uint32_t cap_customs;
+ uint32_t start_func;
+ int has_start;
+ int has_target_features;
} WasmModule;
typedef struct WasmTok {
@@ -271,6 +354,13 @@ static char *wasm_strdup(CfreeHeap *h, const char *s, size_t len) {
return out;
}
+static void wasm_free_str(CfreeHeap *h, char **s) {
+ if (*s) {
+ h->free(h, *s, strlen(*s) + 1u);
+ *s = NULL;
+ }
+}
+
static void wasm_module_init(WasmModule *m, CfreeHeap *heap) {
memset(m, 0, sizeof *m);
m->heap = heap;
@@ -280,23 +370,58 @@ static void wasm_module_free(WasmModule *m) {
uint32_t i;
if (!m || !m->heap)
return;
+ for (i = 0; i < m->ntypes; ++i)
+ wasm_free_str(m->heap, &m->types[i].name);
+ if (m->types)
+ m->heap->free(m->heap, m->types, sizeof(*m->types) * m->cap_types);
for (i = 0; i < m->nfuncs; ++i) {
WasmFunc *f = &m->funcs[i];
- if (f->name)
- m->heap->free(m->heap, f->name, strlen(f->name) + 1u);
- if (f->export_name)
- m->heap->free(m->heap, f->export_name, strlen(f->export_name) + 1u);
+ wasm_free_str(m->heap, &f->name);
+ wasm_free_str(m->heap, &f->import_module);
+ wasm_free_str(m->heap, &f->import_name);
+ wasm_free_str(m->heap, &f->export_name);
for (uint32_t j = 0; j < f->nparams + f->nlocals; ++j)
- if (f->local_names[j])
- m->heap->free(m->heap, f->local_names[j],
- strlen(f->local_names[j]) + 1u);
+ wasm_free_str(m->heap, &f->local_names[j]);
if (f->insns)
m->heap->free(m->heap, f->insns, sizeof(*f->insns) * f->cap_insns);
}
if (m->funcs)
m->heap->free(m->heap, m->funcs, sizeof(*m->funcs) * m->cap_funcs);
+ wasm_free_str(m->heap, &m->memory.name);
+ wasm_free_str(m->heap, &m->memory.import_module);
+ wasm_free_str(m->heap, &m->memory.import_name);
+ wasm_free_str(m->heap, &m->memory.export_name);
if (m->memory.data)
m->heap->free(m->heap, m->memory.data, m->memory.data_len);
+ for (i = 0; i < m->ntables; ++i) {
+ wasm_free_str(m->heap, &m->tables[i].name);
+ wasm_free_str(m->heap, &m->tables[i].import_module);
+ wasm_free_str(m->heap, &m->tables[i].import_name);
+ wasm_free_str(m->heap, &m->tables[i].export_name);
+ }
+ if (m->tables)
+ m->heap->free(m->heap, m->tables, sizeof(*m->tables) * m->cap_tables);
+ for (i = 0; i < m->nglobals; ++i) {
+ wasm_free_str(m->heap, &m->globals[i].name);
+ wasm_free_str(m->heap, &m->globals[i].import_module);
+ wasm_free_str(m->heap, &m->globals[i].import_name);
+ wasm_free_str(m->heap, &m->globals[i].export_name);
+ }
+ if (m->globals)
+ m->heap->free(m->heap, m->globals, sizeof(*m->globals) * m->cap_globals);
+ if (m->elems)
+ m->heap->free(m->heap, m->elems, sizeof(*m->elems) * m->cap_elems);
+ for (i = 0; i < m->nexports; ++i)
+ wasm_free_str(m->heap, &m->exports[i].name);
+ if (m->exports)
+ m->heap->free(m->heap, m->exports, sizeof(*m->exports) * m->cap_exports);
+ for (i = 0; i < m->ncustoms; ++i) {
+ wasm_free_str(m->heap, &m->customs[i].name);
+ if (m->customs[i].data)
+ m->heap->free(m->heap, m->customs[i].data, m->customs[i].len);
+ }
+ if (m->customs)
+ m->heap->free(m->heap, m->customs, sizeof(*m->customs) * m->cap_customs);
memset(m, 0, sizeof *m);
}
@@ -340,6 +465,139 @@ static WasmFunc *wasm_add_func(CfreeCompiler *c, WasmModule *m) {
return f;
}
+static WasmFuncType *wasm_add_type(CfreeCompiler *c, WasmModule *m) {
+ WasmFuncType *t;
+ if (m->ntypes == m->cap_types) {
+ uint32_t new_cap = m->cap_types ? m->cap_types * 2u : 8u;
+ void *p = wasm_realloc(m->heap, m->types, sizeof(*m->types) * m->cap_types,
+ sizeof(*m->types) * new_cap);
+ if (!p)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->types = (WasmFuncType *)p;
+ memset(m->types + m->cap_types, 0,
+ sizeof(*m->types) * (new_cap - m->cap_types));
+ m->cap_types = new_cap;
+ }
+ t = &m->types[m->ntypes++];
+ memset(t, 0, sizeof *t);
+ return t;
+}
+
+static uint32_t wasm_intern_func_type(CfreeCompiler *c, WasmModule *m,
+ const WasmFunc *f) {
+ uint32_t i;
+ for (i = 0; i < m->ntypes; ++i) {
+ WasmFuncType *t = &m->types[i];
+ if (t->nparams == f->nparams && t->nresults == f->nresults &&
+ memcmp(t->params, f->params, sizeof(t->params[0]) * t->nparams) == 0 &&
+ memcmp(t->results, f->results,
+ sizeof(t->results[0]) * t->nresults) == 0)
+ return i;
+ }
+ {
+ WasmFuncType *t = wasm_add_type(c, m);
+ t->nparams = f->nparams;
+ memcpy(t->params, f->params, sizeof(t->params[0]) * f->nparams);
+ t->nresults = f->nresults;
+ memcpy(t->results, f->results, sizeof(t->results[0]) * f->nresults);
+ return m->ntypes - 1u;
+ }
+}
+
+static WasmTable *wasm_add_table(CfreeCompiler *c, WasmModule *m) {
+ WasmTable *t;
+ if (m->ntables == m->cap_tables) {
+ uint32_t new_cap = m->cap_tables ? m->cap_tables * 2u : 2u;
+ void *p = wasm_realloc(m->heap, m->tables,
+ sizeof(*m->tables) * m->cap_tables,
+ sizeof(*m->tables) * new_cap);
+ if (!p)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->tables = (WasmTable *)p;
+ memset(m->tables + m->cap_tables, 0,
+ sizeof(*m->tables) * (new_cap - m->cap_tables));
+ m->cap_tables = new_cap;
+ }
+ t = &m->tables[m->ntables++];
+ memset(t, 0, sizeof *t);
+ return t;
+}
+
+static WasmGlobal *wasm_add_global(CfreeCompiler *c, WasmModule *m) {
+ WasmGlobal *g;
+ if (m->nglobals == m->cap_globals) {
+ uint32_t new_cap = m->cap_globals ? m->cap_globals * 2u : 4u;
+ void *p = wasm_realloc(m->heap, m->globals,
+ sizeof(*m->globals) * m->cap_globals,
+ sizeof(*m->globals) * new_cap);
+ if (!p)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->globals = (WasmGlobal *)p;
+ memset(m->globals + m->cap_globals, 0,
+ sizeof(*m->globals) * (new_cap - m->cap_globals));
+ m->cap_globals = new_cap;
+ }
+ g = &m->globals[m->nglobals++];
+ memset(g, 0, sizeof *g);
+ return g;
+}
+
+static WasmElemSegment *wasm_add_elem(CfreeCompiler *c, WasmModule *m) {
+ WasmElemSegment *e;
+ if (m->nelems == m->cap_elems) {
+ uint32_t new_cap = m->cap_elems ? m->cap_elems * 2u : 4u;
+ void *p = wasm_realloc(m->heap, m->elems, sizeof(*m->elems) * m->cap_elems,
+ sizeof(*m->elems) * new_cap);
+ if (!p)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->elems = (WasmElemSegment *)p;
+ memset(m->elems + m->cap_elems, 0,
+ sizeof(*m->elems) * (new_cap - m->cap_elems));
+ m->cap_elems = new_cap;
+ }
+ e = &m->elems[m->nelems++];
+ memset(e, 0, sizeof *e);
+ return e;
+}
+
+static WasmExport *wasm_add_export(CfreeCompiler *c, WasmModule *m) {
+ WasmExport *e;
+ if (m->nexports == m->cap_exports) {
+ uint32_t new_cap = m->cap_exports ? m->cap_exports * 2u : 8u;
+ void *p = wasm_realloc(m->heap, m->exports,
+ sizeof(*m->exports) * m->cap_exports,
+ sizeof(*m->exports) * new_cap);
+ if (!p)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->exports = (WasmExport *)p;
+ memset(m->exports + m->cap_exports, 0,
+ sizeof(*m->exports) * (new_cap - m->cap_exports));
+ m->cap_exports = new_cap;
+ }
+ e = &m->exports[m->nexports++];
+ memset(e, 0, sizeof *e);
+ return e;
+}
+
+static WasmCustom *wasm_add_custom(CfreeCompiler *c, WasmModule *m) {
+ WasmCustom *cs;
+ if (m->ncustoms == m->cap_customs) {
+ uint32_t new_cap = m->cap_customs ? m->cap_customs * 2u : 4u;
+ void *p = wasm_realloc(m->heap, m->customs,
+ sizeof(*m->customs) * m->cap_customs,
+ sizeof(*m->customs) * new_cap);
+ if (!p)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ m->customs = (WasmCustom *)p;
+ memset(m->customs + m->cap_customs, 0,
+ sizeof(*m->customs) * (new_cap - m->cap_customs));
+ m->cap_customs = new_cap;
+ }
+ cs = &m->customs[m->ncustoms++];
+ memset(cs, 0, sizeof *cs);
+ return cs;
+}
+
static void wasm_func_add_insn(CfreeCompiler *c, WasmModule *m, WasmFunc *f,
WasmInsnKind kind, int64_t imm) {
if (f->ninsns == f->cap_insns) {
@@ -630,9 +888,34 @@ static int wat_val_type(WasmTok t, WasmValType *out) {
*out = WASM_VAL_F64;
return 1;
}
+ if (tok_is(t, "funcref")) {
+ *out = WASM_VAL_FUNCREF;
+ return 1;
+ }
+ if (tok_is(t, "externref")) {
+ *out = WASM_VAL_EXTERNREF;
+ return 1;
+ }
return 0;
}
+static int wasm_is_num_type(WasmValType vt) {
+ return vt == WASM_VAL_I32 || vt == WASM_VAL_I64 || vt == WASM_VAL_F32 ||
+ vt == WASM_VAL_F64;
+}
+
+static uint8_t wasm_export_kind_from_tok(WasmTok t) {
+ if (tok_is(t, "func"))
+ return 0;
+ if (tok_is(t, "table"))
+ return 1;
+ if (tok_is(t, "memory"))
+ return 2;
+ if (tok_is(t, "global"))
+ return 3;
+ return 0xffu;
+}
+
static void wat_skip_list(WatParser *p) {
uint32_t depth = 1;
while (depth && p->tok.kind != WT_EOF) {
@@ -1121,6 +1404,23 @@ static void wat_parse_func_index(WatParser *p, int64_t *out) {
"wasm wat: expected instruction immediate");
}
+static void wat_parse_type_index(WatParser *p, int64_t *out) {
+ uint32_t i;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ for (i = 0; i < p->module->ntypes; ++i) {
+ if (wasm_name_eq(p->module->types[i].name, p->tok)) {
+ *out = i;
+ return;
+ }
+ }
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unknown type name");
+ }
+ if (!wat_parse_i64(p, out))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected type index");
+}
+
static void wat_parse_local_index(WatParser *p, WasmFunc *f, int64_t *out) {
uint32_t i, nlocals = f->nparams + f->nlocals;
if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
@@ -1466,6 +1766,8 @@ static void wat_parse_instr(WatParser *p, WasmFunc *f) {
static void wat_parse_func(WatParser *p) {
WasmFunc *f = wasm_add_func(p->c, p->module);
+ uint32_t checked_params = 0;
+ uint32_t checked_results = 0;
wat_expect(p, WT_LPAREN, "'('");
if (!tok_is(p->tok, "func"))
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
@@ -1487,6 +1789,33 @@ static void wat_parse_func(WatParser *p) {
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
"wasm wat: expected export string");
f->export_name = wat_dup_string(p, p->tok, NULL);
+ {
+ WasmExport *ex = wasm_add_export(p->c, p->module);
+ ex->name = wasm_strdup(p->module->heap, f->export_name,
+ strlen(f->export_name));
+ if (!ex->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ ex->kind = 0;
+ ex->index = p->module->nfuncs - 1u;
+ }
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else if (tok_is(p->tok, "type")) {
+ int64_t typeidx;
+ wat_next(p);
+ wat_parse_type_index(p, &typeidx);
+ if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: type index out of range");
+ f->typeidx = (uint32_t)typeidx;
+ f->has_typeidx = 1;
+ f->nparams = p->module->types[typeidx].nparams;
+ memcpy(f->params, p->module->types[typeidx].params,
+ sizeof(f->params[0]) * f->nparams);
+ f->nresults = p->module->types[typeidx].nresults;
+ memcpy(f->results, p->module->types[typeidx].results,
+ sizeof(f->results[0]) * f->nresults);
wat_next(p);
wat_expect(p, WT_RPAREN, "')'");
} else if (tok_is(p->tok, "param")) {
@@ -1505,6 +1834,26 @@ static void wat_parse_func(WatParser *p) {
if (!wat_val_type(p->tok, &vt))
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
"wasm wat: expected parameter type");
+ if (!wasm_is_num_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unsupported parameter type");
+ if (f->has_typeidx) {
+ if (checked_params >= f->nparams || f->params[checked_params] != vt)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: parameter does not match type");
+ if (have_name) {
+ f->local_names[checked_params] =
+ wasm_strdup(p->module->heap, pending_name.p,
+ pending_name.len);
+ if (!f->local_names[checked_params])
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ have_name = 0;
+ }
+ checked_params++;
+ wat_next(p);
+ continue;
+ }
if (f->nparams >= 16u)
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
"wasm wat: too many parameters");
@@ -1526,8 +1875,18 @@ static void wat_parse_func(WatParser *p) {
if (!wat_val_type(p->tok, &vt))
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
"wasm wat: expected result type");
- f->results[0] = vt;
- f->nresults = 1;
+ if (!wasm_is_num_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unsupported result type");
+ if (f->has_typeidx) {
+ if (checked_results >= f->nresults || f->results[checked_results] != vt)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: result does not match type");
+ checked_results++;
+ } else {
+ f->results[0] = vt;
+ f->nresults = 1;
+ }
wat_next(p);
wat_expect(p, WT_RPAREN, "')'");
} else if (tok_is(p->tok, "local")) {
@@ -1577,30 +1936,128 @@ static void wat_parse_func(WatParser *p) {
wat_parse_instr(p, f);
}
}
+ if (!f->has_typeidx)
+ f->typeidx = wasm_intern_func_type(p->c, p->module, f);
+ else if (checked_params && checked_params != f->nparams)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: parameter list does not match type");
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_type_field(WatParser *p) {
+ WasmFuncType *t = wasm_add_type(p->c, p->module);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!t->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ wat_expect(p, WT_LPAREN, "'('");
+ if (!tok_is(p->tok, "func"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected func type");
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ wat_expect(p, WT_LPAREN, "'('");
+ if (tok_is(p->tok, "param")) {
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ wat_next(p);
+ continue;
+ }
+ if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected parameter type");
+ if (t->nparams >= 16u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many parameters");
+ t->params[t->nparams++] = vt;
+ wat_next(p);
+ }
+ } else if (tok_is(p->tok, "result")) {
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected result type");
+ if (t->nresults >= 1u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: multi-result unsupported");
+ t->results[t->nresults++] = vt;
+ wat_next(p);
+ }
+ } else {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected type field");
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ }
+ wat_expect(p, WT_RPAREN, "')'");
wat_expect(p, WT_RPAREN, "')'");
}
static void wat_parse_export_field(WatParser *p) {
char *name;
int64_t idx = 0;
+ uint8_t kind;
if (p->tok.kind != WT_STRING)
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
"wasm wat: expected export string");
name = wat_dup_string(p, p->tok, NULL);
wat_next(p);
wat_expect(p, WT_LPAREN, "'('");
- if (!tok_is(p->tok, "func"))
+ kind = wasm_export_kind_from_tok(p->tok);
+ if (kind == 0xffu)
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: only function exports are supported");
+ "wasm wat: expected export kind");
wat_next(p);
- wat_parse_func_index(p, &idx);
- if (idx < 0 || (uint64_t)idx >= p->module->nfuncs)
+ if (kind == 0)
+ wat_parse_func_index(p, &idx);
+ else if (!wat_parse_i64(p, &idx))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected export index");
+ if (idx < 0 || idx > UINT32_MAX)
wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
- "wasm wat: function export index out of range");
- if (p->module->funcs[idx].export_name)
- p->module->heap->free(p->module->heap, p->module->funcs[idx].export_name,
- strlen(p->module->funcs[idx].export_name) + 1u);
- p->module->funcs[idx].export_name = name;
+ "wasm wat: export index out of range");
+ {
+ WasmExport *ex = wasm_add_export(p->c, p->module);
+ ex->name = name;
+ ex->kind = kind;
+ ex->index = (uint32_t)idx;
+ }
+ if (kind == 0 && (uint64_t)idx < p->module->nfuncs) {
+ wasm_free_str(p->module->heap, &p->module->funcs[idx].export_name);
+ p->module->funcs[idx].export_name = wasm_strdup(p->module->heap, name,
+ strlen(name));
+ if (!p->module->funcs[idx].export_name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ } else if (kind == 2 && idx == 0 && p->module->has_memory) {
+ wasm_free_str(p->module->heap, &p->module->memory.export_name);
+ p->module->memory.export_name = wasm_strdup(p->module->heap, name,
+ strlen(name));
+ if (!p->module->memory.export_name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ } else if (kind == 1 && (uint64_t)idx < p->module->ntables) {
+ wasm_free_str(p->module->heap, &p->module->tables[idx].export_name);
+ p->module->tables[idx].export_name = wasm_strdup(p->module->heap, name,
+ strlen(name));
+ if (!p->module->tables[idx].export_name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ } else if (kind == 3 && (uint64_t)idx < p->module->nglobals) {
+ wasm_free_str(p->module->heap, &p->module->globals[idx].export_name);
+ p->module->globals[idx].export_name = wasm_strdup(p->module->heap, name,
+ strlen(name));
+ if (!p->module->globals[idx].export_name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ }
wat_next(p);
wat_expect(p, WT_RPAREN, "')'");
wat_expect(p, WT_RPAREN, "')'");
@@ -1633,6 +2090,371 @@ static void wat_parse_memory_field(WatParser *p) {
wat_expect(p, WT_RPAREN, "')'");
}
+static void wat_parse_limits(WatParser *p, uint32_t *min, uint32_t *max,
+ int *has_max, const char *what) {
+ int64_t lo, hi;
+ if (!wat_parse_i64(p, &lo) || lo < 0 || lo > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected %s minimum", what);
+ *min = (uint32_t)lo;
+ wat_next(p);
+ if (p->tok.kind != WT_RPAREN) {
+ if (!wat_parse_i64(p, &hi) || hi < lo || hi > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad %s maximum", what);
+ *has_max = 1;
+ *max = (uint32_t)hi;
+ wat_next(p);
+ }
+}
+
+static void wat_parse_table_limits_and_type(WatParser *p, WasmTable *t) {
+ int64_t lo, hi;
+ if (!wat_parse_i64(p, &lo) || lo < 0 || lo > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected table minimum");
+ t->min = (uint32_t)lo;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM) {
+ WasmValType maybe_type;
+ if (!wat_val_type(p->tok, &maybe_type)) {
+ if (!wat_parse_i64(p, &hi) || hi < lo || hi > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: bad table maximum");
+ t->has_max = 1;
+ t->max = (uint32_t)hi;
+ wat_next(p);
+ }
+ }
+ if (!wat_val_type(p->tok, &t->elem_type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected table element type");
+ wat_next(p);
+}
+
+static void wat_parse_import_field(WatParser *p) {
+ char *mod, *name;
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected import module string");
+ mod = wat_dup_string(p, p->tok, NULL);
+ wat_next(p);
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected import name string");
+ name = wat_dup_string(p, p->tok, NULL);
+ wat_next(p);
+ wat_expect(p, WT_LPAREN, "'('");
+ if (tok_is(p->tok, "func")) {
+ WasmFunc *f = wasm_add_func(p->c, p->module);
+ f->is_import = 1;
+ f->import_module = mod;
+ f->import_name = name;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ f->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!f->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ wat_expect(p, WT_LPAREN, "'('");
+ if (tok_is(p->tok, "type")) {
+ int64_t typeidx;
+ wat_next(p);
+ wat_parse_type_index(p, &typeidx);
+ if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: type index out of range");
+ f->typeidx = (uint32_t)typeidx;
+ f->has_typeidx = 1;
+ f->nparams = p->module->types[typeidx].nparams;
+ memcpy(f->params, p->module->types[typeidx].params,
+ sizeof(f->params[0]) * f->nparams);
+ f->nresults = p->module->types[typeidx].nresults;
+ memcpy(f->results, p->module->types[typeidx].results,
+ sizeof(f->results[0]) * f->nresults);
+ wat_next(p);
+ } else if (tok_is(p->tok, "param")) {
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ wat_next(p);
+ continue;
+ }
+ if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected parameter type");
+ if (f->nparams >= 16u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many parameters");
+ f->params[f->nparams++] = vt;
+ wat_next(p);
+ }
+ } else if (tok_is(p->tok, "result")) {
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ WasmValType vt;
+ if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected result type");
+ if (f->nresults >= 1u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: multi-result unsupported");
+ f->results[f->nresults++] = vt;
+ wat_next(p);
+ }
+ } else {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected import func type field");
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ }
+ if (!f->has_typeidx)
+ f->typeidx = wasm_intern_func_type(p->c, p->module, f);
+ } else if (tok_is(p->tok, "memory")) {
+ if (p->module->has_memory)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: multiple memories are unsupported");
+ p->module->has_memory = 1;
+ p->module->memory.is_import = 1;
+ p->module->memory.import_module = mod;
+ p->module->memory.import_name = name;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ p->module->memory.name =
+ wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!p->module->memory.name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ wat_parse_limits(p, &p->module->memory.min_pages,
+ &p->module->memory.max_pages,
+ &p->module->memory.has_max, "memory");
+ } else if (tok_is(p->tok, "table")) {
+ WasmTable *t = wasm_add_table(p->c, p->module);
+ t->is_import = 1;
+ t->import_module = mod;
+ t->import_name = name;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!t->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ wat_parse_table_limits_and_type(p, t);
+ } else if (tok_is(p->tok, "global")) {
+ WasmGlobal *g = wasm_add_global(p->c, p->module);
+ g->is_import = 1;
+ g->import_module = mod;
+ g->import_name = name;
+ wat_next(p);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ g->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!g->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ if (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (!tok_is(p->tok, "mut"))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected mut");
+ g->mutable_ = 1;
+ wat_next(p);
+ if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else {
+ if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ wat_next(p);
+ }
+ } else {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: unsupported import kind");
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_table_field(WatParser *p) {
+ WasmTable *t = wasm_add_table(p->c, p->module);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ t->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!t->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ wat_parse_table_limits_and_type(p, t);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_const_expr(WatParser *p, WasmInsn *out) {
+ WasmInsnKind kind;
+ int has_imm;
+ memset(out, 0, sizeof *out);
+ wat_expect(p, WT_LPAREN, "'('");
+ if (!wat_instr_kind(p->tok, &kind, &has_imm) ||
+ (kind != WASM_INSN_I32_CONST && kind != WASM_INSN_I64_CONST &&
+ kind != WASM_INSN_F32_CONST && kind != WASM_INSN_F64_CONST))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected constant expression");
+ out->kind = (uint8_t)kind;
+ wat_next(p);
+ if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
+ if (!wat_parse_f64(p, &out->fp))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected float immediate");
+ } else if (!wat_parse_i64(p, &out->imm)) {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected integer immediate");
+ }
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_global_field(WatParser *p) {
+ WasmGlobal *g = wasm_add_global(p->c, p->module);
+ if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
+ g->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
+ if (!g->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ wat_next(p);
+ }
+ if (p->tok.kind == WT_LPAREN) {
+ wat_next(p);
+ if (tok_is(p->tok, "export")) {
+ wat_next(p);
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected export string");
+ g->export_name = wat_dup_string(p, p->tok, NULL);
+ {
+ WasmExport *ex = wasm_add_export(p->c, p->module);
+ ex->name = wasm_strdup(p->module->heap, g->export_name,
+ strlen(g->export_name));
+ if (!ex->name)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ ex->kind = 3;
+ ex->index = p->module->nglobals - 1u;
+ }
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else if (tok_is(p->tok, "mut")) {
+ g->mutable_ = 1;
+ wat_next(p);
+ if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+ } else {
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ }
+ } else {
+ if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected global type");
+ wat_next(p);
+ }
+ if (!g->type)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: missing global type");
+ wat_parse_const_expr(p, &g->init);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_elem_field(WatParser *p) {
+ WasmElemSegment *e = wasm_add_elem(p->c, p->module);
+ e->tableidx = 0;
+ if (p->tok.kind == WT_ATOM) {
+ int64_t tableidx;
+ if (wat_parse_i64(p, &tableidx)) {
+ if (tableidx < 0 || tableidx > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: table index out of range");
+ e->tableidx = (uint32_t)tableidx;
+ wat_next(p);
+ }
+ }
+ {
+ WasmInsn off;
+ wat_parse_const_expr(p, &off);
+ if (off.kind != WASM_INSN_I32_CONST || off.imm < 0)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: element offset must be i32.const");
+ e->offset = off.imm;
+ }
+ if (tok_is(p->tok, "func"))
+ wat_next(p);
+ while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
+ int64_t idx;
+ if (e->nfuncs >= 64u)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: too many element functions");
+ wat_parse_func_index(p, &idx);
+ if (idx < 0 || idx > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: element function index out of range");
+ e->funcs[e->nfuncs++] = (uint32_t)idx;
+ wat_next(p);
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_start_field(WatParser *p) {
+ int64_t idx;
+ wat_parse_func_index(p, &idx);
+ if (idx < 0 || idx > UINT32_MAX)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: start function index out of range");
+ p->module->has_start = 1;
+ p->module->start_func = (uint32_t)idx;
+ wat_next(p);
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
+static void wat_parse_custom_field(WatParser *p) {
+ WasmCustom *cs;
+ size_t n = 0;
+ char *bytes;
+ if (p->tok.kind != WT_STRING)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm wat: expected custom section name");
+ cs = wasm_add_custom(p->c, p->module);
+ cs->name = wat_dup_string(p, p->tok, NULL);
+ if (strcmp(cs->name, "target_features") == 0 ||
+ strcmp(cs->name, "target-feature") == 0)
+ p->module->has_target_features = 1;
+ wat_next(p);
+ if (p->tok.kind == WT_STRING) {
+ bytes = wat_dup_string(p, p->tok, &n);
+ cs->data = (uint8_t *)p->module->heap->alloc(p->module->heap, n ? n : 1u, 1);
+ if (!cs->data)
+ wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
+ "wasm: out of memory");
+ memcpy(cs->data, bytes, n);
+ cs->len = (uint32_t)n;
+ p->module->heap->free(p->module->heap, bytes, p->tok.len + 1u);
+ wat_next(p);
+ }
+ wat_expect(p, WT_RPAREN, "')'");
+}
+
static void wat_parse_data_field(WatParser *p) {
int64_t offset = 0;
char *bytes;
@@ -1686,7 +2508,10 @@ static void wat_parse_module(CfreeCompiler *c, const CfreeBytesInput *input,
wasm_error(c, wasm_loc(p.tok.line, p.tok.col),
"wasm wat: expected module field");
wat_next(&p);
- if (tok_is(p.tok, "func")) {
+ if (tok_is(p.tok, "type")) {
+ wat_next(&p);
+ wat_parse_type_field(&p);
+ } else if (tok_is(p.tok, "func")) {
p.pos = (size_t)(p.tok.p - p.src);
p.line = p.tok.line;
p.col = p.tok.col;
@@ -1705,11 +2530,24 @@ static void wat_parse_module(CfreeCompiler *c, const CfreeBytesInput *input,
} else if (tok_is(p.tok, "data")) {
wat_next(&p);
wat_parse_data_field(&p);
- } else if (tok_is(p.tok, "import") || tok_is(p.tok, "table") ||
- tok_is(p.tok, "global") || tok_is(p.tok, "elem") ||
- tok_is(p.tok, "start")) {
- wasm_error(c, wasm_loc(p.tok.line, p.tok.col),
- "wasm wat: unsupported module field");
+ } else if (tok_is(p.tok, "import")) {
+ wat_next(&p);
+ wat_parse_import_field(&p);
+ } else if (tok_is(p.tok, "table")) {
+ wat_next(&p);
+ wat_parse_table_field(&p);
+ } else if (tok_is(p.tok, "global")) {
+ wat_next(&p);
+ wat_parse_global_field(&p);
+ } else if (tok_is(p.tok, "elem")) {
+ wat_next(&p);
+ wat_parse_elem_field(&p);
+ } else if (tok_is(p.tok, "start")) {
+ wat_next(&p);
+ wat_parse_start_field(&p);
+ } else if (tok_is(p.tok, "custom") || tok_is(p.tok, "@custom")) {
+ wat_next(&p);
+ wat_parse_custom_field(&p);
} else {
wat_skip_list(&p);
}
@@ -1728,9 +2566,10 @@ static uint8_t bin_u8(BinReader *r) {
static uint32_t bin_uleb(BinReader *r) {
uint32_t result = 0, shift = 0;
+ uint32_t nbytes = 0;
for (;;) {
uint8_t b = bin_u8(r);
- if (shift >= 35u)
+ if (nbytes++ >= 5u || (shift == 28u && (b & 0xf0u)))
wasm_error(r->c, wasm_loc(0, 0), "wasm: invalid uleb128");
result |= (uint32_t)(b & 0x7fu) << shift;
if (!(b & 0x80u))
@@ -1742,8 +2581,12 @@ static uint32_t bin_uleb(BinReader *r) {
static int64_t bin_sleb(BinReader *r, uint32_t bits) {
int64_t result = 0;
uint32_t shift = 0;
+ uint32_t max_bytes = (bits + 6u) / 7u;
+ uint32_t nbytes = 0;
uint8_t b;
do {
+ if (nbytes++ >= max_bytes)
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: invalid sleb128");
b = bin_u8(r);
result |= (int64_t)(b & 0x7fu) << shift;
shift += 7u;
@@ -1776,18 +2619,42 @@ static void bin_need(BinReader *r, size_t n) {
wasm_error(r->c, wasm_loc(0, 0), "wasm: section length out of bounds");
}
-typedef struct BinType {
- WasmValType params[16];
- uint32_t nparams;
- WasmValType results[1];
- uint32_t nresults;
-} BinType;
+static char *bin_name(BinReader *r, CfreeHeap *h, uint32_t *len_out) {
+ uint32_t n = bin_uleb(r);
+ char *s;
+ bin_need(r, n);
+ s = wasm_strdup(h, (const char *)(r->data + r->pos), n);
+ if (!s)
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: out of memory");
+ r->pos += n;
+ if (len_out)
+ *len_out = n;
+ return s;
+}
+
+static WasmValType bin_val_type(BinReader *r, int refs_ok) {
+ uint8_t b = bin_u8(r);
+ switch (b) {
+ case WASM_VAL_I32:
+ case WASM_VAL_I64:
+ case WASM_VAL_F32:
+ case WASM_VAL_F64:
+ return (WasmValType)b;
+ case WASM_VAL_FUNCREF:
+ case WASM_VAL_EXTERNREF:
+ if (refs_ok)
+ return (WasmValType)b;
+ break;
+ default:
+ break;
+ }
+ wasm_error(r->c, wasm_loc(0, 0), "wasm: unsupported value type 0x%02x", b);
+ return WASM_VAL_I32;
+}
static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
WasmModule *out) {
BinReader r;
- BinType types[64];
- uint32_t ntypes = 0;
uint32_t nfunc_types = 0;
uint8_t last_id = 0;
memset(&r, 0, sizeof r);
@@ -1814,6 +2681,24 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
if (name_len == 7u && memcmp(r.data + r.pos, "linking", 7) == 0)
wasm_error(c, wasm_loc(0, 0),
"wasm: relocatable object metadata is not frontend input");
+ {
+ WasmCustom *cs = wasm_add_custom(c, out);
+ cs->name = wasm_strdup(out->heap, (const char *)(r.data + r.pos),
+ name_len);
+ if (!cs->name)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ r.pos += name_len;
+ if (strcmp(cs->name, "target_features") == 0 ||
+ strcmp(cs->name, "target-feature") == 0)
+ out->has_target_features = 1;
+ cs->len = (uint32_t)(end - r.pos);
+ if (cs->len) {
+ cs->data = (uint8_t *)out->heap->alloc(out->heap, cs->len, 1);
+ if (!cs->data)
+ wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ memcpy(cs->data, r.data + r.pos, cs->len);
+ }
+ }
r.pos = end;
continue;
}
@@ -1822,25 +2707,89 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
if (count > 64u)
wasm_error(c, wasm_loc(0, 0), "wasm: too many types");
for (i = 0; i < count; ++i) {
+ WasmFuncType *t = wasm_add_type(c, out);
uint32_t j, nparam, nresult;
if (bin_u8(&r) != 0x60u)
wasm_error(c, wasm_loc(0, 0), "wasm: expected function type");
nparam = bin_uleb(&r);
if (nparam > 16u)
wasm_error(c, wasm_loc(0, 0), "wasm: too many parameters");
- types[ntypes].nparams = nparam;
+ t->nparams = nparam;
for (j = 0; j < nparam; ++j)
- types[ntypes].params[j] = (WasmValType)bin_u8(&r);
+ t->params[j] = bin_val_type(&r, 0);
nresult = bin_uleb(&r);
if (nresult > 1u)
wasm_error(c, wasm_loc(0, 0), "wasm: multi-result unsupported");
- types[ntypes].nresults = nresult;
+ t->nresults = nresult;
for (j = 0; j < nresult; ++j)
- types[ntypes].results[j] = (WasmValType)bin_u8(&r);
- ntypes++;
+ t->results[j] = bin_val_type(&r, 0);
}
} else if (id == 2) {
- wasm_error(c, wasm_loc(0, 0), "wasm: imports are unsupported");
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ char *mod = bin_name(&r, out->heap, NULL);
+ char *name = bin_name(&r, out->heap, NULL);
+ uint8_t kind = bin_u8(&r);
+ if (kind == 0) {
+ uint32_t typeidx = bin_uleb(&r);
+ WasmFunc *f;
+ if (typeidx >= out->ntypes)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad import type index");
+ f = wasm_add_func(c, out);
+ f->is_import = 1;
+ f->import_module = mod;
+ f->import_name = name;
+ f->typeidx = typeidx;
+ f->has_typeidx = 1;
+ f->nparams = out->types[typeidx].nparams;
+ memcpy(f->params, out->types[typeidx].params,
+ sizeof(f->params[0]) * f->nparams);
+ f->nresults = out->types[typeidx].nresults;
+ memcpy(f->results, out->types[typeidx].results,
+ sizeof(f->results[0]) * f->nresults);
+ } else if (kind == 1) {
+ WasmTable *t = wasm_add_table(c, out);
+ t->is_import = 1;
+ t->import_module = mod;
+ t->import_name = name;
+ t->elem_type = bin_val_type(&r, 1);
+ {
+ uint32_t flags = bin_uleb(&r);
+ if (flags & ~1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported table limits");
+ t->has_max = (flags & 1u) != 0;
+ t->min = bin_uleb(&r);
+ if (t->has_max)
+ t->max = bin_uleb(&r);
+ }
+ } else if (kind == 2) {
+ uint32_t flags;
+ if (out->has_memory)
+ wasm_error(c, wasm_loc(0, 0), "wasm: multiple memories unsupported");
+ out->has_memory = 1;
+ out->memory.is_import = 1;
+ out->memory.import_module = mod;
+ out->memory.import_name = name;
+ flags = bin_uleb(&r);
+ if (flags & ~1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported memory limits");
+ out->memory.has_max = (flags & 1u) != 0;
+ out->memory.min_pages = bin_uleb(&r);
+ if (out->memory.has_max)
+ out->memory.max_pages = bin_uleb(&r);
+ } else if (kind == 3) {
+ WasmGlobal *g = wasm_add_global(c, out);
+ g->is_import = 1;
+ g->import_module = mod;
+ g->import_name = name;
+ g->type = bin_val_type(&r, 0);
+ g->mutable_ = bin_u8(&r);
+ if (g->mutable_ > 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad global mutability");
+ } else {
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported import kind");
+ }
+ }
} else if (id == 3) {
uint32_t i, count = bin_uleb(&r);
if (count > 64u)
@@ -1848,14 +2797,16 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
for (i = 0; i < count; ++i) {
uint32_t typeidx = bin_uleb(&r);
WasmFunc *f;
- if (typeidx >= ntypes)
+ if (typeidx >= out->ntypes)
wasm_error(c, wasm_loc(0, 0), "wasm: bad function type index");
f = wasm_add_func(c, out);
- f->nparams = types[typeidx].nparams;
- memcpy(f->params, types[typeidx].params,
+ f->typeidx = typeidx;
+ f->has_typeidx = 1;
+ f->nparams = out->types[typeidx].nparams;
+ memcpy(f->params, out->types[typeidx].params,
sizeof(f->params[0]) * f->nparams);
- f->nresults = types[typeidx].nresults;
- memcpy(f->results, types[typeidx].results,
+ f->nresults = out->types[typeidx].nresults;
+ memcpy(f->results, out->types[typeidx].results,
sizeof(f->results[0]) * f->nresults);
nfunc_types++;
}
@@ -1865,6 +2816,8 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
if (count > 1u)
wasm_error(c, wasm_loc(0, 0), "wasm: multiple memories unsupported");
if (count == 1u) {
+ if (out->has_memory)
+ wasm_error(c, wasm_loc(0, 0), "wasm: multiple memories unsupported");
flags = bin_u8(&r);
out->has_memory = 1;
out->memory.min_pages = bin_uleb(&r);
@@ -1877,37 +2830,115 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
wasm_memory_ensure(c, out, out->memory.min_pages * 65536u);
}
} else if (id == 4) {
- wasm_error(c, wasm_loc(0, 0), "wasm: tables are unsupported");
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ WasmTable *t = wasm_add_table(c, out);
+ uint32_t flags;
+ t->elem_type = bin_val_type(&r, 1);
+ flags = bin_uleb(&r);
+ if (flags & ~1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported table limits");
+ t->has_max = (flags & 1u) != 0;
+ t->min = bin_uleb(&r);
+ if (t->has_max)
+ t->max = bin_uleb(&r);
+ }
} else if (id == 6) {
- wasm_error(c, wasm_loc(0, 0), "wasm: globals are unsupported");
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ WasmGlobal *g = wasm_add_global(c, out);
+ uint8_t op;
+ g->type = bin_val_type(&r, 0);
+ g->mutable_ = bin_u8(&r);
+ if (g->mutable_ > 1u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad global mutability");
+ op = bin_u8(&r);
+ switch (op) {
+ case 0x41:
+ g->init.kind = WASM_INSN_I32_CONST;
+ g->init.imm = bin_sleb(&r, 32);
+ break;
+ case 0x42:
+ g->init.kind = WASM_INSN_I64_CONST;
+ g->init.imm = bin_sleb(&r, 64);
+ break;
+ case 0x43:
+ g->init.kind = WASM_INSN_F32_CONST;
+ g->init.fp = bin_f32(&r);
+ break;
+ case 0x44:
+ g->init.kind = WASM_INSN_F64_CONST;
+ g->init.fp = bin_f64(&r);
+ break;
+ default:
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported global initializer");
+ }
+ if (bin_u8(&r) != 0x0bu)
+ wasm_error(c, wasm_loc(0, 0), "wasm: malformed global initializer");
+ }
} else if (id == 7) {
uint32_t i, count = bin_uleb(&r);
for (i = 0; i < count; ++i) {
- uint32_t n = bin_uleb(&r);
- const uint8_t *name;
+ char *name;
uint8_t kind;
uint32_t idx;
- bin_need(&r, n);
- name = r.data + r.pos;
- r.pos += n;
+ WasmExport *ex;
+ name = bin_name(&r, out->heap, NULL);
kind = bin_u8(&r);
idx = bin_uleb(&r);
+ if (kind > 3u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad export kind");
+ ex = wasm_add_export(c, out);
+ ex->name = name;
+ ex->kind = kind;
+ ex->index = idx;
if (kind == 0 && idx < out->nfuncs) {
WasmFunc *f = &out->funcs[idx];
- if (f->export_name)
- out->heap->free(out->heap, f->export_name,
- strlen(f->export_name) + 1u);
- f->export_name = wasm_strdup(out->heap, (const char *)name, n);
+ wasm_free_str(out->heap, &f->export_name);
+ f->export_name = wasm_strdup(out->heap, name, strlen(name));
if (!f->export_name)
wasm_error(c, wasm_loc(0, 0), "wasm: out of memory");
+ } else if (kind == 1 && idx < out->ntables) {
+ wasm_free_str(out->heap, &out->tables[idx].export_name);
+ out->tables[idx].export_name = wasm_strdup(out->heap, name,
+ strlen(name));
+ } else if (kind == 2 && idx == 0 && out->has_memory) {
+ wasm_free_str(out->heap, &out->memory.export_name);
+ out->memory.export_name = wasm_strdup(out->heap, name, strlen(name));
+ } else if (kind == 3 && idx < out->nglobals) {
+ wasm_free_str(out->heap, &out->globals[idx].export_name);
+ out->globals[idx].export_name = wasm_strdup(out->heap, name,
+ strlen(name));
}
}
} else if (id == 8) {
- wasm_error(c, wasm_loc(0, 0), "wasm: start functions are unsupported");
+ out->has_start = 1;
+ out->start_func = bin_uleb(&r);
} else if (id == 9) {
- wasm_error(c, wasm_loc(0, 0), "wasm: element segments are unsupported");
+ uint32_t i, count = bin_uleb(&r);
+ for (i = 0; i < count; ++i) {
+ WasmElemSegment *e = wasm_add_elem(c, out);
+ uint32_t flags = bin_uleb(&r);
+ uint32_t n;
+ if (flags != 0)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: unsupported element segment kind");
+ e->tableidx = 0;
+ if (bin_u8(&r) != 0x41)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: expected i32.const element offset");
+ e->offset = bin_sleb(&r, 32);
+ if (bin_u8(&r) != 0x0b || e->offset < 0)
+ wasm_error(c, wasm_loc(0, 0), "wasm: bad element offset");
+ n = bin_uleb(&r);
+ if (n > 64u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: too many element functions");
+ for (uint32_t k = 0; k < n; ++k)
+ e->funcs[e->nfuncs++] = bin_uleb(&r);
+ }
} else if (id == 10) {
uint32_t i, count = bin_uleb(&r);
+ uint32_t func_index = 0;
if (count != nfunc_types)
wasm_error(c, wasm_loc(0, 0), "wasm: function/code count mismatch");
for (i = 0; i < count; ++i) {
@@ -1915,7 +2946,13 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
size_t body_end;
uint32_t local_groups, j;
uint32_t control_depth = 0;
- WasmFunc *f = &out->funcs[i];
+ int saw_body_end = 0;
+ WasmFunc *f;
+ while (func_index < out->nfuncs && out->funcs[func_index].is_import)
+ func_index++;
+ if (func_index >= out->nfuncs)
+ wasm_error(c, wasm_loc(0, 0), "wasm: code body without function");
+ f = &out->funcs[func_index++];
bin_need(&r, body_size);
body_end = r.pos + body_size;
local_groups = bin_uleb(&r);
@@ -1931,7 +2968,10 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
uint8_t op = bin_u8(&r);
if (op == 0x0bu) {
if (!control_depth)
+ {
+ saw_body_end = 1;
break;
+ }
control_depth--;
wasm_func_add_insn(c, out, f, WASM_INSN_END, 0);
continue;
@@ -1944,17 +2984,23 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
wasm_func_add_insn(c, out, f, WASM_INSN_NOP, 0);
break;
case 0x02:
- (void)bin_u8(&r);
+ if (bin_u8(&r) != 0x40u)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: block results are unsupported");
control_depth++;
wasm_func_add_insn(c, out, f, WASM_INSN_BLOCK, 0);
break;
case 0x03:
- (void)bin_u8(&r);
+ if (bin_u8(&r) != 0x40u)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: loop results are unsupported");
control_depth++;
wasm_func_add_insn(c, out, f, WASM_INSN_LOOP, 0);
break;
case 0x04:
- (void)bin_u8(&r);
+ if (bin_u8(&r) != 0x40u)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: if results are unsupported");
control_depth++;
wasm_func_add_insn(c, out, f, WASM_INSN_IF, 0);
break;
@@ -2417,6 +3463,8 @@ static void wasm_decode_binary(CfreeCompiler *c, const CfreeBytesInput *input,
op);
}
}
+ if (!saw_body_end)
+ wasm_error(c, wasm_loc(0, 0), "wasm: function body missing end");
r.pos = body_end;
}
} else if (id == 11) {
@@ -2464,8 +3512,12 @@ static CfreeCgTypeId wasm_cg_type(CfreeCompiler *c, CfreeCgBuiltinTypes b,
return b.id[CFREE_CG_BUILTIN_F32];
case WASM_VAL_F64:
return b.id[CFREE_CG_BUILTIN_F64];
+ case WASM_VAL_FUNCREF:
+ case WASM_VAL_EXTERNREF:
+ break;
}
wasm_error(c, wasm_loc(0, 0), "wasm: unsupported value type");
+ return b.id[CFREE_CG_BUILTIN_I32];
}
static WasmValType wasm_func_local_type(const WasmFunc *f, uint32_t index) {
@@ -2878,164 +3930,268 @@ static int wasm_conversion_kind(uint8_t kind, WasmValType *src,
}
}
+typedef struct WasmValStack {
+ WasmValType vals[256];
+ uint32_t depth;
+} WasmValStack;
+
+typedef struct WasmControlFrame {
+ uint8_t kind;
+ uint32_t height;
+ int seen_else;
+ int unreachable;
+} WasmControlFrame;
+
+static WasmValType wasm_global_init_type(const WasmInsn *in) {
+ switch (in->kind) {
+ case WASM_INSN_I32_CONST:
+ return WASM_VAL_I32;
+ case WASM_INSN_I64_CONST:
+ return WASM_VAL_I64;
+ case WASM_INSN_F32_CONST:
+ return WASM_VAL_F32;
+ case WASM_INSN_F64_CONST:
+ return WASM_VAL_F64;
+ default:
+ return 0;
+ }
+}
+
+static void wasm_stack_push(CfreeCompiler *c, WasmValStack *s,
+ WasmValType vt) {
+ if (s->depth >= 256u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep");
+ s->vals[s->depth++] = vt;
+}
+
+static int wasm_stack_pop(CfreeCompiler *c, WasmValStack *s,
+ WasmControlFrame *frames, uint32_t nframes,
+ WasmValType expected, const char *what) {
+ WasmControlFrame *top = &frames[nframes - 1u];
+ if (s->depth <= top->height) {
+ if (top->unreachable)
+ return 1;
+ wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
+ }
+ if (expected && s->vals[s->depth - 1u] != expected)
+ wasm_error(c, wasm_loc(0, 0), "wasm: %s type mismatch", what);
+ s->depth--;
+ return 1;
+}
+
+static void wasm_mark_unreachable(WasmValStack *s, WasmControlFrame *frames,
+ uint32_t nframes) {
+ WasmControlFrame *top = &frames[nframes - 1u];
+ s->depth = top->height;
+ top->unreachable = 1;
+}
+
static void wasm_validate(WasmModule *m, CfreeCompiler *c) {
uint32_t i, j;
+ for (i = 0; i < m->ntypes; ++i) {
+ for (j = 0; j < m->types[i].nparams; ++j)
+ if (!wasm_is_num_type(m->types[i].params[j]))
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported parameter type");
+ for (j = 0; j < m->types[i].nresults; ++j)
+ if (!wasm_is_num_type(m->types[i].results[j]))
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported result type");
+ }
+ if (m->has_memory && m->memory.has_max &&
+ m->memory.max_pages < m->memory.min_pages)
+ wasm_error(c, wasm_loc(0, 0), "wasm: memory maximum below minimum");
+ for (i = 0; i < m->ntables; ++i) {
+ if (m->tables[i].elem_type != WASM_VAL_FUNCREF)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: reference type is unsupported for tables");
+ if (m->tables[i].has_max && m->tables[i].max < m->tables[i].min)
+ wasm_error(c, wasm_loc(0, 0), "wasm: table maximum below minimum");
+ }
+ for (i = 0; i < m->nglobals; ++i) {
+ WasmGlobal *g = &m->globals[i];
+ if (!wasm_is_num_type(g->type))
+ wasm_error(c, wasm_loc(0, 0), "wasm: unsupported global type");
+ if (!g->is_import && wasm_global_init_type(&g->init) != g->type)
+ wasm_error(c, wasm_loc(0, 0), "wasm: global initializer type mismatch");
+ }
+ for (i = 0; i < m->nexports; ++i) {
+ WasmExport *ex = &m->exports[i];
+ if ((ex->kind == 0 && ex->index >= m->nfuncs) ||
+ (ex->kind == 1 && ex->index >= m->ntables) ||
+ (ex->kind == 2 && (!m->has_memory || ex->index != 0)) ||
+ (ex->kind == 3 && ex->index >= m->nglobals))
+ wasm_error(c, wasm_loc(0, 0), "wasm: export index out of range");
+ }
+ if (m->has_start) {
+ if (m->start_func >= m->nfuncs)
+ wasm_error(c, wasm_loc(0, 0), "wasm: start function index out of range");
+ if (m->funcs[m->start_func].nparams || m->funcs[m->start_func].nresults)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: start function must have no params or results");
+ }
+ for (i = 0; i < m->nelems; ++i) {
+ if (m->elems[i].tableidx >= m->ntables)
+ wasm_error(c, wasm_loc(0, 0), "wasm: element table index out of range");
+ for (j = 0; j < m->elems[i].nfuncs; ++j)
+ if (m->elems[i].funcs[j] >= m->nfuncs)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: element function index out of range");
+ }
for (i = 0; i < m->nfuncs; ++i) {
WasmFunc *f = &m->funcs[i];
- WasmValType stack[256];
- uint32_t depth = 0;
- uint32_t control_depth = 0;
+ WasmValStack stack;
+ WasmControlFrame control[65];
+ uint32_t ncontrol = 1;
+ memset(&stack, 0, sizeof stack);
+ memset(control, 0, sizeof control);
+ control[0].kind = 0xffu;
+ control[0].height = 0;
+ if (f->is_import) {
+ if (f->ninsns)
+ wasm_error(c, wasm_loc(0, 0), "wasm: imported function has body");
+ continue;
+ }
if (f->nresults > 1u)
wasm_error(c, wasm_loc(0, 0), "wasm: multi-result unsupported");
for (j = 0; j < f->ninsns; ++j) {
WasmInsn *in = &f->insns[j];
WasmValType vt, src, dst;
switch (in->kind) {
- case WASM_INSN_F32_CONST:
- if (depth >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep");
- stack[depth++] = WASM_VAL_F32;
- break;
- case WASM_INSN_F64_CONST:
- if (depth >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep");
- stack[depth++] = WASM_VAL_F64;
- break;
- case WASM_INSN_I32_CONST:
- if (depth >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep");
- stack[depth++] = WASM_VAL_I32;
- break;
- case WASM_INSN_I64_CONST:
- if (depth >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep");
- stack[depth++] = WASM_VAL_I64;
- break;
+ case WASM_INSN_F32_CONST: wasm_stack_push(c, &stack, WASM_VAL_F32); break;
+ case WASM_INSN_F64_CONST: wasm_stack_push(c, &stack, WASM_VAL_F64); break;
+ case WASM_INSN_I32_CONST: wasm_stack_push(c, &stack, WASM_VAL_I32); break;
+ case WASM_INSN_I64_CONST: wasm_stack_push(c, &stack, WASM_VAL_I64); break;
case WASM_INSN_LOCAL_GET:
- if (in->kind == WASM_INSN_LOCAL_GET &&
+ if (in->imm < 0 ||
(uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals)
wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range");
- if (depth >= 256u)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack too deep");
- stack[depth++] = wasm_func_local_type(f, (uint32_t)in->imm);
+ wasm_stack_push(c, &stack, wasm_func_local_type(f, (uint32_t)in->imm));
break;
case WASM_INSN_LOCAL_SET:
- if (in->imm < 0 || (uint64_t)in->imm >=
- (uint64_t)f->nparams + f->nlocals)
- wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range");
- if (depth < 1)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- if (stack[depth - 1u] != wasm_func_local_type(f, (uint32_t)in->imm))
- wasm_error(c, wasm_loc(0, 0), "wasm: local type mismatch");
- depth--;
- break;
case WASM_INSN_LOCAL_TEE:
- if (in->imm < 0 || (uint64_t)in->imm >=
- (uint64_t)f->nparams + f->nlocals)
+ if (in->imm < 0 ||
+ (uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals)
wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range");
- if (depth < 1)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- if (stack[depth - 1u] != wasm_func_local_type(f, (uint32_t)in->imm))
- wasm_error(c, wasm_loc(0, 0), "wasm: local type mismatch");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_func_local_type(f, (uint32_t)in->imm), "local");
+ if (in->kind == WASM_INSN_LOCAL_TEE)
+ wasm_stack_push(c, &stack, wasm_func_local_type(f, (uint32_t)in->imm));
break;
case WASM_INSN_CALL:
if (in->imm < 0 || (uint64_t)in->imm >= m->nfuncs)
wasm_error(c, wasm_loc(0, 0), "wasm: call index out of range");
- if (depth < m->funcs[in->imm].nparams)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
for (uint32_t k = 0; k < m->funcs[in->imm].nparams; ++k) {
uint32_t param = m->funcs[in->imm].nparams - 1u - k;
- if (stack[depth - 1u - k] != m->funcs[in->imm].params[param])
- wasm_error(c, wasm_loc(0, 0), "wasm: call argument type mismatch");
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ m->funcs[in->imm].params[param], "call argument");
}
- depth -= m->funcs[in->imm].nparams;
if (m->funcs[in->imm].nresults)
- stack[depth++] = m->funcs[in->imm].results[0];
+ wasm_stack_push(c, &stack, m->funcs[in->imm].results[0]);
break;
case WASM_INSN_RETURN:
- if (depth < (f->nresults ? 1 : 0))
- wasm_error(c, wasm_loc(0, 0), "wasm: return stack underflow");
- if (f->nresults && stack[depth - 1u] != f->results[0])
- wasm_error(c, wasm_loc(0, 0), "wasm: return type mismatch");
+ if (f->nresults)
+ wasm_stack_pop(c, &stack, control, ncontrol, f->results[0],
+ "return");
+ wasm_mark_unreachable(&stack, control, ncontrol);
break;
case WASM_INSN_DROP:
- if (depth < 1)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- depth--;
+ wasm_stack_pop(c, &stack, control, ncontrol, 0, "drop");
break;
case WASM_INSN_I32_EQZ:
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "eqz");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
+ break;
case WASM_INSN_I64_EQZ:
- if (depth < 1)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- if ((in->kind == WASM_INSN_I32_EQZ && stack[depth - 1u] != WASM_VAL_I32) ||
- (in->kind == WASM_INSN_I64_EQZ && stack[depth - 1u] != WASM_VAL_I64))
- wasm_error(c, wasm_loc(0, 0), "wasm: eqz type mismatch");
- stack[depth - 1u] = WASM_VAL_I32;
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I64, "eqz");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
break;
case WASM_INSN_BLOCK:
case WASM_INSN_LOOP:
- control_depth++;
+ if (ncontrol >= 65u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
+ control[ncontrol].kind = in->kind;
+ control[ncontrol].height = stack.depth;
+ control[ncontrol].seen_else = 0;
+ control[ncontrol].unreachable = 0;
+ ncontrol++;
break;
case WASM_INSN_IF:
- if (depth < 1)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- if (stack[depth - 1u] != WASM_VAL_I32)
- wasm_error(c, wasm_loc(0, 0), "wasm: if condition must be i32");
- depth--;
- control_depth++;
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "if");
+ if (ncontrol >= 65u)
+ wasm_error(c, wasm_loc(0, 0), "wasm: control stack too deep");
+ control[ncontrol].kind = in->kind;
+ control[ncontrol].height = stack.depth;
+ control[ncontrol].seen_else = 0;
+ control[ncontrol].unreachable = 0;
+ ncontrol++;
break;
case WASM_INSN_ELSE:
- if (!control_depth)
- wasm_error(c, wasm_loc(0, 0), "wasm: else without block");
+ if (ncontrol <= 1u || control[ncontrol - 1u].kind != WASM_INSN_IF)
+ wasm_error(c, wasm_loc(0, 0), "wasm: else without if");
+ if (!control[ncontrol - 1u].unreachable &&
+ stack.depth != control[ncontrol - 1u].height)
+ wasm_error(c, wasm_loc(0, 0), "wasm: if branch result mismatch");
+ stack.depth = control[ncontrol - 1u].height;
+ control[ncontrol - 1u].seen_else = 1;
+ control[ncontrol - 1u].unreachable = 0;
break;
case WASM_INSN_END:
- if (!control_depth)
+ if (ncontrol <= 1u)
wasm_error(c, wasm_loc(0, 0), "wasm: end without block");
- control_depth--;
+ if (!control[ncontrol - 1u].unreachable &&
+ stack.depth != control[ncontrol - 1u].height)
+ wasm_error(c, wasm_loc(0, 0), "wasm: block result mismatch");
+ stack.depth = control[ncontrol - 1u].height;
+ ncontrol--;
break;
case WASM_INSN_BR:
- if (in->imm < 0 || (uint64_t)in->imm >= control_depth)
+ if (in->imm < 0 || (uint64_t)in->imm >= ncontrol - 1u)
wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
+ wasm_mark_unreachable(&stack, control, ncontrol);
break;
case WASM_INSN_BR_IF:
- if (in->imm < 0 || (uint64_t)in->imm >= control_depth)
+ if (in->imm < 0 || (uint64_t)in->imm >= ncontrol - 1u)
wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range");
- if (depth < 1)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- if (stack[depth - 1u] != WASM_VAL_I32)
- wasm_error(c, wasm_loc(0, 0), "wasm: br_if condition must be i32");
- depth--;
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "br_if");
break;
case WASM_INSN_BR_TABLE:
if (in->ntargets == 0)
wasm_error(c, wasm_loc(0, 0), "wasm: br_table without targets");
- if (depth < 1 || stack[depth - 1u] != WASM_VAL_I32)
- wasm_error(c, wasm_loc(0, 0), "wasm: br_table selector must be i32");
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
+ "br_table selector");
for (uint32_t k = 0; k < in->ntargets; ++k)
- if (in->targets[k] >= control_depth)
+ if (in->targets[k] >= ncontrol - 1u)
wasm_error(c, wasm_loc(0, 0), "wasm: br_table depth out of range");
- depth--;
+ wasm_mark_unreachable(&stack, control, ncontrol);
break;
- case WASM_INSN_SELECT:
- if (depth < 3)
+ case WASM_INSN_SELECT: {
+ WasmValType rhs, lhs;
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "select");
+ if (stack.depth <= control[ncontrol - 1u].height &&
+ control[ncontrol - 1u].unreachable) {
+ in->type = WASM_VAL_I32;
+ break;
+ }
+ if (stack.depth < control[ncontrol - 1u].height + 2u)
wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- if (stack[depth - 1u] != WASM_VAL_I32 ||
- stack[depth - 2u] != stack[depth - 3u])
+ rhs = stack.vals[--stack.depth];
+ lhs = stack.vals[--stack.depth];
+ if (lhs != rhs)
wasm_error(c, wasm_loc(0, 0), "wasm: select type mismatch");
- in->type = (uint8_t)stack[depth - 3u];
- depth -= 2u;
+ in->type = (uint8_t)lhs;
+ wasm_stack_push(c, &stack, lhs);
break;
+ }
case WASM_INSN_MEMORY_SIZE:
if (!m->has_memory)
wasm_error(c, wasm_loc(0, 0), "wasm: memory.size without memory");
- stack[depth++] = WASM_VAL_I32;
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
break;
case WASM_INSN_MEMORY_GROW:
if (!m->has_memory)
wasm_error(c, wasm_loc(0, 0), "wasm: memory.grow without memory");
- if (depth < 1 || stack[depth - 1u] != WASM_VAL_I32)
- wasm_error(c, wasm_loc(0, 0), "wasm: memory.grow expects i32");
- stack[depth - 1u] = WASM_VAL_I32;
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32,
+ "memory.grow");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
break;
case WASM_INSN_I32_LOAD:
case WASM_INSN_I64_LOAD:
@@ -3051,9 +4207,8 @@ static void wasm_validate(WasmModule *m, CfreeCompiler *c) {
case WASM_INSN_I64_LOAD32_U:
if (!m->has_memory)
wasm_error(c, wasm_loc(0, 0), "wasm: load without memory");
- if (depth < 1 || stack[depth - 1u] != WASM_VAL_I32)
- wasm_error(c, wasm_loc(0, 0), "wasm: load address must be i32");
- stack[depth - 1u] = wasm_load_result_type(in->kind);
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "load");
+ wasm_stack_push(c, &stack, wasm_load_result_type(in->kind));
break;
case WASM_INSN_I32_STORE:
case WASM_INSN_I64_STORE:
@@ -3064,62 +4219,68 @@ static void wasm_validate(WasmModule *m, CfreeCompiler *c) {
case WASM_INSN_I64_STORE32:
if (!m->has_memory)
wasm_error(c, wasm_loc(0, 0), "wasm: store without memory");
- if (depth < 2)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- if (stack[depth - 2u] != WASM_VAL_I32 ||
- stack[depth - 1u] != wasm_store_value_type(in->kind))
- wasm_error(c, wasm_loc(0, 0), "wasm: store type mismatch");
- depth -= 2u;
+ wasm_stack_pop(c, &stack, control, ncontrol,
+ wasm_store_value_type(in->kind), "store");
+ wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "store");
break;
case WASM_INSN_UNREACHABLE:
+ wasm_mark_unreachable(&stack, control, ncontrol);
+ break;
case WASM_INSN_NOP:
break;
default:
if (wasm_int_unop_kind(in->kind, &vt)) {
- if (depth < 1 || stack[depth - 1u] != vt)
- wasm_error(c, wasm_loc(0, 0), "wasm: unary operand type mismatch");
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "unary operand");
+ wasm_stack_push(c, &stack, vt);
break;
}
if (wasm_fp_binop_kind(in->kind, &vt)) {
- if (depth < 2 || stack[depth - 1u] != vt ||
- stack[depth - 2u] != vt)
- wasm_error(c, wasm_loc(0, 0), "wasm: fp operand type mismatch");
- depth--;
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp operand");
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp operand");
+ wasm_stack_push(c, &stack, vt);
break;
}
if (wasm_fp_cmp_kind(in->kind, &vt)) {
- if (depth < 2 || stack[depth - 1u] != vt ||
- stack[depth - 2u] != vt)
- wasm_error(c, wasm_loc(0, 0), "wasm: fp compare type mismatch");
- stack[depth - 2u] = WASM_VAL_I32;
- depth--;
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp compare");
+ wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp compare");
+ wasm_stack_push(c, &stack, WASM_VAL_I32);
break;
}
if (wasm_conversion_kind(in->kind, &src, &dst)) {
- if (depth < 1 || stack[depth - 1u] != src)
- wasm_error(c, wasm_loc(0, 0), "wasm: conversion type mismatch");
- stack[depth - 1u] = dst;
+ wasm_stack_pop(c, &stack, control, ncontrol, src, "conversion");
+ wasm_stack_push(c, &stack, dst);
break;
}
{
CfreeCgIntCmpOp cmp;
- if (depth < 2)
- wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow");
- if (stack[depth - 1u] != stack[depth - 2u])
- wasm_error(c, wasm_loc(0, 0), "wasm: operand type mismatch");
- if (wasm_int_cmp_op(in->kind, &cmp))
- stack[depth - 2u] = WASM_VAL_I32;
- depth--;
- break;
+ WasmValType rhs, lhs;
+ rhs = stack.depth > control[ncontrol - 1u].height
+ ? stack.vals[stack.depth - 1u]
+ : WASM_VAL_I32;
+ wasm_stack_pop(c, &stack, control, ncontrol, 0, "operand");
+ lhs = stack.depth > control[ncontrol - 1u].height
+ ? stack.vals[stack.depth - 1u]
+ : rhs;
+ wasm_stack_pop(c, &stack, control, ncontrol, rhs, "operand");
+ if (lhs != rhs)
+ wasm_error(c, wasm_loc(0, 0), "wasm: operand type mismatch");
+ wasm_stack_push(c, &stack,
+ wasm_int_cmp_op(in->kind, &cmp) ? WASM_VAL_I32
+ : lhs);
+ break;
}
}
}
- if (control_depth)
+ if (ncontrol != 1u)
wasm_error(c, wasm_loc(0, 0), "wasm: unterminated control block");
- if (f->nresults && depth < 1)
- wasm_error(c, wasm_loc(0, 0), "wasm: missing function result");
- if (f->nresults && stack[depth - 1u] != f->results[0])
- wasm_error(c, wasm_loc(0, 0), "wasm: function result type mismatch");
+ if (!control[0].unreachable) {
+ if (f->nresults) {
+ if (stack.depth != 1u || stack.vals[0] != f->results[0])
+ wasm_error(c, wasm_loc(0, 0), "wasm: function result type mismatch");
+ } else if (stack.depth != 0) {
+ wasm_error(c, wasm_loc(0, 0), "wasm: function leaves extra values");
+ }
+ }
}
}
@@ -3136,6 +4297,20 @@ static void wasm_emit_cg(CfreeCompiler *c, const CfreeCompileOptions *opts,
wasm_error(c, wasm_loc(0, 0), "wasm: failed to initialize codegen");
if (m->nfuncs > 64u)
wasm_error(c, wasm_loc(0, 0), "wasm: too many functions");
+ if (m->ntables || m->nelems)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: tables and call_indirect are not lowered yet");
+ if (m->nglobals)
+ wasm_error(c, wasm_loc(0, 0), "wasm: globals are not lowered yet");
+ if (m->has_start)
+ wasm_error(c, wasm_loc(0, 0), "wasm: start functions are not lowered yet");
+ if (m->has_memory && m->memory.is_import)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: imported memories are not lowered yet");
+ for (i = 0; i < m->nfuncs; ++i)
+ if (m->funcs[i].is_import)
+ wasm_error(c, wasm_loc(0, 0),
+ "wasm: imported functions are not lowered yet");
memset(syms, 0, sizeof syms);
memset(func_types, 0, sizeof func_types);
if (m->has_memory) {
@@ -3775,9 +4950,9 @@ static void encode_section(CfreeHeap *h, CfreeWriter *out, uint8_t id,
static void enc_type(CfreeWriter *w, const WasmModule *m) {
uint32_t i, j;
- write_uleb(w, m->nfuncs);
- for (i = 0; i < m->nfuncs; ++i) {
- const WasmFunc *f = &m->funcs[i];
+ write_uleb(w, m->ntypes);
+ for (i = 0; i < m->ntypes; ++i) {
+ const WasmFuncType *f = &m->types[i];
write_byte(w, 0x60);
write_uleb(w, f->nparams);
for (j = 0; j < f->nparams; ++j)
@@ -3788,25 +4963,101 @@ static void enc_type(CfreeWriter *w, const WasmModule *m) {
}
}
-static void enc_func(CfreeWriter *w, const WasmModule *m) {
- uint32_t i;
- write_uleb(w, m->nfuncs);
- for (i = 0; i < m->nfuncs; ++i)
- write_uleb(w, i);
+static void write_limits(CfreeWriter *w, uint32_t min, uint32_t max,
+ int has_max) {
+ write_byte(w, has_max ? 1 : 0);
+ write_uleb(w, min);
+ if (has_max)
+ write_uleb(w, max);
}
-static void enc_export(CfreeWriter *w, const WasmModule *m) {
+static void enc_import(CfreeWriter *w, const WasmModule *m) {
uint32_t i, n = 0;
for (i = 0; i < m->nfuncs; ++i)
- if (m->funcs[i].export_name)
+ if (m->funcs[i].is_import)
+ n++;
+ for (i = 0; i < m->ntables; ++i)
+ if (m->tables[i].is_import)
+ n++;
+ if (m->has_memory && m->memory.is_import)
+ n++;
+ for (i = 0; i < m->nglobals; ++i)
+ if (m->globals[i].is_import)
n++;
write_uleb(w, n);
for (i = 0; i < m->nfuncs; ++i) {
- if (!m->funcs[i].export_name)
+ const WasmFunc *f = &m->funcs[i];
+ if (!f->is_import)
continue;
- write_name(w, m->funcs[i].export_name);
+ write_name(w, f->import_module ? f->import_module : "");
+ write_name(w, f->import_name ? f->import_name : "");
write_byte(w, 0);
- write_uleb(w, i);
+ write_uleb(w, f->typeidx);
+ }
+ for (i = 0; i < m->ntables; ++i) {
+ const WasmTable *t = &m->tables[i];
+ if (!t->is_import)
+ continue;
+ write_name(w, t->import_module ? t->import_module : "");
+ write_name(w, t->import_name ? t->import_name : "");
+ write_byte(w, 1);
+ write_byte(w, (uint8_t)t->elem_type);
+ write_limits(w, t->min, t->max, t->has_max);
+ }
+ if (m->has_memory && m->memory.is_import) {
+ write_name(w, m->memory.import_module ? m->memory.import_module : "");
+ write_name(w, m->memory.import_name ? m->memory.import_name : "");
+ write_byte(w, 2);
+ write_limits(w, m->memory.min_pages, m->memory.max_pages,
+ m->memory.has_max);
+ }
+ for (i = 0; i < m->nglobals; ++i) {
+ const WasmGlobal *g = &m->globals[i];
+ if (!g->is_import)
+ continue;
+ write_name(w, g->import_module ? g->import_module : "");
+ write_name(w, g->import_name ? g->import_name : "");
+ write_byte(w, 3);
+ write_byte(w, (uint8_t)g->type);
+ write_byte(w, g->mutable_);
+ }
+}
+
+static int wasm_module_has_imports(const WasmModule *m) {
+ uint32_t i;
+ for (i = 0; i < m->nfuncs; ++i)
+ if (m->funcs[i].is_import)
+ return 1;
+ for (i = 0; i < m->ntables; ++i)
+ if (m->tables[i].is_import)
+ return 1;
+ if (m->has_memory && m->memory.is_import)
+ return 1;
+ for (i = 0; i < m->nglobals; ++i)
+ if (m->globals[i].is_import)
+ return 1;
+ return 0;
+}
+
+static void enc_func(CfreeWriter *w, const WasmModule *m) {
+ uint32_t i;
+ uint32_t n = 0;
+ for (i = 0; i < m->nfuncs; ++i)
+ if (!m->funcs[i].is_import)
+ n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->nfuncs; ++i)
+ if (!m->funcs[i].is_import)
+ write_uleb(w, m->funcs[i].typeidx);
+}
+
+static void enc_export(CfreeWriter *w, const WasmModule *m) {
+ uint32_t i;
+ write_uleb(w, m->nexports);
+ for (i = 0; i < m->nexports; ++i) {
+ write_name(w, m->exports[i].name ? m->exports[i].name : "");
+ write_byte(w, m->exports[i].kind);
+ write_uleb(w, m->exports[i].index);
}
}
@@ -3822,6 +5073,47 @@ static void enc_memory(CfreeWriter *w, const WasmModule *m) {
write_uleb(w, m->memory.max_pages);
}
+static uint8_t wasm_opcode(uint8_t kind);
+
+static void enc_table(CfreeWriter *w, const WasmModule *m) {
+ uint32_t i, n = 0;
+ for (i = 0; i < m->ntables; ++i)
+ if (!m->tables[i].is_import)
+ n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->ntables; ++i) {
+ const WasmTable *t = &m->tables[i];
+ if (t->is_import)
+ continue;
+ write_byte(w, (uint8_t)t->elem_type);
+ write_limits(w, t->min, t->max, t->has_max);
+ }
+}
+
+static void enc_global(CfreeWriter *w, const WasmModule *m) {
+ uint32_t i, n = 0;
+ for (i = 0; i < m->nglobals; ++i)
+ if (!m->globals[i].is_import)
+ n++;
+ write_uleb(w, n);
+ for (i = 0; i < m->nglobals; ++i) {
+ const WasmGlobal *g = &m->globals[i];
+ if (g->is_import)
+ continue;
+ write_byte(w, (uint8_t)g->type);
+ write_byte(w, g->mutable_);
+ write_byte(w, wasm_opcode(g->init.kind));
+ if (g->init.kind == WASM_INSN_I32_CONST ||
+ g->init.kind == WASM_INSN_I64_CONST)
+ write_sleb(w, g->init.imm);
+ else if (g->init.kind == WASM_INSN_F32_CONST)
+ write_f32(w, g->init.fp);
+ else if (g->init.kind == WASM_INSN_F64_CONST)
+ write_f64(w, g->init.fp);
+ write_byte(w, 0x0b);
+ }
+}
+
static uint8_t wasm_opcode(uint8_t kind) {
switch (kind) {
case WASM_INSN_UNREACHABLE:
@@ -4120,11 +5412,19 @@ static uint8_t wasm_opcode(uint8_t kind) {
static void enc_code(CfreeWriter *w, const WasmModule *m) {
uint32_t i, j;
- write_uleb(w, m->nfuncs);
+ uint32_t n = 0;
+ for (i = 0; i < m->nfuncs; ++i)
+ if (!m->funcs[i].is_import)
+ n++;
+ write_uleb(w, n);
for (i = 0; i < m->nfuncs; ++i) {
CfreeWriter *body = cfree_writer_mem(m->heap);
size_t len;
const uint8_t *bytes;
+ if (m->funcs[i].is_import) {
+ cfree_writer_close(body);
+ continue;
+ }
if (m->funcs[i].nlocals) {
uint32_t group_count = 0;
WasmValType prev = 0;
@@ -4202,19 +5502,67 @@ static void enc_data(CfreeWriter *w, const WasmModule *m) {
w->write(w, m->memory.data, m->memory.data_len);
}
+static void enc_elem(CfreeWriter *w, const WasmModule *m) {
+ uint32_t i, j;
+ write_uleb(w, m->nelems);
+ for (i = 0; i < m->nelems; ++i) {
+ const WasmElemSegment *e = &m->elems[i];
+ write_uleb(w, 0);
+ write_byte(w, 0x41);
+ write_sleb(w, e->offset);
+ write_byte(w, 0x0b);
+ write_uleb(w, e->nfuncs);
+ for (j = 0; j < e->nfuncs; ++j)
+ write_uleb(w, e->funcs[j]);
+ }
+}
+
+static void enc_start(CfreeWriter *w, const WasmModule *m) {
+ write_uleb(w, m->start_func);
+}
+
+static void encode_custom(CfreeHeap *h, CfreeWriter *out,
+ const WasmCustom *cs) {
+ CfreeWriter *tmp = cfree_writer_mem(h);
+ size_t len;
+ const uint8_t *bytes;
+ if (!tmp)
+ return;
+ write_name(tmp, cs->name ? cs->name : "");
+ if (cs->len)
+ tmp->write(tmp, cs->data, cs->len);
+ bytes = cfree_writer_mem_bytes(tmp, &len);
+ write_byte(out, 0);
+ write_uleb(out, len);
+ out->write(out, bytes, len);
+ cfree_writer_close(tmp);
+}
+
static void wasm_encode(CfreeCompiler *c, const WasmModule *m,
CfreeWriter *out) {
static const uint8_t magic[] = {0x00, 0x61, 0x73, 0x6d,
0x01, 0x00, 0x00, 0x00};
CfreeHeap *h = cfree_compiler_heap(c);
out->write(out, magic, sizeof magic);
+ for (uint32_t i = 0; i < m->ncustoms; ++i)
+ encode_custom(h, out, &m->customs[i]);
encode_section(h, out, 1, enc_type, m);
+ if (wasm_module_has_imports(m))
+ encode_section(h, out, 2, enc_import, m);
encode_section(h, out, 3, enc_func, m);
- if (m->has_memory)
+ if (m->ntables)
+ encode_section(h, out, 4, enc_table, m);
+ if (m->has_memory && !m->memory.is_import)
encode_section(h, out, 5, enc_memory, m);
+ if (m->nglobals)
+ encode_section(h, out, 6, enc_global, m);
encode_section(h, out, 7, enc_export, m);
+ if (m->has_start)
+ encode_section(h, out, 8, enc_start, m);
+ if (m->nelems)
+ encode_section(h, out, 9, enc_elem, m);
encode_section(h, out, 10, enc_code, m);
- if (m->has_memory)
+ if (m->has_memory && m->memory.data_len)
encode_section(h, out, 11, enc_data, m);
}
diff --git a/test/wasm/cases/control_loop.wat b/test/wasm/cases/control_loop.wat
@@ -4,7 +4,7 @@
(local.set $i (i32.const 0))
(block
(loop
- (local.tee $i
+ (local.set $i
(i32.add
(local.get $i)
(i32.const 1)))
diff --git a/test/wasm/err/bad_index.wat b/test/wasm/err/bad_index.wat
@@ -0,0 +1,3 @@
+(module
+ (func (export "test_main") (result i32)
+ local.get 0))
diff --git a/test/wasm/err/br_table_depth.wat b/test/wasm/err/br_table_depth.wat
@@ -0,0 +1,4 @@
+(module
+ (func (export "test_main")
+ i32.const 0
+ br_table 0))
diff --git a/test/wasm/err/global_init_type.wat b/test/wasm/err/global_init_type.wat
@@ -0,0 +1,4 @@
+(module
+ (global i32 (i64.const 1))
+ (func (export "test_main") (result i32)
+ i32.const 0))
diff --git a/test/wasm/err/result_shape.wat b/test/wasm/err/result_shape.wat
@@ -0,0 +1,2 @@
+(module
+ (func (export "test_main") (result i32)))
diff --git a/test/wasm/err/stack_underflow.wat b/test/wasm/err/stack_underflow.wat
@@ -0,0 +1,3 @@
+(module
+ (func (export "test_main") (result i32)
+ i32.add))
diff --git a/test/wasm/err/start_sig.wat b/test/wasm/err/start_sig.wat
@@ -0,0 +1,6 @@
+(module
+ (func $start (result i32)
+ i32.const 0)
+ (start $start)
+ (func (export "test_main") (result i32)
+ i32.const 0))
diff --git a/test/wasm/err/table_ref_type.wat b/test/wasm/err/table_ref_type.wat
@@ -0,0 +1,4 @@
+(module
+ (table 1 externref)
+ (func (export "test_main") (result i32)
+ i32.const 0))
diff --git a/test/wasm/err/unsupported_opcode.wat b/test/wasm/err/unsupported_opcode.wat
@@ -0,0 +1,4 @@
+(module
+ (func (export "test_main") (result i32)
+ i32.const 0
+ i32.atomic.load))
diff --git a/test/wasm/meta/import_table_global_start.wat b/test/wasm/meta/import_table_global_start.wat
@@ -0,0 +1,10 @@
+(module
+ (type $binop (func (param i32 i32) (result i32)))
+ (import "host" "add" (func $add (type $binop)))
+ (table 1 funcref)
+ (global $g i32 (i32.const 7))
+ (elem (i32.const 0) func $add)
+ (func $start)
+ (start $start)
+ (func (export "test_main") (result i32)
+ i32.const 0))
diff --git a/test/wasm/meta/type_custom.wat b/test/wasm/meta/type_custom.wat
@@ -0,0 +1,6 @@
+(module
+ (custom "cfree.test" "metadata")
+ (type $ret_i32 (func (result i32)))
+ (func (type $ret_i32)
+ i32.const 0)
+ (export "test_main" (func 0)))
diff --git a/test/wasm/run.sh b/test/wasm/run.sh
@@ -4,6 +4,8 @@ set -u
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
BUILD_DIR="$ROOT/build/test/wasm"
CASES_DIR="$ROOT/test/wasm/cases"
+ERR_DIR="$ROOT/test/wasm/err"
+META_DIR="$ROOT/test/wasm/meta"
CFREE_BIN="${CFREE:-$ROOT/build/cfree}"
WASM_TOOL="$ROOT/build/test/wasm-tool"
JIT_RUNNER="$ROOT/build/test/jit-runner"
@@ -109,6 +111,16 @@ run_expect_zero() {
fi
}
+run_expect_fail() {
+ local label=$1
+ shift
+ if "$@" >"$BUILD_DIR/${label//\//_}.out" 2>"$BUILD_DIR/${label//\//_}.err"; then
+ note_fail "$label expected failure"
+ else
+ note_pass "$label"
+ fi
+}
+
printf 'test-wasm-front target=%s obj=%s\n' "$target_triple" "$TEST_OBJ"
for wat in "$CASES_DIR"/*.wat; do
@@ -169,5 +181,40 @@ for wat in "$CASES_DIR"/*.wat; do
fi
done
+for wat in "$ERR_DIR"/*.wat; do
+ [ -e "$wat" ] || continue
+ name=$(basename "$wat" .wat)
+ run_expect_fail "err/$name/cc" "$CFREE_BIN" cc -target "$target_triple" -c \
+ "$wat" -o "$BUILD_DIR/err-$name.o"
+done
+
+if [ "$have_wasm_tool" -eq 1 ]; then
+ for wat in "$META_DIR"/*.wat; do
+ [ -e "$wat" ] || continue
+ name=$(basename "$wat" .wat)
+ wasm="$BUILD_DIR/meta-$name.wasm"
+ run_expect_zero "meta/$name/W" "$WASM_TOOL" --wat2wasm "$wat" "$wasm"
+ if [ "$name" = "type_custom" ]; then
+ run_expect_zero "meta/$name/native" "$CFREE_BIN" cc \
+ -target "$target_triple" -c "$wasm" -o "$BUILD_DIR/meta-$name.o"
+ else
+ run_expect_fail "meta/$name/native" "$CFREE_BIN" cc \
+ -target "$target_triple" -c "$wasm" -o "$BUILD_DIR/meta-$name.o"
+ fi
+ done
+else
+ note_skip "meta" "no wasm-tool"
+fi
+
+bad_wasm="$BUILD_DIR/malformed-section.wasm"
+printf '\000asm\001\000\000\000\001\005\001\140' > "$bad_wasm"
+run_expect_fail "err/malformed-section/wasm" "$CFREE_BIN" cc \
+ -target "$target_triple" -c "$bad_wasm" -o "$BUILD_DIR/bad-section.o"
+
+bad_leb="$BUILD_DIR/malformed-leb.wasm"
+printf '\000asm\001\000\000\000\001\200\200\200\200\020' > "$bad_leb"
+run_expect_fail "err/malformed-leb/wasm" "$CFREE_BIN" cc \
+ -target "$target_triple" -c "$bad_leb" -o "$BUILD_DIR/bad-leb.o"
+
printf 'test-wasm-front: pass=%d fail=%d skip=%d\n' "$pass" "$fail" "$skip"
[ "$fail" -eq 0 ]