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/call_import.c | |
| download | nanowasm-6d9d80362f9932bbc87e162b8ef7df06c73e27e1.tar.gz | |
First commit
Diffstat (limited to 'src/routines/call_import.c')
| -rw-r--r-- | src/routines/call_import.c | 477 |
1 files changed, 477 insertions, 0 deletions
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); +} |
