diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-09-07 00:04:38 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-11-06 14:38:40 +0100 |
| commit | 6d9d80362f9932bbc87e162b8ef7df06c73e27e1 (patch) | |
| tree | e3e228c63fe26f07503f226de7fb5086b3dc2286 /src/routines | |
| download | nanowasm-6d9d80362f9932bbc87e162b8ef7df06c73e27e1.tar.gz | |
First commit
Diffstat (limited to 'src/routines')
45 files changed, 5588 insertions, 0 deletions
diff --git a/src/routines/CMakeLists.txt b/src/routines/CMakeLists.txt new file mode 100644 index 0000000..05c055b --- /dev/null +++ b/src/routines/CMakeLists.txt @@ -0,0 +1,32 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + arithm.c + break.c + call.c + call_function.c + call_import.c + call_indirect.c + check_magic.c + check_version.c + find_function.c + find_local.c + find_param.c + get_function_type.c + get_import_type.c + init_data.c + init_globals.c + mem_imm.c + section.c + set_local.c + start_block.c + unary.c + unwind.c +) + +add_subdirectory(section) diff --git a/src/routines/arithm.c b/src/routines/arithm.c new file mode 100644 index 0000000..6ab4799 --- /dev/null +++ b/src/routines/arithm.c @@ -0,0 +1,119 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_arithm *const s = &i->sm.arithm; + const enum nw_state n = nwp_stack_push(i, &s->io); + + if (n) + return n; + + i->push_type = s->type; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static int init_io(struct nw_interp *const i) +{ + struct nw_i_sm_arithm *const s = &i->sm.arithm; + size_t sz; + + if (nwp_type_sz(s->type, &sz)) + { + static const char *const exc = "invalid operand type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)s->type); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &s->value; + io.n = sz; + s->io = io; + } + + return 0; +} + +static enum nw_state pop_left(struct nw_interp *const i) +{ + struct nw_i_sm_arithm *const s = &i->sm.arithm; + const enum nw_state n = nwp_stack_pop(i, &s->io); + + if (n) + return n; + + s->out.left = s->value; + + if (s->op(&s->out, &s->value)) + { + static const char *const exc = "arithmetic operation failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if (init_io(i)) + return NW_FATAL; + + i->next = push; + return NW_AGAIN; +} + +static enum nw_state pop_right(struct nw_interp *const i) +{ + struct nw_i_sm_arithm *const s = &i->sm.arithm; + const enum nw_state n = nwp_stack_pop(i, &s->io); + + if (n) + return n; + else if (init_io(i)) + return NW_FATAL; + + s->out.right = s->value; + i->next = pop_left; + return NW_AGAIN; +} + +static enum nw_state prepare_right(struct nw_interp *const i) +{ + if (init_io(i)) + return NW_FATAL; + + i->next = pop_right; + return NW_AGAIN; +} + +void nwp_arithm(struct nw_interp *const i, const enum nw_type t, + int (*const op)(const struct nw_i_sm_arithm_out *, union nw_value *)) +{ + const struct nw_i_sm_arithm s = {0}; + struct nw_i_sm_arithm *const ps = &i->sm.arithm; + + *ps = s; + ps->type = t; + ps->op = op; + i->next = prepare_right; +} diff --git a/src/routines/break.c b/src/routines/break.c new file mode 100644 index 0000000..3ee94a1 --- /dev/null +++ b/src/routines/break.c @@ -0,0 +1,243 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_pc(struct nw_interp *); + +static enum nw_state seek_dst(struct nw_interp *const i) +{ + const struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const unsigned long dst = nwp_leuint32(&b->dst); + const enum nw_state n = cfg->seek(dst, cfg->user); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state skip(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const unsigned long n_labels = nwp_leuint32(&b->n); + + if (++b->label_i >= n_labels) + { + static const char *const exc = "no label found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s from offset: %ld\n", exc, b->pc); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->lpc; + io.n = sizeof b->lpc; + b->io = io; + i->next = get_pc; + } + + return NW_AGAIN; +} + +static enum nw_state get_dst(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const unsigned long pc = nwp_leuint32(&b->lpc); + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + else if (pc == b->pc) + i->next = seek_dst; + else + return skip(i); + + return NW_AGAIN; +} + +static enum nw_state get_pc(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->dst; + io.n = sizeof b->dst; + b->io = io; + i->next = get_dst; + } + + return NW_AGAIN; +} + +static enum nw_state get_n(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + else if (!nwp_leuint32(&b->n)) + { + static const char *const exc = "function has no labels"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->lpc; + io.n = sizeof b->lpc; + b->io = io; + } + + i->next = get_pc; + return NW_AGAIN; +} + +static enum nw_state seek_start(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const unsigned long offset = nwp_leuint32(&b->offset); + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->n; + io.n = sizeof b->n; + b->io = io; + i->next = get_n; + } + + return NW_AGAIN; +} + +static enum nw_state get_start(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user); + + if (n) + return n; + + i->next = seek_start; + return NW_AGAIN; +} + +static enum nw_state seek_lo(struct nw_interp *const i) +{ + const struct nw_mod *const m = i->cfg.m; + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + long offset = m->c_sections[NW_CUSTOM_LO]; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "nw_lo section not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if (i->fr.fn.index < m->import_count) + { + static const char *const exc = "invalid function index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)i->fr.fn.index); +#endif + return NW_FATAL; + } + + offset += sizeof b->offset * (i->fr.fn.index - m->import_count); + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &b->offset; + io.n = sizeof b->offset; + b->io = io; + i->next = get_start; + } + + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_break *const b = &i->sm.brk; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&b->pc, cfg->user); + + if (n) + return n; + else if (i->fr.block_i < b->relative_depth) + { + static const char *const exc = "relative depth exceeds block level"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, rdepth: %lu, blvl: %lu\n", exc, + (unsigned long)i->fr.block_i, (unsigned long)b->relative_depth); +#endif + return NW_FATAL; + } + + i->fr.block_i -= b->relative_depth; + i->next = seek_lo; + return NW_AGAIN; +} + +void nwp_break(struct nw_interp *const i, const nw_varuint32 relative_depth) +{ + const struct nw_i_sm_break b = {0}; + struct nw_i_sm_break *const pb = &i->sm.brk; + + *pb = b; + pb->relative_depth = relative_depth; + i->next = tell; +} diff --git a/src/routines/call.c b/src/routines/call.c new file mode 100644 index 0000000..d564344 --- /dev/null +++ b/src/routines/call.c @@ -0,0 +1,17 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> + +void nwp_call(struct nw_interp *const i, const nw_varuint32 index) +{ + index >= i->cfg.m->import_count ? + nwp_call_function(i, index) : nwp_call_import(i, index); +} diff --git a/src/routines/call_function.c b/src/routines/call_function.c new file mode 100644 index 0000000..eeee2fc --- /dev/null +++ b/src/routines/call_function.c @@ -0,0 +1,242 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state get_entry_count(struct nw_interp *); + +static enum nw_state push_frame(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + const enum nw_state n = nwp_stack_push(i, &pl->io); + + if (n) + return n; + + i->fr = pl->fr; +#ifdef NW_LOG + nwp_log("entering function %lu\n", (unsigned long)i->fr.fn.index); +#endif + nwp_interp_resume(i); + return NW_AGAIN; +} + +static void init(struct nw_i_sm_pl *const pl) +{ + union nw_value *const v = &pl->value; + struct nw_sm_io io = {0}; + + io.buf = v; + + switch (pl->meta.type) + { + case NW_TYPE_I32: + v->i32 = 0; + io.n = sizeof v->i32; + break; + + case NW_TYPE_I64: + v->i64.low = 0; + v->i64.hi = 0; + io.n = sizeof v->i64; + break; + + case NW_TYPE_F32: + v->f32 = 0; + io.n = sizeof v->f32; + break; + + case NW_TYPE_F64: + v->f64 = 0; + io.n = sizeof v->f64; + break; + } + + pl->io = io; +} + +static enum nw_state get_code_start(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_frame *const fr = &pl->fr; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&fr->start, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &i->fr; + io.n = sizeof i->fr; + pl->io = io; + fr->local_end = nwp_stack_ptr(i); + fr->body_size -= fr->start - pl->body_start; + i->next = push_frame; + } + + return NW_AGAIN; +} + +static enum nw_state push_local(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + const enum nw_state n = nwp_stack_push(i, &pl->io); + + if (n) + return n; + else if (++pl->entry_i >= pl->meta.entry_count) + i->next = ++pl->local_i >= pl->local_count ? + get_code_start : get_entry_count; + else + init(pl); + + return NW_AGAIN; +} + +static enum nw_state push_meta(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + const enum nw_state n = nwp_stack_push(i, &pl->io); + + if (n) + return n; + + init(pl); + i->next = push_local; + return NW_AGAIN; +} + +static enum nw_state get_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_sm_leb128 *const l = &pl->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + + if (n) + return n; + else if (nwp_get_type(type, &pl->meta.type)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &pl->meta; + io.n = sizeof pl->meta; + pl->io = io; + i->next = push_meta; + } + + return NW_AGAIN; +} + +static enum nw_state get_entry_count(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_sm_leb128 *const l = &pl->leb128; + nw_varuint32 *const count = &pl->meta.entry_count; + struct nw_frame *const fr = &pl->fr; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, count, cfg->user); + + if (n) + return n; + + pl->entry_i = 0; + fr->local_count += *count; + i->next = get_type; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_sm_leb128 *const l = &pl->leb128; + struct nw_frame *const fr = &pl->fr; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &pl->local_count, cfg->user); + + if (n) + return n; + + fr->local_start = nwp_stack_ptr(i); + i->next = pl->local_count ? get_entry_count : get_code_start; + return NW_AGAIN; +} + +static enum nw_state get_body_start(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&pl->body_start, cfg->user); + + if (n) + return n; + + i->next = get_count; + return NW_AGAIN; +} + +static enum nw_state get_body_len(struct nw_interp *const i) +{ + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_sm_leb128 *const l = &pl->leb128; + struct nw_frame *const fr = &pl->fr; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &fr->body_size, cfg->user); + + if (n) + return n; + + i->next = get_body_start; + return NW_AGAIN; +} + +static void begin(struct nw_interp *const i) +{ + const struct nw_i_sm_pl l = {0}; + const struct nw_fn fn = i->sm.ffn.fn; + struct nw_i_sm_pl *const pl = &i->sm.pl; + struct nw_frame *const fr = &pl->fr; + + *pl = l; + fr->child = 1; + fr->fn = fn; + fr->fr_start = nwp_stack_ptr(i); + i->next = get_body_len; +} + +static enum nw_state find(struct nw_interp *const i) +{ + nwp_find_function(i, &i->sm.type.out, begin); + return NW_AGAIN; +} + +void nwp_call_function(struct nw_interp *const i, const nw_varuint32 index) +{ + nwp_get_function_type(i, index, find); +} diff --git a/src/routines/call_import.c b/src/routines/call_import.c new file mode 100644 index 0000000..63a736e --- /dev/null +++ b/src/routines/call_import.c @@ -0,0 +1,477 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> +#include <stddef.h> +#include <string.h> + +static enum nw_state seek_param_types(struct nw_interp *); + +static enum nw_state seek_pc(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(ci->pc, cfg->user); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state push_return(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const enum nw_state n = nwp_stack_push(i, &ci->io); + + if (n) + return n; + + i->next = seek_pc; + return NW_AGAIN; +} + +static int parse_signature(const struct nw_import *const imp, + size_t *const nargs, int *const ret) +{ + enum + { + RETURN, + PARAM_BEGIN, + PARAM, + END + } state = 0; + + const char *s; + + if (nargs) + *nargs = 0; + + if (ret) + *ret = 0; + + for (s = imp->u.function.signature; *s; s++) + { + switch (state) + { + case RETURN: + if (strchr("iIfF", *s)) + { + if (ret) + *ret = 1; + + state++; + } + else if (*s == '(') + state = PARAM; + + break; + + case PARAM_BEGIN: + if (*s != '(') + return -1; + + state++; + break; + + case PARAM: + if (strchr("iIfF", *s)) + { + if (nargs) + (*nargs)++; + } + else if (*s == ')') + state++; + else + return -1; + + break; + + case END: + goto end; + } + } + +end: + return state != END || *s; +} + +static int check_ret(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_import *const imp = ci->imp; + const struct nw_fn *const fn = &ci->fn; + const nw_varuint1 retcnt = fn->ret.count; + int hasret; + + if (parse_signature(imp, NULL, &hasret)) + { + static const char *const exc = "invalid imported function signature"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s::%s: %s: %s\n", imp->module, imp->field, exc, + imp->u.function.signature); +#endif + return NW_FATAL; + } + else if (hasret != retcnt) + { + static const char *const exc = "return count mismatch"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s::%s: %s: expected: %d, got: %d\n", imp->module, imp->field, + exc, hasret, retcnt); +#endif + return -1; + } + + return 0; +} + +static enum nw_state call_done(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_fn *const fn = &ci->fn; + const nw_varuint32 pcnt = fn->param_count; + const struct nw_interp_cfg *const icfg = &i->cfg; + union nw_value *const ret = fn->ret.count ? icfg->args + pcnt : NULL; + + if (ret) + { + size_t sz; + + if (nwp_type_sz(fn->ret.type, &sz)) + { + static const char *const exc = "invalid return type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)fn->ret.type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = ret; + io.n = sz; + ci->io = io; + i->next = push_return; + } + } + else + nwp_interp_resume(i); + + return NW_AGAIN; +} + +static enum nw_state repeat(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + struct nw_next *const next = &ci->next; + const struct nw_import *const imp = ci->imp; + const enum nw_state n = next->fn(next->user, next); + + if (n == NW_FATAL) + { + static const char *const exc = "import function failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s::%s\n", exc, imp->module, imp->field); +#endif + return NW_FATAL; + } + else if (n) + return n; + + return call_done(i); +} + +static enum nw_state call(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_fn *const fn = &ci->fn; + const nw_varuint32 pcnt = fn->param_count; + const struct nw_interp_cfg *const icfg = &i->cfg; + const union nw_value *const args = pcnt ? icfg->args : NULL; + union nw_value *const ret = fn->ret.count ? icfg->args + pcnt : NULL; + const struct nw_import *const imp = ci->imp; + enum nw_state n; + + if (fn->ret.count && check_ret(i)) + return NW_FATAL; + else if ((n = imp->u.function.fn(args, ret, icfg->user, &ci->next))) + { + if (n == NW_FATAL) + { + static const char *const exc = "import function failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s::%s\n", exc, imp->module, imp->field); +#endif + } + else if (ci->next.fn) + i->next = repeat; + + return n; + } + + return call_done(i); +} + +static enum nw_state pop_args(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const enum nw_state n = nwp_stack_pop(i, &ci->io); + + if (n) + return n; + + i->next = call; + return NW_AGAIN; +} + +static enum nw_state get_value(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const enum nw_state n = nwp_stack_read(i, &ci->io, ci->addr); + const struct nw_import *const imp = ci->imp; + const size_t max = i->cfg.n_args; + const nw_varuint32 pcnt = ci->fn.param_count; + size_t nargs; + + if (n) + return n; + else if (parse_signature(imp, &nargs, NULL)) + { + static const char *const exc = "invalid imported function signature"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s::%s: %s: %s\n", imp->module, imp->field, exc, + imp->u.function.signature); +#endif + return NW_FATAL; + } + else if (nargs > max) + { + static const char *const exc = "exceeded maximum args"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, %s::%s requests %lu, max: %lu\n", exc, imp->module, + imp->field, (unsigned long)nargs, (unsigned long)max); +#endif + return NW_FATAL; + } + else if (pcnt != nargs) + { + static const char *const exc = "arg number mismatch"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s, %s::%s expected %lu args, got %lu\n", exc, imp->module, + imp->field, (unsigned long)nargs, (unsigned long)pcnt); +#endif + return NW_FATAL; + } + + i->cfg.args[ci->param_i++] = ci->value; + + if (ci->param_i >= ci->fn.param_count) + { + struct nw_sm_io io = {0}; + + io.n = ci->sz; + ci->io = io; + i->next = pop_args; + } + else + i->next = seek_param_types; + + return NW_AGAIN; +} + +static enum nw_state get_param_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + struct nw_sm_leb128 *const l = &ci->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + enum nw_type vtype; + size_t sz; + + if (n) + return n; + else if (nwp_get_type(type, &vtype) || nwp_type_sz(vtype, &sz)) + { + static const char *const exc = "invalid param type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + else if (ci->type_i >= ci->param_i) + { + if (ci->addr < sz) + { + static const char *const exc = "stack underflow"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + if (ci->type_i == ci->param_i) + ci->type = vtype; + + ci->addr -= sz; + } + + ci->sz += sz; + + if (++ci->type_i >= ci->fn.param_count) + { + struct nw_sm_io io = {0}; + + if (nwp_type_sz(ci->type, &sz)) + { + static const char *const exc = "invalid param type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + io.buf = &ci->value; + io.n = sz; + ci->io = io; + i->next = get_value; + } + + return NW_AGAIN; +} + +static enum nw_state seek_param_types(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(ci->fn.param_types, cfg->user); + + if (n) + return n; + + ci->type_i = 0; + ci->sz = 0; + ci->addr = nwp_stack_ptr(i); + i->next = get_param_type; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_call_import *const ci = &i->sm.call_import; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&ci->pc, cfg->user); + + if (n) + return n; + + i->next = seek_param_types; + return NW_AGAIN; +} + +static const struct nw_import *find(const struct nw_interp *const i, + const nw_varuint32 index) +{ + const struct nw_mod_cfg *const cfg = &i->cfg.m->cfg; + size_t j; + + for (j = 0; j < cfg->n_imports; j++) + { + const struct nw_import_index *const ii = &cfg->imp_indexes[j]; + + if (ii->index == index) + { + const struct nw_import *const imp = &cfg->imports[j]; + +#ifdef NW_LOG + nwp_log("import function index %lu matches %s::%s (import index %lu)\n", + (unsigned long)index, imp->module, imp->field, (unsigned long)j); +#endif + return &cfg->imports[j]; + } + } + + return NULL; +} + +static enum nw_state prepare(struct nw_interp *const i) +{ + const struct nw_fn fn = i->sm.type.out; + const struct nw_import *const imp = find(i, fn.index); + + if (!imp) + { + static const char *const exc = "function import not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)fn.index); +#endif + return NW_FATAL; + } + else if (imp->kind != NW_KIND_FUNCTION) + { + static const char *const exc = "import not a function"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %s::%s\n", exc, imp->module, imp->field); +#endif + return NW_FATAL; + } + else + { + const struct nw_i_sm_call_import ci = {0}; + struct nw_i_sm_call_import *const pci = &i->sm.call_import; + + *pci = ci; + pci->fn = fn; + pci->imp = imp; + i->next = pci->fn.param_count ? tell : call; + } + + return NW_AGAIN; +} + +void nwp_call_import(struct nw_interp *const i, const nw_varuint32 index) +{ + nwp_get_function_type(i, index, prepare); +} diff --git a/src/routines/call_indirect.c b/src/routines/call_indirect.c new file mode 100644 index 0000000..9500969 --- /dev/null +++ b/src/routines/call_indirect.c @@ -0,0 +1,48 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> + +static enum nw_state get_value(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + m->next = m->sm.code.next; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + m->next = get_value; + return NW_AGAIN; +} + +void nwp_op_check_call_indirect(struct nw_mod *const m) +{ + m->next = get_index; +} diff --git a/src/routines/check_magic.c b/src/routines/check_magic.c new file mode 100644 index 0000000..868d669 --- /dev/null +++ b/src/routines/check_magic.c @@ -0,0 +1,48 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state run(struct nw_mod *const m) +{ + static const unsigned char magic[] = {'\0', 'a', 's', 'm'}; + struct nw_sm_cm *const cm = &m->sm.check_magic; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &cm->io, cfg->user); + + if (n) + return n; + + if (memcmp(cm->buf, magic, sizeof cm->buf)) + { +#ifdef NW_LOG + nwp_log("wrong magic bytes\n"); +#endif + return NW_FATAL; + } + + nwp_check_version(m); + return NW_AGAIN; +} + +void nwp_check_magic(struct nw_mod *const m) +{ + const struct nw_sm_cm cm = {0}; + struct nw_sm_cm *const pcm = &m->sm.check_magic; + struct nw_sm_io *const io = &pcm->io; + + *pcm = cm; + io->buf = &pcm->buf; + io->n = sizeof pcm->buf; + m->next = run; +} diff --git a/src/routines/check_version.c b/src/routines/check_version.c new file mode 100644 index 0000000..4f6ee05 --- /dev/null +++ b/src/routines/check_version.c @@ -0,0 +1,48 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state run(struct nw_mod *const m) +{ + static const unsigned char version[] = {1, 0, 0, 0}; + struct nw_sm_cv *const cv = &m->sm.check_version; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &cv->io, cfg->user); + + if (n) + return n; + + if (memcmp(&cv->version, version, sizeof cv->version)) + { +#ifdef NW_LOG + nwp_log("wrong version bytes\n"); +#endif + return NW_FATAL; + } + + nwp_section(m); + return NW_AGAIN; +} + +void nwp_check_version(struct nw_mod *const m) +{ + const struct nw_sm_cv cv = {0}; + struct nw_sm_cv *const pcv = &m->sm.check_version; + struct nw_sm_io *const io = &pcv->io; + + *pcv = cv; + io->buf = &pcv->version; + io->n = sizeof pcv->version; + m->next = run; +} diff --git a/src/routines/find_function.c b/src/routines/find_function.c new file mode 100644 index 0000000..c0273ec --- /dev/null +++ b/src/routines/find_function.c @@ -0,0 +1,100 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state seek_code(struct nw_interp *const i) +{ + const struct nw_i_sm_ffn *const f = &i->sm.ffn; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const unsigned long fbo = nwp_leuint32(&f->fbo); + const enum nw_state n = cfg->seek(fbo, cfg->user); + + if (n) + return n; + + f->next(i); + return NW_AGAIN; +} + +static enum nw_state get_fbo(struct nw_interp *const i) +{ + struct nw_i_sm_ffn *const f = &i->sm.ffn; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &f->io, cfg->user); + + if (n) + return n; + + i->next = seek_code; + return NW_AGAIN; +} + +static enum nw_state seek_fbo(struct nw_interp *const i) +{ + struct nw_i_sm_ffn *const f = &i->sm.ffn; + const struct nw_mod *const m = i->cfg.m; + long offset = m->c_sections[NW_CUSTOM_FBO]; + const struct nw_io_cfg *const cfg = &i->cfg.io; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "nw_fbo section not found"; + +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + i->exception = exc; + return NW_FATAL; + } + else if (f->fn.index < m->import_count) + { + static const char *const exc = "invalid function index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)f->fn.index); +#endif + return NW_FATAL; + } + + offset += sizeof f->fbo * (f->fn.index - m->import_count); + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &f->fbo; + io.n = sizeof f->fbo; + f->io = io; + i->next = get_fbo; + } + + return NW_AGAIN; +} + +void nwp_find_function(struct nw_interp *const i, const struct nw_fn *const fn, + void (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_ffn f = {0}; + struct nw_i_sm_ffn *const pf = &i->sm.ffn; + + *pf = f; + pf->fn = *fn; + pf->next = next; + i->next = seek_fbo; +} diff --git a/src/routines/find_local.c b/src/routines/find_local.c new file mode 100644 index 0000000..e5ab9d2 --- /dev/null +++ b/src/routines/find_local.c @@ -0,0 +1,125 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> +#include <limits.h> + +static enum nw_state read_meta(struct nw_interp *const i) +{ + struct nw_find_local *const f = i->state; + const struct nw_local_meta *const m = &f->meta; + const nw_varuint32 index = f->index - f->entry_i; + const enum nw_state n = nwp_stack_read(i, &f->io, f->addr); + size_t sz; + nw_varuint32 count; + + if (n) + return n; + + f->addr += sizeof f->meta; + count = m->entry_count; + + if (nwp_type_sz(m->type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)m->type); +#endif + return NW_FATAL; + } + else if (index < count) + { + const size_t offset = sz * index; + + if (f->addr > ULONG_MAX - offset) + { + static const char *const exc = "out-of-bounds access to local"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: addr: %lu, offset: %lu\n", exc, + (unsigned long)f->addr, (unsigned long)offset); +#endif + return NW_FATAL; + } + else if ((f->addr += offset) >= i->fr.local_end) + { + static const char *const exc = "invalid local index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)f->index); +#endif + return NW_FATAL; + } + + i->next = f->next; + } + else + { + const size_t offset = sz * count; + + f->entry_i += count; + + if (f->addr > ULONG_MAX - offset) + { + static const char *const exc = "out-of-bounds access to local"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: addr: %lu, offset: %lu\n", exc, + (unsigned long)f->addr, (unsigned long)offset); +#endif + return NW_FATAL; + } + else if ((f->addr += offset) >= i->fr.local_end) + { + static const char *const exc = "invalid local index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)f->index); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &f->meta; + io.n = sizeof f->meta; + f->io = io; + } + } + + return NW_AGAIN; +} + +void nwp_find_local(struct nw_interp *const i, struct nw_find_local *const f, + const nw_varuint32 index, enum nw_state (*const next)(struct nw_interp *), + void *const args) +{ + const struct nw_find_local fl = {0}; + + *f = fl; + f->index = index; + f->next = next; + f->addr = i->fr.local_start; + f->io.buf = &f->meta; + f->io.n = sizeof f->meta; + i->state = f; + i->args = args; + i->next = read_meta; +} diff --git a/src/routines/find_param.c b/src/routines/find_param.c new file mode 100644 index 0000000..eeae86d --- /dev/null +++ b/src/routines/find_param.c @@ -0,0 +1,104 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/types.h> + +static enum nw_state seek_pc(struct nw_interp *const i) +{ + struct nw_find_param *const fp = i->state; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(fp->pc, cfg->user); + + if (n) + return n; + + fp->out.addr = i->fr.fr_start - fp->sz; + i->next = fp->next; + return NW_AGAIN; +} + +static enum nw_state get_param_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_find_param *const fp = i->state; + struct nw_sm_leb128 *const l = &fp->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + enum nw_type param_type; + size_t sz; + + if (n) + return n; + else if (nwp_get_type(type, ¶m_type) + || nwp_type_sz(param_type, &sz)) + { + static const char *const exc = "invalid param type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + if (fp->param_i == fp->index) + fp->out.type = param_type; + + if (fp->param_i >= fp->index) + fp->sz += sz; + + if (++fp->param_i >= i->fr.fn.param_count) + i->next = seek_pc; + + return NW_AGAIN; +} + +static enum nw_state seek_param_types(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + const long offset = i->fr.fn.param_types; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + i->next = get_param_type; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_find_param *const fp = i->state; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&fp->pc, cfg->user); + + if (n) + return n; + + i->next = seek_param_types; + return NW_AGAIN; +} + +void nwp_find_param(struct nw_interp *const i, struct nw_find_param *const f, + const nw_varuint32 index, enum nw_state (*const next)(struct nw_interp *), + void *const args) +{ + const struct nw_find_param fp = {0}; + + *f = fp; + f->index = index; + f->next = next; + i->state = f; + i->next = tell; + i->args = args; +} diff --git a/src/routines/get_function_type.c b/src/routines/get_function_type.c new file mode 100644 index 0000000..e30200f --- /dev/null +++ b/src/routines/get_function_type.c @@ -0,0 +1,331 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state get_param_type(struct nw_interp *); + +static enum nw_state seek_pc(struct nw_interp *const i) +{ + const struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(t->pc, cfg->user); + + if (n) + return n; + + i->next = t->next; + return NW_AGAIN; +} + +static enum nw_state get_return_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, &t->leb128, &type, cfg->user); + + if (n) + return n; + else if (nwp_get_type(type, &t->out.ret.type)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + i->next = seek_pc; + return NW_AGAIN; +} + +static enum nw_state get_return_count(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + struct nw_sm_leb128 *const l = &t->leb128; + nw_varuint1 *const return_count = &t->out.ret.count; + const enum nw_state n = nwp_varuint1(cfg, l, return_count, cfg->user); + + if (n) + return n; + else if (*return_count) + i->next = get_return_type; + else + t->next(i); + + return NW_AGAIN; +} + +static enum nw_state get_param_type(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + nw_varint7 type; + enum nw_type param_type; + const enum nw_state n = nwp_varint7(cfg, &t->leb128, &type, cfg->user); + + if (n) + return n; + else if (nwp_get_type(type, ¶m_type)) + { + i->exception = "invalid type"; + return NW_FATAL; + } + else if (++t->param_i >= t->out.param_count) + i->next = get_return_count; + + return NW_AGAIN; +} + +static enum nw_state tell_param_types(struct nw_interp *const i) +{ + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->tell(&t->out.param_types, cfg->user); + + if (n) + return n; + + i->next = get_param_type; + return NW_AGAIN; +} + +static enum nw_state get_param_count(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + struct nw_sm_leb128 *const l = &t->leb128; + nw_varuint32 *const count = &t->out.param_count; + const enum nw_state n = nwp_varuint32(cfg, l, count, cfg->user); + + if (n) + return n; + + i->next = *count ? tell_param_types : get_return_count; + return NW_AGAIN; +} + +static enum nw_state get_form(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + struct nw_sm_leb128 *const l = &t->leb128; + nw_varint7 form; + const enum nw_state n = nwp_varint7(cfg, l, &form, cfg->user); + + if (n) + return n; + else if (form != 0x60) + { + static const char *const exc = "type index not a function"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)form); +#endif + return NW_FATAL; + } + + i->next = get_param_count; + return NW_AGAIN; +} + +static enum nw_state seek_type(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + const struct nw_i_sm_type *const t = &i->sm.type; + const unsigned long to = nwp_leuint32(&t->to); + const enum nw_state n = cfg->seek(to, cfg->user); + + if (n) + return n; + + i->next = get_form; + return NW_AGAIN; +} + +static enum nw_state get_to(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + const enum nw_state n = nwp_io_read(cfg, &t->io, cfg->user); + + if (n) + return n; + + i->next = seek_type; + return NW_AGAIN; +} + +static enum nw_state seek_to(struct nw_interp *const i) +{ + const struct nw_interp_cfg *const icfg = &i->cfg; + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_mod *const m = icfg->m; + long offset = m->c_sections[NW_CUSTOM_TO]; + const unsigned long fti = nwp_leuint32(&t->fti); + const struct nw_io_cfg *const cfg = &icfg->io; + enum nw_state n; + + if (fti >= m->type_count) + { + static const char *const exc = "invalid type index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, fti); +#endif + return NW_FATAL; + } + else if (!offset) + { + static const char *const exc = "nw_to section not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + + offset += sizeof t->to * fti; + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &t->to; + io.n = sizeof t->to; + t->io = io; + i->next = get_to; + } + + return NW_AGAIN; +} + +static enum nw_state get_fti(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_i_sm_type *const t = &i->sm.type; + const enum nw_state n = nwp_io_read(cfg, &t->io, cfg->user); + + if (n) + return n; + + i->next = seek_to; + return NW_AGAIN; +} + +static enum nw_state seek_fti(struct nw_interp *const i) +{ + const struct nw_interp_cfg *const icfg = &i->cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + struct nw_i_sm_type *const t = &i->sm.type; + long offset = icfg->m->c_sections[NW_CUSTOM_FTI]; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "nw_fti section not found"; + +#ifdef NW_LOG + nwp_log("%s: %s\n", exc, "function type index"); +#endif + i->exception = exc; + return NW_FATAL; + } + + offset += sizeof t->fti * t->fn_index; + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &t->fti; + io.n = sizeof t->fti; + t->io = io; + i->next = get_fti; + } + + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_get_import_type_out *const out = &t->git.out; + struct nw_sm_leb128 *const l = &t->leb128; + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &i->cfg.io; + enum nw_state n; + + if (out->kind != NW_KIND_FUNCTION) + { + static const char *const exc = "import type not a function"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)out->kind); +#endif + return NW_FATAL; + } + else if ((n = nwp_varuint32(cfg, l, &index, cfg->user))) + return n; + + nwp_toleuint32(index, &t->fti); + i->next = seek_to; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_interp *const i) +{ + struct nw_i_sm_type *const t = &i->sm.type; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const nw_varuint32 icnt = i->cfg.m->import_count, index = t->out.index; + const enum nw_state n = cfg->tell(&t->pc, cfg->user); + + if (n) + return n; + else if (index >= icnt) + { + t->fn_index = index - icnt; + i->next = seek_fti; + } + else + nwp_get_import_type(i, &t->git, index, get_index); + + return NW_AGAIN; +} + +void nwp_get_function_type(struct nw_interp *const i, const nw_varuint32 index, + enum nw_state (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_type t = {0}; + struct nw_i_sm_type *const pt = &i->sm.type; + + *pt = t; + pt->out.index = index; + pt->next = next; + i->next = tell; +} diff --git a/src/routines/get_import_type.c b/src/routines/get_import_type.c new file mode 100644 index 0000000..cf5c371 --- /dev/null +++ b/src/routines/get_import_type.c @@ -0,0 +1,123 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/types.h> + +static enum nw_state get_kind(struct nw_interp *const i) +{ + struct nw_get_import_type *const t = i->state; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &t->io, cfg->user); + + if (n) + return n; + else if (t->kind >= NW_KINDS) + { + static const char *const exc = "invalid external_kind"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t->kind); +#endif + return NW_FATAL; + } + + t->out.kind = t->kind; + i->next = t->next; + return NW_AGAIN; +} + +static enum nw_state seek_type(struct nw_interp *const i) +{ + struct nw_get_import_type *const t = i->state; + const unsigned long offset = nwp_leuint32(&t->value); + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &t->kind; + io.n = sizeof t->kind; + t->io = io; + i->next = get_kind; + } + + return NW_AGAIN; +} + +static enum nw_state get_value(struct nw_interp *const i) +{ + struct nw_get_import_type *const t = i->state; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_io_read(cfg, &t->io, cfg->user); + + if (n) + return n; + + i->next = seek_type; + return NW_AGAIN; +} + +static enum nw_state seek_iti(struct nw_interp *const i) +{ + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_get_import_type *const t = i->state; + long offset = i->cfg.m->c_sections[NW_CUSTOM_ITI]; + enum nw_state n; + + if (!offset) + { + static const char *const exc = "nw_iti section not found"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + + offset += t->index * sizeof t->value; + + if ((n = cfg->seek(offset, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &t->value; + io.n = sizeof t->value; + t->io = io; + i->next = get_value; + } + + return NW_AGAIN; +} + +void nwp_get_import_type(struct nw_interp *const i, + struct nw_get_import_type *const it, + const nw_varuint32 index, + enum nw_state (*const next)(struct nw_interp *const i)) +{ + const struct nw_get_import_type t = {0}; + + *it = t; + it->index = index; + it->next = next; + i->state = it; + i->next = seek_iti; +} diff --git a/src/routines/i32_arithm.c b/src/routines/i32_arithm.c new file mode 100644 index 0000000..e833a11 --- /dev/null +++ b/src/routines/i32_arithm.c @@ -0,0 +1,39 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/stack.h> +#include <nw/routines.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_i32_arithm *const a = &i->sm.i32_arithm; + const enum nw_state n = nwp_stack_push(i, &a->io); + + if (n) + return n; + + i->push_type = NW_TYPE_I32; + nwp_interp_resume(i); + return NW_AGAIN; +} + +void nwp_op_i32_arithm(struct nw_interp *const i, const long result) +{ + const struct nw_i_sm_i32_arithm a = {0}; + struct nw_i_sm_i32_arithm *const s = &i->sm.i32_arithm; + + *s = a; + s->result = result; + s->io.buf = &s->result; + s->io.n = sizeof s->result; + i->next = push; +} diff --git a/src/routines/init_data.c b/src/routines/init_data.c new file mode 100644 index 0000000..c9482a0 --- /dev/null +++ b/src/routines/init_data.c @@ -0,0 +1,196 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/linear.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state get_index(struct nw_inst *); +static enum nw_state get_byte(struct nw_inst *); + +static enum nw_state store_byte(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + struct nw_interp *const i = &inst->interp; + const unsigned long offset = d->offset + d->bytes_i; + const enum nw_state n = nwp_linear_store(i, &d->io, offset); + + if (n) + return n; + else if (++d->bytes_i >= d->size) + inst->next = ++d->entry_i >= d->count ? d->next : get_index; + else + inst->next = get_byte; + + return NW_AGAIN; +} + +static enum nw_state get_byte(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + struct nw_interp *const i = &inst->interp; + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &d->b; + io.n = sizeof d->b; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &d->b; + io.n = sizeof d->b; + d->io = io; + } + + inst->next = store_byte; + return NW_AGAIN; +} + +static enum nw_state get_size(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + struct nw_interp *const i = &inst->interp; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &d->size, cfg->user); + + if (n) + return n; + + d->bytes_i = 0; + inst->next = get_byte; + return NW_AGAIN; +} + +static enum nw_state pop(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + const enum nw_state n = nwp_stack_pop(&inst->interp, &d->io); + + if (n) + return n; + + inst->next = get_size; + return NW_AGAIN; +} + +static enum nw_state loop(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + const enum nw_state n = nwp_interp_run(&inst->interp); + struct nw_sm_io io = {0}; + + if (n) + return n; + + io.buf = &d->offset; + io.n = sizeof d->offset; + d->io = io; + inst->next = pop; + return NW_AGAIN; +} + +static enum nw_state setup_initexpr(struct nw_inst *const inst) +{ + struct nw_interp *const i = &inst->interp; + const struct nw_interp_cfg icfg = i->cfg; + + if (nwp_interp_start(i, &icfg, &nwp_interp_data_set)) + { +#ifdef NW_LOG + nwp_log("nw_interp_start failed\n"); +#endif + return NW_FATAL; + } + + inst->next = loop; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_inst *const inst) +{ + struct nw_inst_sm_d *const d = &inst->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + struct nw_interp *const i = &inst->interp; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &d->index, cfg->user); + + if (n) + return n; + + return setup_initexpr(inst); +} + +static enum nw_state get_count(struct nw_inst *const i) +{ + struct nw_inst_sm_d *const d = &i->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + const struct nw_interp_cfg *const icfg = &i->interp.cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + const nw_varuint32 expected = icfg->m->data_count; + const enum nw_state n = nwp_varuint32(cfg, l, &d->count, cfg->user); + + if (n) + return n; + else if (d->count != expected) + { +#ifdef NW_LOG + static const char *const exc = "data count mismatch"; + + nwp_log("%s, expected %lu, got %lu\n", exc, + (unsigned long)expected, (unsigned long)d->count); +#endif + return NW_FATAL; + } + + i->next = d->count ? get_index : d->next; + return NW_AGAIN; +} + +static enum nw_state seek_data(struct nw_inst *const i) +{ + struct nw_inst_sm_gl *const d = &i->sm.global; + const struct nw_interp_cfg *const icfg = &i->interp.cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + const long offset = icfg->m->sections[NW_SECTION_DATA]; + enum nw_state n; + + if (!offset) + { + i->next = d->next; + return NW_AGAIN; + } + else if ((n = cfg->seek(offset, cfg->user))) + return n; + + i->next = get_count; + return NW_AGAIN; +} + +void nwp_init_data(struct nw_inst *const i, + enum nw_state (*const next)(struct nw_inst *)) +{ + const struct nw_inst_sm_d d = {0}; + struct nw_inst_sm_d *const p = &i->sm.data; + + *p = d; + p->next = next; + i->next = seek_data; +} diff --git a/src/routines/init_globals.c b/src/routines/init_globals.c new file mode 100644 index 0000000..6d1e5e4 --- /dev/null +++ b/src/routines/init_globals.c @@ -0,0 +1,181 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nanowasm/types.h> +#include <nw/global.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state get_content_type(struct nw_inst *const i); + +static enum nw_state store(struct nw_inst *const inst) +{ + struct nw_inst_sm_gl *const g = &inst->sm.global; + struct nw_interp *const i = &inst->interp; + const enum nw_state n = nwp_global_store(i, &g->io, g->entry_i); + + if (n) + return n; + + inst->next = ++g->entry_i >= g->count ? g->next : get_content_type; + return NW_AGAIN; +} + +static enum nw_state pop(struct nw_inst *const inst) +{ + struct nw_inst_sm_gl *const g = &inst->sm.global; + struct nw_interp *const i = &inst->interp; + const enum nw_state n = nwp_stack_pop(i, &g->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &g->out; + io.n = sizeof g->out; + g->io = io; + inst->next = store; + } + + return NW_AGAIN; +} + +static enum nw_state loop(struct nw_inst *const i) +{ + struct nw_inst_sm_gl *const g = &i->sm.global; + const enum nw_state n = nwp_interp_run(&i->interp); + size_t sz; + + if (n) + return n; + else if (nwp_type_sz(g->out.type, &sz)) + { +#ifdef NW_LOG + static const char *const exc = "invalid type"; + + nwp_log("%s: %#x\n", exc, (unsigned)g->out.type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &g->out.value; + io.n = sz; + g->io = io; + i->next = pop; + } + + return NW_AGAIN; +} + +static enum nw_state get_mutability(struct nw_inst *const inst) +{ + struct nw_inst_sm_gl *const g = &inst->sm.global; + struct nw_interp *const i = &inst->interp; + const struct nw_io_cfg *const cfg = &i->cfg.io; + struct nw_sm_leb128 *const l = &g->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &g->out.mutability, cfg->user); + + if (n) + return n; + + nwp_interp_resume(i); + inst->next = loop; + return NW_AGAIN; +} + +static enum nw_state get_content_type(struct nw_inst *const i) +{ + nw_varint7 content_type; + struct nw_inst_sm_gl *const g = &i->sm.global; + const struct nw_io_cfg *const cfg = &i->interp.cfg.io; + struct nw_sm_leb128 *const l = &g->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &content_type, cfg->user); + + if (n) + return n; + else if (nwp_get_type(content_type, &g->out.type)) + { +#ifdef NW_LOG + static const char *const exc = "invalid type"; + + nwp_log("%s: %#x\n", exc, (unsigned)content_type); +#endif + return NW_FATAL; + } + + i->next = get_mutability; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_inst *const i) +{ + struct nw_inst_sm_gl *const g = &i->sm.global; + struct nw_sm_leb128 *const l = &g->leb128; + const struct nw_interp_cfg *const icfg = &i->interp.cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + const nw_varuint32 expected = icfg->m->global_count; + const enum nw_state n = nwp_varuint32(cfg, l, &g->count, cfg->user); + + if (n) + return n; + else if (g->count != expected) + { +#ifdef NW_LOG + static const char *const exc = "global count mismatch"; + + nwp_log("%s, expected %lu, got %lu\n", exc, + (unsigned long)expected, (unsigned long)g->count); +#endif + return NW_FATAL; + } + + i->next = g->count ? get_content_type : g->next; + return NW_AGAIN; +} + +static enum nw_state seek_global(struct nw_inst *const i) +{ + struct nw_inst_sm_gl *const g = &i->sm.global; + const struct nw_interp_cfg *const icfg = &i->interp.cfg; + const struct nw_io_cfg *const cfg = &icfg->io; + const long offset = icfg->m->sections[NW_SECTION_GLOBAL]; + enum nw_state n; + + if (!offset) + { + i->next = g->next; + return NW_AGAIN; + } + else if ((n = cfg->seek(offset, cfg->user))) + return n; + + i->next = get_count; + return NW_AGAIN; +} + +void nwp_init_globals(struct nw_inst *const i, + enum nw_state (*const next)(struct nw_inst *)) +{ + const struct nw_inst_sm_gl gl = {0}; + struct nw_inst_sm_gl *const p = &i->sm.global; + + *p = gl; + p->next = next; + i->next = seek_global; +} diff --git a/src/routines/mem_imm.c b/src/routines/mem_imm.c new file mode 100644 index 0000000..c93ee1a --- /dev/null +++ b/src/routines/mem_imm.c @@ -0,0 +1,51 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> +#include <nw/io.h> + +static enum nw_state get_offset(struct nw_interp *const i) +{ + struct nw_i_sm_imm *const imm = &i->sm.imm; + struct nw_sm_leb128 *const l = &imm->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &imm->out.offset, cfg->user); + + if (n) + return n; + + imm->next(i); + return NW_AGAIN; +} + +static enum nw_state get_flags(struct nw_interp *const i) +{ + struct nw_i_sm_imm *const imm = &i->sm.imm; + struct nw_sm_leb128 *const l = &imm->leb128; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &imm->out.flags, cfg->user); + + if (n) + return n; + + i->next = get_offset; + return NW_AGAIN; +} + +void nwp_mem_imm(struct nw_interp *const i, + void (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_imm imm = {0}; + struct nw_i_sm_imm *const p = &i->sm.imm; + + *p = imm; + p->next = next; + i->next = get_flags; +} diff --git a/src/routines/section.c b/src/routines/section.c new file mode 100644 index 0000000..5198424 --- /dev/null +++ b/src/routines/section.c @@ -0,0 +1,105 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static void (*const fn[])(struct nw_mod *) = +{ + nwp_section_custom, + nwp_section_type, + nwp_section_import, + nwp_section_function, + nwp_section_table, + nwp_section_memory, + nwp_section_global, + nwp_section_export, + nwp_section_start, + nwp_section_element, + nwp_section_code, + nwp_section_data, + nwp_section_data_count +}; + +static enum nw_state get_offset(struct nw_mod *const m) +{ + struct nw_mod_section *const s = &m->section; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&s->offset, cfg->user); + long *const offset = &m->sections[s->section]; + + if (n) + return n; + /* Custom section can appear more than once. */ + else if (s->section != NW_SECTION_CUSTOM && *offset) + { +#ifdef NW_LOG + nwp_log("ignoring duplicate section %u\n", (unsigned)s->section); +#endif + nwp_section_skip(m); + } + else + { + nwp_log("processing section %u\n", (unsigned)s->section); + *offset = s->offset; + fn[s->section](m); + } + + return NW_AGAIN; +} + +static enum nw_state get_length(struct nw_mod *const m) +{ + struct nw_mod_section *const s = &m->section; + struct nw_sm_leb128 *const l = &s->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &s->len, cfg->user); + + if (n) + return n; + + m->next = get_offset; + return NW_AGAIN; +} + +static enum nw_state get_section(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_mod_section *const s = &m->section; + struct nw_sm_leb128 *const l = &s->leb128; + const enum nw_state n = nwp_varuint7(cfg, l, &s->section, cfg->user); + + if (n) + { + if (cfg->eof(cfg->user)) + return NW_OK; + + return n; + } + else if (s->section >= sizeof fn / sizeof *fn) + { +#ifdef NW_LOG + nwp_log("invalid section %u\n", (unsigned)s->section); +#endif + return NW_FATAL; + } + + m->next = get_length; + return NW_AGAIN; +} + +void nwp_section(struct nw_mod *const m) +{ + const struct nw_mod_section s = {0}; + + m->section = s; + m->next = get_section; +} diff --git a/src/routines/section/CMakeLists.txt b/src/routines/section/CMakeLists.txt new file mode 100644 index 0000000..ae78d27 --- /dev/null +++ b/src/routines/section/CMakeLists.txt @@ -0,0 +1,34 @@ +# nanowasm, a tiny WebAssembly/Wasm interpreter +# Copyright (C) 2023-2025 Xavier Del Campo Romero +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +target_sources(${PROJECT_NAME} PRIVATE + code.c + custom.c + data.c + data_count.c + element.c + exit.c + export.c + fbo.c + fti.c + function.c + global.c + import.c + iti.c + lo.c + memory.c + ops.c + skip.c + start.c + table.c + to.c + type.c +) + +if(NW_CHECK_CODE) + target_compile_definitions(${PROJECT_NAME} PRIVATE NW_CHECK_CODE) +endif() diff --git a/src/routines/section/code.c b/src/routines/section/code.c new file mode 100644 index 0000000..3ee0c5b --- /dev/null +++ b/src/routines/section/code.c @@ -0,0 +1,298 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/routines.h> + +static enum nw_state local_loop(struct nw_mod *); +static enum nw_state get_opcode(struct nw_mod *); +static enum nw_state entry_loop(struct nw_mod *); + +static enum nw_state get_post_offset(struct nw_mod *const m) +{ + long offset; + unsigned long consumed; + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&offset, cfg->user); + + if (n) + return n; + else if ((consumed = offset - c->op_off) && consumed >= c->rem) + { +#ifdef NW_LOG + nwp_log("parameters exceed expected body length\n"); +#endif + return NW_FATAL; + } + + c->rem -= consumed; + m->next = get_opcode; + return NW_AGAIN; +} + +typedef void (*op_fn)(struct nw_mod *); + +static op_fn find(unsigned char op) +{ + size_t i; + + for (i = 0; i < nwp_check_ops.n; i++) + { + const struct nwp_check_op *const o = &nwp_check_ops.ops[i]; + + if (op >= o->start && op <= o->end) + return o->f; + } + + return NULL; +} + +static enum nw_state get_op_offset(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->op_off, cfg->user); + op_fn fn; + + if (n) + return n; + else if (!(fn = find(c->op))) + { +#ifdef NW_LOG + nwp_log("invalid opcode %#hhx\n", (unsigned char)c->op); +#endif + return NW_FATAL; + } + +#ifdef NW_LOG + nwp_log("opcode: %#hhx\n", (unsigned char)c->op); +#endif + c->next = get_post_offset; + fn(m); + return NW_AGAIN; +} + +static enum nw_state check_exit(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn *const fn = &c->fn; + + if (fn->blocks) + { +#ifdef NW_LOG + nwp_log("mismatched number of blocks in function %lu\n", + (unsigned long)c->entry_i); +#endif + return NW_FATAL; + } + + c->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state get_opcode(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &c->op; + io.n = sizeof c->op; + + if (!c->rem) + return check_exit(m); + else if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (c->rem == 1 && c->op != OP_END) + { +#ifdef NW_LOG + nwp_log("%s: unexpected opcode %#x at body end\n", (unsigned)c->op); +#endif + return NW_FATAL; + } + + c->rem--; + m->next = get_op_offset; + return NW_AGAIN; +} + +#ifndef NW_CHECK_CODE +static enum nw_state skip(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(c->body_start + c->rem, cfg->user); + + if (n) + return n; + + c->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} +#endif + +static enum nw_state get_rem(struct nw_mod *const m) +{ + unsigned long consumed; + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->body_start, cfg->user); + + if (n) + return n; + else if ((consumed = c->body_start - c->start) >= c->body_size) + { +#ifdef NW_LOG + nwp_log("exceeded function body size\n"); +#endif + return NW_FATAL; + } + + c->rem = c->body_size - consumed; +#ifdef NW_CHECK_CODE + m->next = get_opcode; +#else + m->next = skip; +#endif + + return NW_AGAIN; +} + +static enum nw_state get_type(struct nw_mod *const m) +{ + nw_varint7 type; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); + + if (n) + return n; + + c->fn.local_i++; + m->next = local_loop; + return NW_AGAIN; +} + +static enum nw_state get_group_count(struct nw_mod *const m) +{ + nw_varuint32 group_count; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &group_count, cfg->user); + + if (n) + return n; + + c->fn.local_total += group_count; + m->next = get_type; + return NW_AGAIN; +} + +static enum nw_state local_loop(struct nw_mod *const m) +{ + const struct nw_sm_c_fn *const fn = &m->sm.code.fn; + + m->next = fn->local_i < fn->local_count ? get_group_count : get_rem; + return NW_AGAIN; +} + +static enum nw_state get_local_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn *const fn = &c->fn; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &fn->local_count, + cfg->user); + + if (n) + return n; + + m->next = fn->local_count ? local_loop : get_rem; + return NW_AGAIN; +} + +static enum nw_state get_start(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->start, cfg->user); + + if (n) + return n; + + m->next = get_local_count; + return NW_AGAIN; +} + +static enum nw_state get_body_size(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &c->body_size, cfg->user); + + if (n) + return n; + else if (!c->body_size) + { +#ifdef NW_LOG + nwp_log("unexpected zero size for function body %lu\n", + (unsigned long)c->entry_i); +#endif + return NW_FATAL; + } + + m->next = get_start; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_c_fn fn = {0}; + + fn.blocks = 1; + c->fn = fn; + m->next = c->entry_i < c->count ? get_body_size : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_c *const c = &m->sm.code; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &c->count, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_code(struct nw_mod *const m) +{ + const struct nw_sm_c c = {0}; + + m->sm.code = c; + m->next = get_count; +} diff --git a/src/routines/section/custom.c b/src/routines/section/custom.c new file mode 100644 index 0000000..926f46b --- /dev/null +++ b/src/routines/section/custom.c @@ -0,0 +1,145 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state get_byte(struct nw_mod *); + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_sm_custom *const c = &m->sm.custom; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(c->start, cfg->user); + + if (n) + return n; + + nwp_section_skip(m); + return NW_AGAIN; +} + +static enum nw_state compare(struct nw_mod *const m) +{ + struct nw_sm_custom *const c = &m->sm.custom; + const struct entry *out = NULL; + size_t i; + static const struct entry + { + const char *section; + void (*f)(struct nw_mod *); + } s[] = + { + {"nw_to", nwp_section_to}, + {"nw_fti", nwp_section_fti}, + {"nw_fbo", nwp_section_fbo}, + {"nw_lo", nwp_section_lo}, + {"nw_iti", nwp_section_iti} + }; + + for (i = 0; i < sizeof s / sizeof *s; i++) + { + int *const candidate = &c->candidate[i]; + const struct entry *const e = &s[i]; + + if (!*candidate) + continue; + else if (strlen(e->section) != c->name_len + || e->section[c->len_i] != c->byte) + { + *candidate = 0; + continue; + } + + out = e; + break; + } + + if (++c->len_i >= c->name_len) + { + if (out) + { + if (m->c_sections[out - s]) + { +#ifdef NW_LOG + nwp_log("duplicate section: %s\n", out->section); +#endif + return NW_FATAL; + } + + out->f(m); + } + else + m->next = skip; + } + else + m->next = get_byte; + + return NW_AGAIN; +} + +static enum nw_state get_byte(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_custom *const c = &m->sm.custom; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &c->byte; + io.n = sizeof c->byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + + m->next = compare; + return NW_AGAIN; +} + +static enum nw_state get_name_len(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_custom *const c = &m->sm.custom; + struct nw_sm_leb128 *const l = &c->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &c->name_len, cfg->user); + + if (n) + return n; + + m->next = get_byte; + return NW_AGAIN; +} + +static enum nw_state get_start(struct nw_mod *const m) +{ + struct nw_sm_custom *const c = &m->sm.custom; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&c->start, cfg->user); + + if (n) + return n; + + m->next = get_name_len; + return NW_AGAIN; +} + +void nwp_section_custom(struct nw_mod *const m) +{ + const struct nw_sm_custom c = {0}; + struct nw_sm_custom *const pc = &m->sm.custom; + size_t i; + + m->next = get_start; + *pc = c; + + for (i = 0; i < sizeof pc->candidate / sizeof *pc->candidate; i++) + pc->candidate[i] = 1; +} diff --git a/src/routines/section/data.c b/src/routines/section/data.c new file mode 100644 index 0000000..b189c9d --- /dev/null +++ b/src/routines/section/data.c @@ -0,0 +1,178 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state get_index(struct nw_mod *); + +static enum nw_state skip_bytes(struct nw_mod *const m) +{ + struct nw_sm_d *const d = &m->sm.data; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = d->offset + d->size; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + m->next = ++d->entry_i >= m->data_count ? nwp_section_exit : get_index; + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_d *const d = &m->sm.data; + const enum nw_state n = cfg->tell(&d->offset, cfg->user); + + if (n) + return n; + + m->next = skip_bytes; + return NW_AGAIN; +} + +static enum nw_state get_size(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_d *const d = &m->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &d->size, cfg->user); + + if (n) + return n; + else if (!d->size) + { +#ifdef NW_LOG + nwp_log("unexpected zero size for data entry %lu\n", + (unsigned long)d->entry_i); +#endif + return NW_FATAL; + } + + m->next = tell; + return NW_AGAIN; +} + +static int push(const void *const src, const size_t n, void *const user) +{ + struct nw_mod *const m = user; + struct nw_sm_d *const d = &m->sm.data; + + if (n > sizeof d->value) + { +#ifdef NW_LOG + nwp_log("stack overflow\n"); +#endif + return -1; + } + + memcpy(&d->value, src, n); + return n; +} + +static enum nw_state loop(struct nw_mod *const m) +{ + struct nw_sm_d *const d = &m->sm.data; + const enum nw_state n = nwp_interp_run(&d->interp); + + if (n) + return n; + + m->next = get_size; + return NW_AGAIN; +} + +static enum nw_state setup_initexpr(struct nw_mod *const m) +{ + struct nw_interp_cfg icfg = {0}; + struct nw_sm_d *const d = &m->sm.data; + + icfg.m = m; + icfg.io = m->cfg.io; + icfg.user = m; + icfg.stack.push = push; + + if (nwp_interp_start(&d->interp, &icfg, &nwp_interp_data_set)) + { +#ifdef NW_LOG + nwp_log("nw_interp_start failed\n"); +#endif + return NW_FATAL; + } + + m->next = loop; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_d *const d = &m->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + nw_varuint32 *const index = &d->index; + const enum nw_state n = nwp_varuint32(cfg, l, index, cfg->user); + + if (n) + return n; + else if (*index) + { +#ifdef NW_LOG + nwp_log("expected memory index 0 in data entry %lu, got %lu\n", + (unsigned long)d->entry_i, *index); +#endif + return NW_FATAL; + } + + return setup_initexpr(m); +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long dc = m->sections[NW_SECTION_DATA_COUNT]; + struct nw_sm_d *const d = &m->sm.data; + struct nw_sm_leb128 *const l = &d->leb128; + nw_varuint32 count, *const pcount = dc ? &count : &m->data_count; + enum nw_state n; + + if (!m->sections[NW_SECTION_MEMORY]) + { +#ifdef NW_LOG + nwp_log("data section found before memory section\n"); +#endif + return NW_FATAL; + } + else if ((n = nwp_varuint32(cfg, l, pcount, cfg->user))) + return n; + else if (dc && count != m->data_count) + { +#ifdef NW_LOG + nwp_log("data count mismatch, expected %lu, got %lu\n", + (unsigned long)m->data_count, (unsigned long)count); +#endif + return NW_FATAL; + } + + m->next = m->data_count ? get_index : nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_data(struct nw_mod *const m) +{ + const struct nw_sm_d d = {0}; + + m->sm.data = d; + m->next = get_count; +} diff --git a/src/routines/section/data_count.c b/src/routines/section/data_count.c new file mode 100644 index 0000000..9e7ea07 --- /dev/null +++ b/src/routines/section/data_count.c @@ -0,0 +1,35 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_dc *const dc = &m->sm.data_count; + struct nw_sm_leb128 *const l = &dc->leb128; + enum nw_state n; + + if ((n = nwp_varuint32(cfg, l, &m->data_count, cfg->user))) + return n; + + m->next = nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_data_count(struct nw_mod *const m) +{ + const struct nw_sm_dc dc = {0}; + + m->sm.data_count = dc; + m->next = get_count; +} diff --git a/src/routines/section/element.c b/src/routines/section/element.c new file mode 100644 index 0000000..7fd2f54 --- /dev/null +++ b/src/routines/section/element.c @@ -0,0 +1,20 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +void nwp_section_element(struct nw_mod *const m) +{ + /* TODO */ + nwp_section_skip(m); +} diff --git a/src/routines/section/exit.c b/src/routines/section/exit.c new file mode 100644 index 0000000..93a2446 --- /dev/null +++ b/src/routines/section/exit.c @@ -0,0 +1,36 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +enum nw_state nwp_section_exit(struct nw_mod *const m) +{ + long offset; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const struct nw_mod_section *const s = &m->section; + const enum nw_state n = cfg->tell(&offset, cfg->user); + unsigned long size; + + if (n) + return n; + else if ((size = offset - s->offset) != s->len) + { +#ifdef NW_LOG + nwp_log("size mismatch (%lu expected, got %lu)\n", + (unsigned long)s->len, size); +#endif + return NW_FATAL; + } + + nwp_section(m); + return NW_AGAIN; +} diff --git a/src/routines/section/export.c b/src/routines/section/export.c new file mode 100644 index 0000000..643b55f --- /dev/null +++ b/src/routines/section/export.c @@ -0,0 +1,123 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state entry_loop(struct nw_mod *m); + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_exp *const e = &m->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + e->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state get_kind(struct nw_mod *const m) +{ + unsigned char kind; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &kind; + io.n = sizeof kind; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + + /* TODO: check kind */ + m->next = get_index; + return NW_AGAIN; +} + +static enum nw_state get_name(struct nw_mod *const m) +{ + unsigned char b; + struct nw_sm_exp *const e = &m->sm.export; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &b; + io.n = sizeof b; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (++e->len_i >= e->field_len) + { + e->len_i = 0; + m->next = get_kind; + } + + return NW_AGAIN; +} + +static enum nw_state get_len(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_exp *const e = &m->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->field_len, cfg->user); + + if (n) + return n; + else if (!e->field_len) + { +#ifdef NW_LOG + nwp_log("invalid field length\n"); +#endif + return NW_FATAL; + } + + m->next = get_name; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + const struct nw_sm_exp *const e = &m->sm.export; + + m->next = e->entry_i < e->count ? get_len : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_exp *const e = &m->sm.export; + struct nw_sm_leb128 *const l = &e->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &e->count, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_export(struct nw_mod *const m) +{ + const struct nw_sm_exp e = {0}; + + m->sm.export = e; + m->next = get_count; +} diff --git a/src/routines/section/fbo.c b/src/routines/section/fbo.c new file mode 100644 index 0000000..82ebd14 --- /dev/null +++ b/src/routines/section/fbo.c @@ -0,0 +1,52 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = m->c_sections[NW_CUSTOM_FBO] + + sizeof (struct nw_leuint32) * m->function_count; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + enum nw_state n; + + if (!m->sections[NW_SECTION_CODE]) + { +#ifdef NW_LOG + nwp_log("code section must be defined first\n"); +#endif + return NW_FATAL; + } + else if ((n = cfg->tell(&m->c_sections[NW_CUSTOM_FBO], cfg->user))) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_fbo(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/fti.c b/src/routines/section/fti.c new file mode 100644 index 0000000..ddc7762 --- /dev/null +++ b/src/routines/section/fti.c @@ -0,0 +1,59 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = m->c_sections[NW_CUSTOM_FTI] + + sizeof (struct nw_leuint32) * m->function_count; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + enum nw_state n; + + if (!m->sections[NW_SECTION_FUNCTION]) + { +#ifdef NW_LOG + nwp_log("function section must be defined first\n"); +#endif + return NW_FATAL; + } + else if (!m->sections[NW_SECTION_IMPORT]) + { +#ifdef NW_LOG + nwp_log("import section must be defined first\n"); +#endif + return NW_FATAL; + } + else if ((n = cfg->tell(&m->c_sections[NW_CUSTOM_FTI], cfg->user))) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_fti(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/function.c b/src/routines/section/function.c new file mode 100644 index 0000000..7cbcaca --- /dev/null +++ b/src/routines/section/function.c @@ -0,0 +1,52 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_entry(struct nw_mod *const m) +{ + struct nw_sm_fn *const fn = &m->sm.function; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_leb128 *const l = &fn->leb128; + nw_varuint32 type; + const enum nw_state n = nwp_varuint32(cfg, l, &type, cfg->user); + + if (n) + return n; + else if (++fn->entry_i >= m->function_count) + m->next = nwp_section_exit; + + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_fn *const fn = &m->sm.function; + struct nw_sm_leb128 *const l = &fn->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &m->function_count, + cfg->user); + + if (n) + return n; + + m->next = get_entry; + return NW_AGAIN; +} + +void nwp_section_function(struct nw_mod *const m) +{ + const struct nw_sm_fn fn = {0}; + + m->next = get_count; + m->sm.function = fn; +} diff --git a/src/routines/section/global.c b/src/routines/section/global.c new file mode 100644 index 0000000..6223fc9 --- /dev/null +++ b/src/routines/section/global.c @@ -0,0 +1,148 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/types.h> +#include <limits.h> +#include <string.h> + +static enum nw_state get_content_type(struct nw_mod *); + +static int push(const void *const src, const size_t n, void *const user) +{ + struct nw_mod *const m = user; + struct nw_sm_gl *const gl = &m->sm.global; + + if (n > sizeof gl->value) + { +#ifdef NW_LOG + nwp_log("stack overflow\n"); +#endif + return -1; + } + + memcpy(&gl->value, src, n); + return n; +} + +static enum nw_state loop(struct nw_mod *const m) +{ + struct nw_sm_gl *const gl = &m->sm.global; + const enum nw_state n = nwp_interp_run(&gl->interp); + + if (n) + return n; + + m->next = ++gl->entry_i >= m->global_count ? + nwp_section_exit : get_content_type; + return NW_AGAIN; +} + +static enum nw_state get_global(struct nw_mod *const m) +{ + struct nw_sm_gl *const gl = &m->sm.global; + struct nw_interp_cfg cfg = {0}; + + cfg.m = m; + cfg.io = m->cfg.io; + cfg.user = m; + cfg.stack.push = push; + + if (nwp_interp_start(&gl->interp, &cfg, &nwp_interp_initexpr_set)) + { +#ifdef NW_LOG + nwp_log("nw_interp_start failed\n"); +#endif + return NW_FATAL; + } + + m->next = loop; + return NW_AGAIN; +} + +static enum nw_state get_mutability(struct nw_mod *const m) +{ + nw_varuint1 mutability; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_gl *const gl = &m->sm.global; + struct nw_sm_leb128 *const l = &gl->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &mutability, cfg->user); + + if (n) + return n; + + m->next = get_global; + return NW_AGAIN; +} + +static enum nw_state get_content_type(struct nw_mod *const m) +{ + nw_varint7 content_type; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_gl *const gl = &m->sm.global; + struct nw_sm_leb128 *const l = &gl->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &content_type, cfg->user); + enum nw_type type; + + /* TODO: do not ignore type */ + if (n) + return n; + else if (nwp_get_type(content_type, &type)) + { +#ifdef NW_LOG + nwp_log("nwp_get_type failed\n"); +#endif + return NW_FATAL; + } + + m->next = get_mutability; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_gl *const gl = &m->sm.global; + struct nw_sm_leb128 *const l = &gl->leb128; + enum nw_state n; + + if (!m->sections[NW_SECTION_IMPORT]) + { +#ifdef NW_LOG + nwp_log("global section found before import section\n"); +#endif + return NW_FATAL; + } + else if ((n = nwp_varuint32(cfg, l, &m->global_count, cfg->user))) + return n; + else if (m->global_count > ULONG_MAX / sizeof (struct nw_global)) + { +#ifdef NW_LOG + nwp_log("global_count too large: %lu\n", + (unsigned long)m->global_count); +#endif + return NW_FATAL; + } + + m->out.global = m->global_count * sizeof (struct nw_global); + m->next = m->global_count ? get_content_type : nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_global(struct nw_mod *const m) +{ + const struct nw_sm_gl g = {0}; + + m->sm.global = g; + m->next = get_count; +} diff --git a/src/routines/section/import.c b/src/routines/section/import.c new file mode 100644 index 0000000..26ef49a --- /dev/null +++ b/src/routines/section/import.c @@ -0,0 +1,339 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <string.h> + +static enum nw_state entry_loop(struct nw_mod *); +static enum nw_state find_import(struct nw_mod *); + +static enum nw_state get_function_type(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_leb128 *const l = &imp->leb128; + struct nw_import_index *const ii = &m->cfg.imp_indexes[imp->imp_i]; + nw_varuint32 type; + const enum nw_state n = nwp_varuint32(cfg, l, &type, cfg->user); + + if (n) + return n; + else if (imp->entry_i >= m->cfg.n_imports) + { +#ifdef NW_LOG + nwp_log("too many import entries\n"); +#endif + return NW_FATAL; + } + + ii->index = imp->entry_i++; + m->next = entry_loop; + return NW_AGAIN; +} + +static enum nw_state skip_import(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(imp->mod_off, cfg->user); + + if (n) + return n; + + imp->len_i = 0; + imp->imp_i++; + m->next = find_import; + return NW_AGAIN; +} + +static enum nw_state get_kind(struct nw_mod *const m) +{ + unsigned char kind; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_import *const i = &m->cfg.imports[imp->imp_i]; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &kind; + io.n = sizeof kind; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (kind != i->kind) + m->next = skip_import; + /* TODO: process every import type. */ + else if (kind != NW_KIND_FUNCTION) + m->next = nwp_section_exit; + else + m->next = get_function_type; + + return NW_AGAIN; +} + +static enum nw_state compare_field(struct nw_mod *const m) +{ + unsigned char byte; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_import *const i = &m->cfg.imports[imp->imp_i]; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &byte; + io.n = sizeof byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (byte != i->field[imp->len_i]) + m->next = skip_import; + else if (++imp->len_i >= imp->field_len) + { + m->next = get_kind; + imp->len_i = 0; + } + + return NW_AGAIN; +} + +static enum nw_state get_field_offset(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const enum nw_state n = cfg->tell(&imp->field_off, cfg->user); + const struct nw_import *const i = &m->cfg.imports[imp->imp_i]; + + if (n) + return n; + + m->next = imp->field_len != strlen(i->field) ? skip_import : compare_field; + return NW_AGAIN; +} + +static enum nw_state get_field_len(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_leb128 *const l = &imp->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &imp->field_len, cfg->user); + + if (n) + return n; + + m->next = get_field_offset; + return NW_AGAIN; +} + +static enum nw_state compare_name(struct nw_mod *const m) +{ + unsigned char byte; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_import *const i = &m->cfg.imports[imp->imp_i]; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &byte; + io.n = sizeof byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + else if (byte != i->module[imp->len_i]) + m->next = skip_import; + else if (++imp->len_i >= imp->mod_len) + { + m->next = get_field_len; + imp->len_i = 0; + } + + return NW_AGAIN; +} + +static enum nw_state dump_import_field_name(struct nw_mod *const m) +{ + unsigned char byte; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_io io = {0}; + struct nw_sm_imp *const imp = &m->sm.import; + enum nw_state n; + + io.buf = &byte; + io.n = sizeof byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + +#ifdef NW_LOG + nwp_log("%c", (char)byte); +#endif + + if (++imp->len_i >= imp->field_len) + { +#ifdef NW_LOG + nwp_log("\n"); +#endif + return NW_FATAL; + } + + return NW_AGAIN; +} + +static enum nw_state rewind_to_field_name(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(imp->field_off, cfg->user); + + if (n) + return n; + + m->next = dump_import_field_name; + return NW_AGAIN; +} + +static enum nw_state dump_import_mod_name(struct nw_mod *const m) +{ + unsigned char byte; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_io io = {0}; + enum nw_state n; + + io.buf = &byte; + io.n = sizeof byte; + + if ((n = nwp_io_read(cfg, &io, cfg->user))) + return n; + +#ifdef NW_LOG + nwp_log("%c", (char)byte); +#endif + + if (++imp->len_i >= imp->mod_len) + { + imp->len_i = 0; + + if (imp->field_off) + { +#ifdef NW_LOG + nwp_log("::"); +#endif + m->next = rewind_to_field_name; + } + else + { +#ifdef NW_LOG + nwp_log("\n"); +#endif + return NW_FATAL; + } + } + + return NW_AGAIN; +} + +static enum nw_state rewind_to_mod_name(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(imp->mod_off, cfg->user); + + if (n) + return n; + + m->next = dump_import_mod_name; + return NW_AGAIN; +} + +static enum nw_state find_import(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + const struct nw_import *i; + + if (imp->imp_i >= m->cfg.n_imports) + { +#ifdef NW_LOG + nwp_log("required import: "); +#endif + m->next = rewind_to_mod_name; + return NW_AGAIN; + } + + i = &m->cfg.imports[imp->imp_i]; + + if (imp->mod_len == strlen(i->module)) + m->next = compare_name; + else + imp->imp_i++; + + return NW_AGAIN; +} + +static enum nw_state get_module_offset(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + const enum nw_state n = cfg->tell(&imp->mod_off, cfg->user); + + if (n) + return n; + + m->next = find_import; + return NW_AGAIN; +} + +static enum nw_state get_module_len(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_leb128 *const l = &imp->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &imp->mod_len, cfg->user); + + if (n) + return n; + + m->next = get_module_offset; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + struct nw_sm_imp *const imp = &m->sm.import; + + imp->len_i = 0; + imp->imp_i = 0; + m->next = imp->entry_i < m->import_count ? + get_module_len : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_imp *const imp = &m->sm.import; + struct nw_sm_leb128 *const l = &imp->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &m->import_count, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_import(struct nw_mod *const m) +{ + const struct nw_sm_imp imp = {0}; + + m->next = get_count; + m->sm.import = imp; +} diff --git a/src/routines/section/iti.c b/src/routines/section/iti.c new file mode 100644 index 0000000..6fc7abc --- /dev/null +++ b/src/routines/section/iti.c @@ -0,0 +1,52 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = m->c_sections[NW_CUSTOM_ITI] + + sizeof (struct nw_leuint32) * m->import_count; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + enum nw_state n; + + if (!m->sections[NW_SECTION_IMPORT]) + { +#ifdef NW_LOG + nwp_log("import section must be defined first\n"); +#endif + return NW_FATAL; + } + else if ((n = cfg->tell(&m->c_sections[NW_CUSTOM_ITI], cfg->user))) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_iti(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/lo.c b/src/routines/section/lo.c new file mode 100644 index 0000000..aacfa67 --- /dev/null +++ b/src/routines/section/lo.c @@ -0,0 +1,58 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_sm_custom *const c = &m->sm.custom; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->seek(c->start, cfg->user); + + if (n) + return n; + + nwp_section_skip(m); + return NW_AGAIN; +} + +static enum nw_state get_offset(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&m->c_sections[NW_CUSTOM_LO], cfg->user); + + if (n) + return n; + + m->next = skip; + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + if (!m->sections[NW_SECTION_CODE]) + { +#ifdef NW_LOG + nwp_log("code section must be defined first\n"); +#endif + return NW_FATAL; + } + + m->next = get_offset; + return NW_AGAIN; +} + +void nwp_section_lo(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/memory.c b/src/routines/section/memory.c new file mode 100644 index 0000000..9277230 --- /dev/null +++ b/src/routines/section/memory.c @@ -0,0 +1,86 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_maximum(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_mem *const mem = &m->sm.memory; + struct nw_mod_out_mem *const lin = &m->out.linear; + struct nw_sm_leb128 *const l = &mem->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &lin->max, cfg->user); + + if (n) + return n; + + m->next = nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_initial(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_mem *const mem = &m->sm.memory; + struct nw_mod_out_mem *const lin = &m->out.linear; + struct nw_sm_leb128 *const l = &mem->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &lin->initial, cfg->user); + + if (n) + return n; + + m->next = mem->flags ? get_maximum : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_flags(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_mem *const mem = &m->sm.memory; + struct nw_sm_leb128 *const l = &mem->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &mem->flags, cfg->user); + + if (n) + return n; + + m->next = get_initial; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_mem *const mem = &m->sm.memory; + struct nw_sm_leb128 *const l = &mem->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &mem->count, cfg->user); + + if (n) + return n; + else if (mem->count > 1) + { +#ifdef NW_LOG + nwp_log("MVP WebAssembly supports no more than 1 memory\n"); +#endif + return NW_FATAL; + } + + m->next = mem->count ? get_flags : nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_memory(struct nw_mod *const m) +{ + const struct nw_sm_mem mem = {0}; + + m->next = get_count; + m->sm.memory = mem; +} diff --git a/src/routines/section/ops.c b/src/routines/section/ops.c new file mode 100644 index 0000000..a81c8e7 --- /dev/null +++ b/src/routines/section/ops.c @@ -0,0 +1,141 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/ops.h> +#include <nw/opcodes.h> + +static const struct nwp_check_op ops[] = +{ + { + OP_UNREACHABLE, + OP_NOP, + nwp_op_check_no_immediate + }, + + { + OP_BLOCK, + OP_IF, + nwp_op_check_block + }, + + { + OP_ELSE, + OP_ELSE, + nwp_op_check_no_immediate + }, + + { + OP_END, + OP_END, + nwp_op_check_end + }, + + { + OP_BR, + OP_BR_IF, + nwp_op_check_relative_depth + }, + + { + OP_BR_TABLE, + OP_BR_TABLE, + nwp_op_check_br_table + }, + + { + OP_RETURN, + OP_RETURN, + nwp_op_check_no_immediate + }, + + { + OP_CALL, + OP_CALL, + nwp_op_check_call + }, + + { + OP_CALL_INDIRECT, + OP_CALL_INDIRECT, + nwp_op_check_call_indirect + }, + + { + OP_DROP, + OP_SELECT, + nwp_op_check_no_immediate + }, + + { + OP_GET_LOCAL, + OP_TEE_LOCAL, + nwp_op_check_local_index, + }, + + { + OP_GET_GLOBAL, + OP_SET_GLOBAL, + nwp_op_check_global_index + }, + + { + OP_I32_LOAD, + OP_I64_STORE32, + nwp_op_check_memory_immediate + }, + + { + OP_CURRENT_MEMORY, + OP_GROW_MEMORY, + nwp_op_check_varuint1 + }, + + { + OP_I32_CONST, + OP_I32_CONST, + nwp_op_check_varint32 + }, + + { + OP_I64_CONST, + OP_I64_CONST, + nwp_op_check_varint64 + }, + + { + OP_F32_CONST, + OP_F32_CONST, + nwp_op_check_uint32 + }, + + { + OP_F64_CONST, + OP_F64_CONST, + nwp_op_check_uint64 + }, + + { + OP_I32_EQZ, + OP_F64_PROMOTE_F32, + nwp_op_check_no_immediate + }, + + { + OP_MISC, + OP_MISC, + nwp_op_check_misc + } +}; + +const struct nwp_check_ops nwp_check_ops = +{ + ops, + sizeof ops / sizeof *ops +}; diff --git a/src/routines/section/skip.c b/src/routines/section/skip.c new file mode 100644 index 0000000..72296ce --- /dev/null +++ b/src/routines/section/skip.c @@ -0,0 +1,45 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_mod_section *const s = &m->section; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = s->cur + s->len; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state tell(struct nw_mod *const m) +{ + struct nw_mod_section *const s = &m->section; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = cfg->tell(&s->cur, cfg->user); + + if (n) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_skip(struct nw_mod *const m) +{ + m->next = tell; +} diff --git a/src/routines/section/start.c b/src/routines/section/start.c new file mode 100644 index 0000000..8ff22f3 --- /dev/null +++ b/src/routines/section/start.c @@ -0,0 +1,37 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_index(struct nw_mod *const m) +{ + nw_varuint32 index; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_start *const st = &m->sm.start; + struct nw_sm_leb128 *const l = &st->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user); + + if (n) + return n; + + m->next = nwp_section_exit; + return NW_AGAIN; +} + +void nwp_section_start(struct nw_mod *const m) +{ + const struct nw_sm_start s = {0}; + + m->sm.start = s; + m->next = get_index; +} diff --git a/src/routines/section/table.c b/src/routines/section/table.c new file mode 100644 index 0000000..56f62ae --- /dev/null +++ b/src/routines/section/table.c @@ -0,0 +1,117 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state get_maximum(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_mod_out_mem *const ot = &m->out.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &ot->max, cfg->user); + + if (n) + return n; + + m->next = nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_initial(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_mod_out_mem *const ot = &m->out.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &ot->initial, cfg->user); + + if (n) + return n; + + m->next = tb->flags ? get_maximum : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_flags(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varuint1(cfg, l, &tb->flags, cfg->user); + + if (n) + return n; + + m->next = get_initial; + return NW_AGAIN; +} + +static enum nw_state get_element_type(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varint7(cfg, l, &tb->elem_type, cfg->user); + + if (n) + return n; + /* TODO: replace with enum? */ + else if (tb->elem_type != 0x70) + { +#ifdef NW_LOG + nwp_log("unexpected elem_type %hhx\n", (char)tb->elem_type); +#endif + return NW_FATAL; + } + + m->next = get_flags; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + const struct nw_sm_tb *const tb = &m->sm.table; + + m->next = tb->entry_i < tb->count ? get_element_type : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_tb *const tb = &m->sm.table; + struct nw_sm_leb128 *const l = &tb->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &tb->count, cfg->user); + + if (n) + return n; + else if (tb->count > 1u) + { +#ifdef NW_LOG + nwp_log("got %lu tables, but only 1 supported by the MVP\n", + (unsigned long)tb->count); +#endif + return NW_FATAL; + } + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_table(struct nw_mod *const m) +{ + const struct nw_sm_tb t = {0}; + + m->sm.table = t; + m->next = get_count; +} diff --git a/src/routines/section/to.c b/src/routines/section/to.c new file mode 100644 index 0000000..cf459cd --- /dev/null +++ b/src/routines/section/to.c @@ -0,0 +1,52 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> + +static enum nw_state skip(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + const long offset = m->c_sections[NW_CUSTOM_TO] + + sizeof (struct nw_leuint32) * m->type_count; + const enum nw_state n = cfg->seek(offset, cfg->user); + + if (n) + return n; + + nwp_section(m); + return NW_AGAIN; +} + +static enum nw_state check(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + enum nw_state n; + + if (!m->sections[NW_SECTION_TYPE]) + { +#ifdef NW_LOG + nwp_log("type section must be defined first\n"); +#endif + return NW_FATAL; + } + else if ((n = cfg->tell(&m->c_sections[NW_CUSTOM_TO], cfg->user))) + return n; + + m->next = skip; + return NW_AGAIN; +} + +void nwp_section_to(struct nw_mod *const m) +{ + m->next = check; +} diff --git a/src/routines/section/type.c b/src/routines/section/type.c new file mode 100644 index 0000000..c8e5e9c --- /dev/null +++ b/src/routines/section/type.c @@ -0,0 +1,164 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <stddef.h> + +static enum nw_state entry_loop(struct nw_mod *); + +static enum nw_state get_return_type(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + nw_varint7 return_type; + const enum nw_state n = nwp_varint7(cfg, l, &return_type, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + st->entry_i++; + return NW_AGAIN; +} + +static enum nw_state get_return_count(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + nw_varuint1 return_count; + const enum nw_state n = nwp_varuint1(cfg, l, &return_count, cfg->user); + + if (n) + return n; + else if (return_count) + m->next = get_return_type; + else + { + m->next = entry_loop; + st->entry_i++; + } + + return NW_AGAIN; +} + +static enum nw_state param_loop(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_leb128 *const l = &st->leb128; + nw_varint7 type; + enum nw_state n; + + if (st->p_i >= st->param_count) + { + m->next = get_return_count; + return NW_AGAIN; + } + else if ((n = nwp_varint7(cfg, l, &type, cfg->user))) + return n; + + st->p_i++; + return NW_AGAIN; +} + +static enum nw_state get_param_count(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &st->param_count, + cfg->user); + + if (n) + return n; + + st->p_i = 0; + m->next = param_loop; + return NW_AGAIN; +} + +static int check_form(const nw_varint7 form) +{ + static const nw_varint7 v[] = + { + /* TODO: move values to enums? */ + 0x7f, /* i32 */ + 0x7e, /* i64 */ + 0x7d, /* f32 */ + 0x7c, /* f64 */ + 0x70, /* anyfunc */ + 0x60, /* func */ + 0x40 /* empty block_type */ + }; + + size_t i; + + for (i = 0; i < sizeof v / sizeof *v; i++) + if (form == v[i]) + return 0; + + return -1; +} + +static enum nw_state get_form(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const struct nw_io_cfg *const cfg = &m->cfg.io; + nw_varint7 form; + const enum nw_state n = nwp_varint7(cfg, l, &form, cfg->user); + + if (n) + return n; + else if (check_form(form)) + { +#ifdef NW_LOG + nwp_log("invalid form %#x\n", (unsigned)form); +#endif + return NW_FATAL; + } + + m->next = get_param_count; + return NW_AGAIN; +} + +static enum nw_state entry_loop(struct nw_mod *const m) +{ + struct nw_sm_st *const st = &m->sm.type; + + m->next = st->entry_i < m->type_count ? get_form : nwp_section_exit; + return NW_AGAIN; +} + +static enum nw_state get_count(struct nw_mod *const m) +{ + const struct nw_io_cfg *const cfg = &m->cfg.io; + struct nw_sm_st *const st = &m->sm.type; + struct nw_sm_leb128 *const l = &st->leb128; + const enum nw_state n = nwp_varuint32(cfg, l, &m->type_count, cfg->user); + + if (n) + return n; + + m->next = entry_loop; + return NW_AGAIN; +} + +void nwp_section_type(struct nw_mod *const m) +{ + const struct nw_sm_st st = {0}; + + m->next = get_count; + m->sm.type = st; +} diff --git a/src/routines/set_local.c b/src/routines/set_local.c new file mode 100644 index 0000000..84c7c26 --- /dev/null +++ b/src/routines/set_local.c @@ -0,0 +1,162 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/ops.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state set_local(struct nw_interp *const i) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + const enum nw_state n = nwp_stack_write(i, &sl->io, sl->f.l.addr); + + if (n) + return n; + + i->next = sl->next; + return NW_AGAIN; +} + +static int init_io(struct nw_interp *const i, const enum nw_type t) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + size_t sz; + + if (nwp_type_sz(t, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &sl->out.value; + io.n = sz; + sl->io = io; + } + + return 0; +} + +static enum nw_state pop(struct nw_interp *const i, const enum nw_type t, + enum nw_state (*const next)(struct nw_interp *)) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + const enum nw_state n = nwp_stack_pop(i, &sl->io); + + if (n) + return n; + else if (init_io(i, t)) + return NW_FATAL; + + sl->out.type = t; + i->next = next; + return NW_AGAIN; +} + +static enum nw_state pop_local(struct nw_interp *const i) +{ + const struct nw_local_meta *const m = &i->sm.set_local.f.l.meta; + + return pop(i, m->type, set_local); +} + +static enum nw_state prepare_local(struct nw_interp *const i) +{ + const struct nw_local_meta *const m = &i->sm.set_local.f.l.meta; + + if (init_io(i, m->type)) + return NW_FATAL; + + i->next = pop_local; + return NW_AGAIN; +} + +static enum nw_state set_param(struct nw_interp *const i) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + const enum nw_state n = nwp_stack_write(i, &sl->io, sl->f.p.out.addr); + + if (n) + return n; + + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state pop_param(struct nw_interp *const i) +{ + const struct nw_find_param_out *const o = &i->sm.set_local.f.p.out; + + return pop(i, o->type, set_param); +} + +static enum nw_state prepare_param(struct nw_interp *const i) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + const struct nw_find_param_out *const o = &sl->f.p.out; + + if (init_io(i, o->type)) + return NW_FATAL; + + i->next = pop_param; + return NW_AGAIN; +} + +static enum nw_state get_index(struct nw_interp *const i) +{ + struct nw_i_sm_set_local *const sl = &i->sm.set_local; + struct nw_sm_leb128 *const l = &sl->leb128; + const struct nw_frame *const fr = &i->fr; + const struct nw_fn *const fn = &fr->fn; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varuint32(cfg, l, &sl->index, cfg->user); + nw_varuint32 index; + + if (n) + return n; + else if (sl->index < fn->param_count) + nwp_find_param(i, &sl->f.p, sl->index, prepare_param, NULL); + else if ((index = sl->index - fn->param_count) >= fr->local_count) + { + static const char *const exc = "invalid local index"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu\n", exc, (unsigned long)sl->index); +#endif + return NW_FATAL; + } + else + nwp_find_local(i, &sl->f.l, index, prepare_local, NULL); + + return NW_AGAIN; +} + +void nwp_set_local(struct nw_interp *const i, + enum nw_state (*const next)(struct nw_interp *)) +{ + const struct nw_i_sm_set_local sl = {0}; + struct nw_i_sm_set_local *const p = &i->sm.set_local; + + *p = sl; + p->next = next; + i->next = get_index; +} diff --git a/src/routines/start_block.c b/src/routines/start_block.c new file mode 100644 index 0000000..83be44c --- /dev/null +++ b/src/routines/start_block.c @@ -0,0 +1,49 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/types.h> + +static enum nw_state get_block_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_sb *const sb = &i->sm.start_block; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, &sb->leb128, &type, cfg->user); + enum nw_type block_type; + + if (n) + return n; + else if (type != 0x40 && nwp_get_type(type, &block_type)) + { + static const char *const exc = "invalid block_type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#hhx\n", exc, (unsigned char)type); +#endif + return NW_FATAL; + } + + i->fr.block_i++; + nwp_interp_resume(i); + return NW_AGAIN; +} + +void nwp_start_block(struct nw_interp *const i) +{ + const struct nw_i_sm_sb sb = {0}; + + i->sm.start_block = sb; + i->next = get_block_type; +} diff --git a/src/routines/unary.c b/src/routines/unary.c new file mode 100644 index 0000000..d65f8aa --- /dev/null +++ b/src/routines/unary.c @@ -0,0 +1,86 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/io.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/routines.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state push(struct nw_interp *const i) +{ + struct nw_i_sm_unary *const u = &i->sm.unary; + const enum nw_state n = nwp_stack_push(i, &u->io); + + if (n) + return n; + + i->push_type = u->type; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state pop(struct nw_interp *const i) +{ + struct nw_i_sm_unary *const u = &i->sm.unary; + const enum nw_state n = nwp_stack_pop(i, &u->io); + size_t sz; + + if (n) + return n; + else if (u->op(&u->in, &u->out)) + { + static const char *const exc = "unary operation failed"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if (nwp_type_sz(u->type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %d\n", exc, u->type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->out; + io.n = sz; + u->io = io; + } + + i->next = push; + return NW_AGAIN; +} + +void nwp_unary(struct nw_interp *const i, const enum nw_type t, + int (*const op)(const union nw_value *, union nw_value *)) +{ + const struct nw_i_sm_unary u = {0}; + struct nw_i_sm_unary *const pu = &i->sm.unary; + size_t sz; + + *pu = u; + pu->type = t; + pu->op = op; + nwp_type_sz(t, &sz); + pu->io.buf = &pu->in; + pu->io.n = sz; + i->next = pop; +} diff --git a/src/routines/unwind.c b/src/routines/unwind.c new file mode 100644 index 0000000..e4200fd --- /dev/null +++ b/src/routines/unwind.c @@ -0,0 +1,391 @@ +/* + * nanowasm, a tiny WebAssembly/Wasm interpreter + * Copyright (C) 2023-2025 Xavier Del Campo Romero + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include <nanowasm/nw.h> +#include <nw/routines.h> +#include <nw/interp.h> +#include <nw/io.h> +#include <nw/log.h> +#include <nw/stack.h> +#include <nw/types.h> + +static enum nw_state prepare_pop(struct nw_interp *); + +static enum nw_state seek_pc(struct nw_interp *const i) +{ + const struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(u->fr.pc, cfg->user); + struct nw_return r; + + if (n) + return n; + +#ifdef NW_LOG + nwp_log("returning to function %lu\n", u->fr.fn.index); +#endif + + r = i->fr.fn.ret; + i->fr = u->fr; + i->fr.prev_ret = r; + nwp_interp_resume(i); + return NW_AGAIN; +} + +static enum nw_state push_return_value(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const enum nw_state n = nwp_stack_push(i, &u->io); + + if (n) + return n; + else if (u->fr.child) + i->next = seek_pc; + else + return NW_OK; + + return NW_AGAIN; +} + +static int prepare_retval(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_fn *const fn = &i->fr.fn; + const struct nw_return *const r = &fn->ret; + const enum nw_type t = r->type; + size_t sz; + + if (nwp_type_sz(t, &sz)) + { + static const char *const exc = "invalid return type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)t); +#endif + return -1; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->retval; + io.n = sz; + u->io = io; + i->push_type = t; + i->next = push_return_value; + } + + return 0; +} + +static enum nw_state pop_params(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_fn *const fn = &i->fr.fn; + const struct nw_return *const r = &fn->ret; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + else if (r->count) + { + if (prepare_retval(i)) + return NW_FATAL; + } + else if (u->fr.child) + i->next = seek_pc; + else + return NW_OK; + + return NW_AGAIN; +} + +static enum nw_state get_param_type(struct nw_interp *const i) +{ + nw_varint7 type; + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = nwp_varint7(cfg, &u->leb128, &type, cfg->user); + enum nw_type vtype; + size_t sz; + + if (n) + return n; + else if (nwp_get_type(type, &vtype) || nwp_type_sz(vtype, &sz)) + { + static const char *const exc = "invalid param type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)type); +#endif + return NW_FATAL; + } + + u->sz += sz; + + if (++u->entry_i >= i->fr.fn.param_count) + { + struct nw_sm_io io = {0}; + + io.n = u->sz; + u->io = io; + i->next = pop_params; + } + + return NW_AGAIN; +} + +static enum nw_state seek_param_types(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_io_cfg *const cfg = &i->cfg.io; + const enum nw_state n = cfg->seek(i->fr.fn.param_types, cfg->user); + + if (n) + return n; + + u->entry_i = 0; + i->next = get_param_type; + return NW_AGAIN; +} + +static enum nw_state check_retval(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_fn *const fn = &i->fr.fn; + const size_t addr = nwp_stack_ptr(i), start = i->fr.local_start; + const struct nw_return *const r = &fn->ret; + + if (addr != start) + { + static const char *const exc = "mismatched stack address"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: expected: %lu, got: %lu\n", exc, + (unsigned long)start, (unsigned long)addr); +#endif + return NW_FATAL; + } + else if (fn->param_count) + i->next = seek_param_types; + else if (r->count) + { + if (prepare_retval(i)) + return NW_FATAL; + } + else if (u->fr.child) + i->next = seek_pc; + else + return NW_OK; + + return NW_AGAIN; +} + +static enum nw_state pop_meta(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + else if (u->pending) + nwp_find_local(i, &u->fl, u->pending - 1, prepare_pop, NULL); + else + return check_retval(i); + + return NW_AGAIN; +} + +static enum nw_state pop_local(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + struct nw_local_meta *const m = &u->fl.meta; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + + u->pending--; + + if (++u->entry_i >= m->entry_count) + { + struct nw_sm_io io = {0}; + + io.buf = m; + io.n = sizeof *m; + u->io = io; + u->entry_i = 0; + i->next = pop_meta; + } + else if (u->pending) + return prepare_pop(i); + else + { + static const char *const exc = "entry and local count mismatch"; +#ifdef NW_LOG + const unsigned long pending = m->entry_count - u->entry_i; +#endif + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %lu locals pending\n", exc, pending); +#endif + return NW_FATAL; + } + + return NW_AGAIN; +} + +static enum nw_state prepare_pop(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_local_meta *const m = &u->fl.meta; + size_t sz; + + if (nwp_type_sz(m->type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)m->type); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->value; + io.n = sz; + u->io = io; + i->next = pop_local; + } + + return NW_AGAIN; +} + +static enum nw_state pop_frame(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_frame *const fr = &i->fr; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + else if (fr->local_count) + { + u->pending = fr->local_count; + nwp_find_local(i, &u->fl, u->pending - 1, prepare_pop, NULL); + } + else + return check_retval(i); + + return NW_AGAIN; +} + +static enum nw_state pop_return_value(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const enum nw_state n = nwp_stack_pop(i, &u->io); + + if (n) + return n; + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->fr; + io.n = sizeof u->fr; + u->io = io; + i->next = pop_frame; + } + + return NW_AGAIN; +} + +static enum nw_state run(struct nw_interp *const i) +{ + struct nw_i_sm_unwind *const u = &i->sm.unwind; + const struct nw_frame *const fr = &i->fr; + const struct nw_fn *const fn = &fr->fn; + const struct nw_return *const r = &fn->ret; + + if (r->count) + { + size_t sz, end, addr; + + if (nwp_type_sz(r->type, &sz)) + { + static const char *const exc = "invalid type"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: %#x\n", exc, (unsigned)r->type); +#endif + return NW_FATAL; + } + + end = fr->local_end; + addr = nwp_stack_ptr(i); + + if (addr < sz || addr < sizeof *fr) + { + static const char *const exc = "stack underflow"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s\n", exc); +#endif + return NW_FATAL; + } + else if ((addr -= sizeof *fr + sz) != end) + { + static const char *const exc = "stack memory leak"; + + i->exception = exc; +#ifdef NW_LOG + nwp_log("%s: expected: %lu, got: %lu\n", exc, (unsigned long)end, + (unsigned long)addr); +#endif + return NW_FATAL; + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->retval; + io.n = sz; + u->io = io; + i->next = pop_return_value; + } + } + else + { + struct nw_sm_io io = {0}; + + io.buf = &u->fr; + io.n = sizeof u->fr; + u->io = io; + i->next = pop_frame; + } + + return NW_AGAIN; +} + +void nwp_unwind(struct nw_interp *const i) +{ + const struct nw_i_sm_unwind u = {0}; + + i->sm.unwind = u; + i->next = run; +} |
