#ifndef WASMTIME_FUNC_HH
#define WASMTIME_FUNC_HH
#include <wasmtime/_func_class.hh>
#include <wasmtime/_val_class.hh>
namespace wasmtime {
namespace detail {
#define NATIVE_WASM_TYPE(native, valkind, field) \
template <> struct WasmType<native> { \
static const bool valid = true; \
static ValType valtype() { return ValType::valkind(); } \
static void store(Store::Context cx, wasmtime_val_raw_t *p, \
const native &t) { \
(void)cx; \
p->field = t; \
} \
static native load(Store::Context cx, wasmtime_val_raw_t *p) { \
(void)cx; \
return p->field; \
} \
};
NATIVE_WASM_TYPE(int32_t, i32, i32)
NATIVE_WASM_TYPE(uint32_t, i32, i32)
NATIVE_WASM_TYPE(int64_t, i64, i64)
NATIVE_WASM_TYPE(uint64_t, i64, i64)
NATIVE_WASM_TYPE(float, f32, f32)
NATIVE_WASM_TYPE(double, f64, f64)
#undef NATIVE_WASM_TYPE
template <> struct WasmType<std::optional<Func>> {
static const bool valid = true;
static ValType valtype() { return ValType::funcref(); }
static void store(Store::Context cx, wasmtime_val_raw_t *p,
const std::optional<Func> func) {
if (func) {
p->funcref = wasmtime_func_to_raw(cx.capi(), &func->capi());
} else {
p->funcref = 0;
}
}
static std::optional<Func> load(Store::Context cx, wasmtime_val_raw_t *p) {
if (p->funcref == 0) {
return std::nullopt;
}
wasmtime_func_t ret;
wasmtime_func_from_raw(cx.capi(), p->funcref, &ret);
return ret;
}
};
template <> struct WasmTypeList<std::monostate> {
static const bool valid = true;
static const size_t size = 0;
static bool matches(ValType::ListRef types) { return types.size() == 0; }
static void store(Store::Context cx, wasmtime_val_raw_t *storage,
const std::monostate &t) {
(void)cx;
(void)storage;
(void)t;
}
static std::monostate load(Store::Context cx, wasmtime_val_raw_t *storage) {
(void)cx;
(void)storage;
return std::monostate();
}
static std::vector<ValType> types() { return {}; }
};
template <typename... T> struct WasmTypeList<std::tuple<T...>> {
static const bool valid = (WasmType<T>::valid && ...);
static const size_t size = sizeof...(T);
static bool matches(ValType::ListRef types) {
if (types.size() != size) {
return false;
}
size_t n = 0;
return ((WasmType<T>::valtype() == types.begin()[n++]) && ...);
}
static void store(Store::Context cx, wasmtime_val_raw_t *storage,
std::tuple<T...> &&t) {
size_t n = 0;
std::apply(
[&](auto &...val) {
(WasmType<T>::store(cx, &storage[n++], val), ...); },
t);
}
static void store(Store::Context cx, wasmtime_val_raw_t *storage,
const std::tuple<T...> &t) {
size_t n = 0;
std::apply(
[&](const auto &...val) {
(WasmType<T>::store(cx, &storage[n++], val), ...); },
t);
}
static std::tuple<T...> load(Store::Context cx, wasmtime_val_raw_t *storage) {
(void)cx;
return std::tuple<T...>{WasmType<T>::load(cx, storage++)...}; }
static std::vector<ValType> types() { return {WasmType<T>::valtype()...}; }
};
template <> struct WasmHostRet<void> {
using Results = WasmTypeList<std::tuple<>>;
template <typename F, typename... A>
static std::optional<Trap> invoke(F f, Caller cx, wasmtime_val_raw_t *raw,
A... args) {
(void)cx;
(void)raw;
f(args...);
return std::nullopt;
}
};
template <> struct WasmHostRet<std::monostate> : public WasmHostRet<void> {};
template <typename R> struct WasmHostRet<Result<R, Trap>> {
using Results = WasmTypeList<R>;
template <typename F, typename... A>
static std::optional<Trap> invoke(F f, Caller cx, wasmtime_val_raw_t *raw,
A... args) {
Result<R, Trap> ret = f(args...);
if (!ret) {
return ret.err();
}
Results::store(cx, raw, ret.ok());
return std::nullopt;
}
};
template <typename R, typename... A> struct WasmHostFunc<R (*)(A...)> {
using Params = WasmTypeList<std::tuple<A...>>;
using Results = typename WasmHostRet<R>::Results;
template <typename F>
static std::optional<Trap> invoke(F &f, Caller cx, wasmtime_val_raw_t *raw) {
auto params = Params::load(cx, raw);
return std::apply(
[&](const auto &...val) {
return WasmHostRet<R>::invoke(f, cx, raw, val...);
},
params);
}
};
template <typename R, typename... A>
struct WasmHostFunc<R (*)(Caller, A...)> : public WasmHostFunc<R (*)(A...)> {
template <typename F>
static std::optional<Trap> invoke(F &f, Caller cx, wasmtime_val_raw_t *raw) {
auto params = WasmTypeList<std::tuple<A...>>::load(cx, raw);
return std::apply(
[&](const auto &...val) {
return WasmHostRet<R>::invoke(f, cx, raw, cx, val...);
},
params);
}
};
template <typename R, typename C, typename... A>
struct WasmHostFunc<R (C::*)(A...)> : public WasmHostFunc<R (*)(A...)> {};
template <typename R, typename C, typename... A>
struct WasmHostFunc<R (C::*)(A...) const> : public WasmHostFunc<R (*)(A...)> {};
template <typename R, typename C, typename... A>
struct WasmHostFunc<R (C::*)(Caller, A...)>
: public WasmHostFunc<R (*)(Caller, A...)> {};
template <typename R, typename C, typename... A>
struct WasmHostFunc<R (C::*)(Caller, A...) const>
: public WasmHostFunc<R (*)(Caller, A...)> {};
template <typename T>
struct WasmHostFunc<T, std::void_t<decltype(&T::operator())>>
: public WasmHostFunc<decltype(&T::operator())> {};
}
using namespace detail;
template <typename F>
inline wasm_trap_t *Func::raw_callback(void *env, wasmtime_caller_t *caller,
const wasmtime_val_t *args, size_t nargs,
wasmtime_val_t *results,
size_t nresults) {
static_assert(alignof(Val) == alignof(wasmtime_val_t));
static_assert(sizeof(Val) == sizeof(wasmtime_val_t));
F *func = reinterpret_cast<F *>(env); Span<const Val> args_span(reinterpret_cast<const Val *>(args), nargs);
Span<Val> results_span(reinterpret_cast<Val *>(results), nresults);
Result<std::monostate, Trap> result =
(*func)(Caller(caller), args_span, results_span);
if (!result) {
return result.err().capi_release();
}
return nullptr;
}
template <typename I>
inline TrapResult<std::vector<Val>>
Func::call(Store::Context cx, const I &begin, const I &end) const {
std::vector<wasmtime_val_t> raw_params;
raw_params.reserve(end - begin);
for (auto i = begin; i != end; i++) {
raw_params.push_back(i->val);
}
size_t nresults = this->type(cx)->results().size();
std::vector<wasmtime_val_t> raw_results(nresults);
wasm_trap_t *trap = nullptr;
auto *error =
wasmtime_func_call(cx.ptr, &func, raw_params.data(), raw_params.size(),
raw_results.data(), raw_results.capacity(), &trap);
if (error != nullptr) {
return TrapError(Error(error));
}
if (trap != nullptr) {
return TrapError(Trap(trap));
}
std::vector<Val> results;
results.reserve(nresults);
for (size_t i = 0; i < nresults; i++) {
results.push_back(raw_results[i]);
}
return results;
}
inline TrapResult<std::vector<Val>>
Func::call(Store::Context cx, const std::initializer_list<Val> ¶ms) const {
return this->call(cx, params.begin(), params.end());
}
inline TrapResult<std::vector<Val>>
Func::call(Store::Context cx, const std::vector<Val> ¶ms) const {
return this->call(cx, params.begin(), params.end());
}
}
#endif