1 /**
2 * \file wasmtime/func.hh
3 */
4
5 #ifndef WASMTIME_FUNC_HH
6 #define WASMTIME_FUNC_HH
7
8 #include <array>
9 #include <wasmtime/error.hh>
10 #include <wasmtime/extern_declare.hh>
11 #include <wasmtime/func.h>
12 #include <wasmtime/span.hh>
13 #include <wasmtime/store.hh>
14 #include <wasmtime/trap.hh>
15 #include <wasmtime/types/func.hh>
16 #include <wasmtime/types/val.hh>
17 #include <wasmtime/val.hh>
18
19 namespace wasmtime {
20
21 /**
22 * \brief Structure provided to host functions to lookup caller information or
23 * acquire a `Store::Context`.
24 *
25 * This structure is passed to all host functions created with `Func`. It can be
26 * used to create a `Store::Context`.
27 */
28 class Caller {
29 friend class Func;
30 friend class Store;
31 wasmtime_caller_t *ptr;
Caller(wasmtime_caller_t * ptr)32 Caller(wasmtime_caller_t *ptr) : ptr(ptr) {}
33
34 public:
35 /// Attempts to load an exported item from the calling instance.
36 ///
37 /// For more information see the Rust documentation -
38 /// https://docs.wasmtime.dev/api/wasmtime/struct.Caller.html#method.get_export
39 std::optional<Extern> get_export(std::string_view name);
40
41 /// Explicitly acquire a `Store::Context` from this `Caller`.
context()42 Store::Context context() { return this; }
43 };
44
Context(Caller & caller)45 inline Store::Context::Context(Caller &caller)
46 : Context(wasmtime_caller_context(caller.ptr)) {}
Context(Caller * caller)47 inline Store::Context::Context(Caller *caller) : Context(*caller) {}
48
49 namespace detail {
50
51 /// A "trait" for native types that correspond to WebAssembly types for use with
52 /// `Func::wrap` and `TypedFunc::call`
53 template <typename T> struct WasmType {
54 static const bool valid = false;
55 };
56
57 /// Helper macro to define `WasmType` definitions for primitive types like
58 /// int32_t and such.
59 // NOLINTNEXTLINE
60 #define NATIVE_WASM_TYPE(native, valkind, field) \
61 template <> struct WasmType<native> { \
62 static const bool valid = true; \
63 static const ValKind kind = ValKind::valkind; \
64 static void store(Store::Context cx, wasmtime_val_raw_t *p, \
65 const native &t) { \
66 (void)cx; \
67 p->field = t; \
68 } \
69 static native load(Store::Context cx, wasmtime_val_raw_t *p) { \
70 (void)cx; \
71 return p->field; \
72 } \
73 };
74
75 NATIVE_WASM_TYPE(int32_t, I32, i32)
76 NATIVE_WASM_TYPE(uint32_t, I32, i32)
77 NATIVE_WASM_TYPE(int64_t, I64, i64)
78 NATIVE_WASM_TYPE(uint64_t, I64, i64)
79 NATIVE_WASM_TYPE(float, F32, f32)
80 NATIVE_WASM_TYPE(double, F64, f64)
81
82 #undef NATIVE_WASM_TYPE
83
84 /// Type information for `externref`, represented on the host as an optional
85 /// `ExternRef`.
86 template <> struct WasmType<std::optional<ExternRef>> {
87 static const bool valid = true;
88 static const ValKind kind = ValKind::ExternRef;
storewasmtime::detail::WasmType89 static void store(Store::Context cx, wasmtime_val_raw_t *p,
90 std::optional<ExternRef> &&ref) {
91 if (ref) {
92 p->externref = ref->take_raw(cx);
93 } else {
94 p->externref = 0;
95 }
96 }
storewasmtime::detail::WasmType97 static void store(Store::Context cx, wasmtime_val_raw_t *p,
98 const std::optional<ExternRef> &ref) {
99 if (ref) {
100 p->externref = ref->borrow_raw(cx);
101 } else {
102 p->externref = 0;
103 }
104 }
loadwasmtime::detail::WasmType105 static std::optional<ExternRef> load(Store::Context cx,
106 wasmtime_val_raw_t *p) {
107 if (p->externref == 0) {
108 return std::nullopt;
109 }
110 wasmtime_externref_t val;
111 wasmtime_externref_from_raw(cx.capi(), p->externref, &val);
112 return ExternRef(val);
113 }
114 };
115
116 /// Type information for the `V128` host value used as a wasm value.
117 template <> struct WasmType<V128> {
118 static const bool valid = true;
119 static const ValKind kind = ValKind::V128;
storewasmtime::detail::WasmType120 static void store(Store::Context cx, wasmtime_val_raw_t *p, const V128 &t) {
121 (void)cx;
122 memcpy(&p->v128[0], &t.v128[0], sizeof(wasmtime_v128));
123 }
loadwasmtime::detail::WasmType124 static V128 load(Store::Context cx, wasmtime_val_raw_t *p) {
125 (void)cx;
126 return p->v128;
127 }
128 };
129
130 /// A "trait" for a list of types and operations on them, used for `Func::wrap`
131 /// and `TypedFunc::call`
132 ///
133 /// The base case is a single type which is a list of one element.
134 template <typename T> struct WasmTypeList {
135 static const bool valid = WasmType<T>::valid;
136 static const size_t size = 1;
matcheswasmtime::detail::WasmTypeList137 static bool matches(ValType::ListRef types) {
138 return WasmTypeList<std::tuple<T>>::matches(types);
139 }
storewasmtime::detail::WasmTypeList140 static void store(Store::Context cx, wasmtime_val_raw_t *storage, T &&t) {
141 WasmType<T>::store(cx, storage, t);
142 }
storewasmtime::detail::WasmTypeList143 static void store(Store::Context cx, wasmtime_val_raw_t *storage,
144 const T &t) {
145 WasmType<T>::store(cx, storage, t);
146 }
loadwasmtime::detail::WasmTypeList147 static T load(Store::Context cx, wasmtime_val_raw_t *storage) {
148 return WasmType<T>::load(cx, storage);
149 }
typeswasmtime::detail::WasmTypeList150 static std::vector<ValType> types() { return {WasmType<T>::kind}; }
151 };
152
153 /// std::monostate translates to an empty list of types.
154 template <> struct WasmTypeList<std::monostate> {
155 static const bool valid = true;
156 static const size_t size = 0;
matcheswasmtime::detail::WasmTypeList157 static bool matches(ValType::ListRef types) { return types.size() == 0; }
storewasmtime::detail::WasmTypeList158 static void store(Store::Context cx, wasmtime_val_raw_t *storage,
159 const std::monostate &t) {
160 (void)cx;
161 (void)storage;
162 (void)t;
163 }
loadwasmtime::detail::WasmTypeList164 static std::monostate load(Store::Context cx, wasmtime_val_raw_t *storage) {
165 (void)cx;
166 (void)storage;
167 return std::monostate();
168 }
typeswasmtime::detail::WasmTypeList169 static std::vector<ValType> types() { return {}; }
170 };
171
172 /// std::tuple<> translates to the corresponding list of types
173 template <typename... T> struct WasmTypeList<std::tuple<T...>> {
174 static const bool valid = (WasmType<T>::valid && ...);
175 static const size_t size = sizeof...(T);
matcheswasmtime::detail::WasmTypeList176 static bool matches(ValType::ListRef types) {
177 if (types.size() != size) {
178 return false;
179 }
180 size_t n = 0;
181 return ((WasmType<T>::kind == types.begin()[n++].kind()) && ...);
182 }
storewasmtime::detail::WasmTypeList183 static void store(Store::Context cx, wasmtime_val_raw_t *storage,
184 std::tuple<T...> &&t) {
185 size_t n = 0;
186 std::apply(
187 [&](auto &...val) {
188 (WasmType<T>::store(cx, &storage[n++], val), ...); // NOLINT
189 },
190 t);
191 }
storewasmtime::detail::WasmTypeList192 static void store(Store::Context cx, wasmtime_val_raw_t *storage,
193 const std::tuple<T...> &t) {
194 size_t n = 0;
195 std::apply(
196 [&](const auto &...val) {
197 (WasmType<T>::store(cx, &storage[n++], val), ...); // NOLINT
198 },
199 t);
200 }
loadwasmtime::detail::WasmTypeList201 static std::tuple<T...> load(Store::Context cx, wasmtime_val_raw_t *storage) {
202 (void)cx;
203 return std::tuple<T...>{WasmType<T>::load(cx, storage++)...}; // NOLINT
204 }
typeswasmtime::detail::WasmTypeList205 static std::vector<ValType> types() { return {WasmType<T>::kind...}; }
206 };
207
208 /// A "trait" for what can be returned from closures specified to `Func::wrap`.
209 ///
210 /// The base case here is a bare return value like `int32_t`.
211 template <typename R> struct WasmHostRet {
212 using Results = WasmTypeList<R>;
213
214 template <typename F, typename... A>
invokewasmtime::detail::WasmHostRet215 static std::optional<Trap> invoke(F f, Caller cx, wasmtime_val_raw_t *raw,
216 A... args) {
217 auto ret = f(args...);
218 Results::store(cx, raw, ret);
219 return std::nullopt;
220 }
221 };
222
223 /// Host functions can return nothing
224 template <> struct WasmHostRet<void> {
225 using Results = WasmTypeList<std::tuple<>>;
226
227 template <typename F, typename... A>
invokewasmtime::detail::WasmHostRet228 static std::optional<Trap> invoke(F f, Caller cx, wasmtime_val_raw_t *raw,
229 A... args) {
230 (void)cx;
231 (void)raw;
232 f(args...);
233 return std::nullopt;
234 }
235 };
236
237 // Alternative method of returning "nothing" (also enables `std::monostate` in
238 // the `R` type of `Result` below)
239 template <> struct WasmHostRet<std::monostate> : public WasmHostRet<void> {};
240
241 /// Host functions can return a result which allows them to also possibly return
242 /// a trap.
243 template <typename R> struct WasmHostRet<Result<R, Trap>> {
244 using Results = WasmTypeList<R>;
245
246 template <typename F, typename... A>
invokewasmtime::detail::WasmHostRet247 static std::optional<Trap> invoke(F f, Caller cx, wasmtime_val_raw_t *raw,
248 A... args) {
249 Result<R, Trap> ret = f(args...);
250 if (!ret) {
251 return ret.err();
252 }
253 Results::store(cx, raw, ret.ok());
254 return std::nullopt;
255 }
256 };
257
258 template <typename F, typename = void> struct WasmHostFunc;
259
260 /// Base type information for host free-function pointers being used as wasm
261 /// functions
262 template <typename R, typename... A> struct WasmHostFunc<R (*)(A...)> {
263 using Params = WasmTypeList<std::tuple<A...>>;
264 using Results = typename WasmHostRet<R>::Results;
265
266 template <typename F>
invokewasmtime::detail::WasmHostFunc267 static std::optional<Trap> invoke(F &f, Caller cx, wasmtime_val_raw_t *raw) {
268 auto params = Params::load(cx, raw);
269 return std::apply(
270 [&](const auto &...val) {
271 return WasmHostRet<R>::invoke(f, cx, raw, val...);
272 },
273 params);
274 }
275 };
276
277 /// Function type information, but with a `Caller` first parameter
278 template <typename R, typename... A>
279 struct WasmHostFunc<R (*)(Caller, A...)> : public WasmHostFunc<R (*)(A...)> {
280 // Override `invoke` here to pass the `cx` as the first parameter
281 template <typename F>
invokewasmtime::detail::WasmHostFunc282 static std::optional<Trap> invoke(F &f, Caller cx, wasmtime_val_raw_t *raw) {
283 auto params = WasmTypeList<std::tuple<A...>>::load(cx, raw);
284 return std::apply(
285 [&](const auto &...val) {
286 return WasmHostRet<R>::invoke(f, cx, raw, cx, val...);
287 },
288 params);
289 }
290 };
291
292 /// Function type information, but with a class method.
293 template <typename R, typename C, typename... A>
294 struct WasmHostFunc<R (C::*)(A...)> : public WasmHostFunc<R (*)(A...)> {};
295
296 /// Function type information, but with a const class method.
297 template <typename R, typename C, typename... A>
298 struct WasmHostFunc<R (C::*)(A...) const> : public WasmHostFunc<R (*)(A...)> {};
299
300 /// Function type information, but as a host method with a `Caller` first
301 /// parameter.
302 template <typename R, typename C, typename... A>
303 struct WasmHostFunc<R (C::*)(Caller, A...)>
304 : public WasmHostFunc<R (*)(Caller, A...)> {};
305
306 /// Function type information, but as a host const method with a `Caller`
307 /// first parameter.
308 template <typename R, typename C, typename... A>
309 struct WasmHostFunc<R (C::*)(Caller, A...) const>
310 : public WasmHostFunc<R (*)(Caller, A...)> {};
311
312 /// Base type information for host callables being used as wasm
313 /// functions
314 template <typename T>
315 struct WasmHostFunc<T, std::void_t<decltype(&T::operator())>>
316 : public WasmHostFunc<decltype(&T::operator())> {};
317
318 } // namespace detail
319
320 using namespace detail;
321
322 // forward-declaration for `Func::typed` below.
323 template <typename Params, typename Results> class TypedFunc;
324
325 /**
326 * \brief Representation of a WebAssembly function.
327 *
328 * This class represents a WebAssembly function, either created through
329 * instantiating a module or a host function.
330 *
331 * Note that this type does not itself own any resources. It points to resources
332 * owned within a `Store` and the `Store` must be passed in as the first
333 * argument to the functions defined on `Func`. Note that if the wrong `Store`
334 * is passed in then the process will be aborted.
335 */
336 class Func {
337 friend class Val;
338 friend class Instance;
339 friend class Linker;
340 template <typename Params, typename Results> friend class TypedFunc;
341
342 wasmtime_func_t func;
343
344 template <typename F>
raw_callback(void * env,wasmtime_caller_t * caller,const wasmtime_val_t * args,size_t nargs,wasmtime_val_t * results,size_t nresults)345 static wasm_trap_t *raw_callback(void *env, wasmtime_caller_t *caller,
346 const wasmtime_val_t *args, size_t nargs,
347 wasmtime_val_t *results, size_t nresults) {
348 static_assert(alignof(Val) == alignof(wasmtime_val_t));
349 static_assert(sizeof(Val) == sizeof(wasmtime_val_t));
350 F *func = reinterpret_cast<F *>(env); // NOLINT
351 Span<const Val> args_span(reinterpret_cast<const Val *>(args), // NOLINT
352 nargs);
353 Span<Val> results_span(reinterpret_cast<Val *>(results), // NOLINT
354 nresults);
355 Result<std::monostate, Trap> result =
356 (*func)(Caller(caller), args_span, results_span);
357 if (!result) {
358 return result.err().capi_release();
359 }
360 return nullptr;
361 }
362
363 template <typename F>
364 static wasm_trap_t *
raw_callback_unchecked(void * env,wasmtime_caller_t * caller,wasmtime_val_raw_t * args_and_results,size_t nargs_and_results)365 raw_callback_unchecked(void *env, wasmtime_caller_t *caller,
366 wasmtime_val_raw_t *args_and_results,
367 size_t nargs_and_results) {
368 (void)nargs_and_results;
369 using HostFunc = WasmHostFunc<F>;
370 Caller cx(caller);
371 F *func = reinterpret_cast<F *>(env); // NOLINT
372 auto trap = HostFunc::invoke(*func, cx, args_and_results);
373 if (trap) {
374 return trap->capi_release();
375 }
376 return nullptr;
377 }
378
raw_finalize(void * env)379 template <typename F> static void raw_finalize(void *env) {
380 std::unique_ptr<F> ptr(reinterpret_cast<F *>(env)); // NOLINT
381 }
382
383 public:
384 /// Creates a new function from the raw underlying C API representation.
Func(wasmtime_func_t func)385 Func(wasmtime_func_t func) : func(func) {}
386
387 /**
388 * \brief Creates a new host-defined function.
389 *
390 * This constructor is used to create a host function within the store
391 * provided. This is how WebAssembly can call into the host and make use of
392 * external functionality.
393 *
394 * > **Note**: host functions created this way are more flexible but not
395 * > as fast to call as those created by `Func::wrap`.
396 *
397 * \param cx the store to create the function within
398 * \param ty the type of the function that will be created
399 * \param f the host callback to be executed when this function is called.
400 *
401 * The parameter `f` is expected to be a lambda (or a lambda lookalike) which
402 * takes three parameters:
403 *
404 * * The first parameter is a `Caller` to get recursive access to the store
405 * and other caller state.
406 * * The second parameter is a `Span<const Val>` which is the list of
407 * parameters to the function. These parameters are guaranteed to be of the
408 * types specified by `ty` when constructing this function.
409 * * The last argument is `Span<Val>` which is where to write the return
410 * values of the function. The function must produce the types of values
411 * specified by `ty` or otherwise a trap will be raised.
412 *
413 * The parameter `f` is expected to return `Result<std::monostate, Trap>`.
414 * This allows `f` to raise a trap if desired, or otherwise return no trap and
415 * finish successfully. If a trap is raised then the results pointer does not
416 * need to be written to.
417 */
418 template <typename F,
419 std::enable_if_t<
420 std::is_invocable_r_v<Result<std::monostate, Trap>, F, Caller,
421 Span<const Val>, Span<Val>>,
422 bool> = true>
Func(Store::Context cx,const FuncType & ty,F f)423 Func(Store::Context cx, const FuncType &ty, F f) : func({}) {
424 wasmtime_func_new(cx.ptr, ty.ptr.get(), raw_callback<F>,
425 std::make_unique<F>(f).release(), raw_finalize<F>, &func);
426 }
427
428 /**
429 * \brief Creates a new host function from the provided callback `f`,
430 * inferring the WebAssembly function type from the host signature.
431 *
432 * This function is akin to the `Func` constructor except that the WebAssembly
433 * type does not need to be specified and additionally the signature of `f`
434 * is different. The main goal of this function is to enable WebAssembly to
435 * call the function `f` as-fast-as-possible without having to validate any
436 * types or such.
437 *
438 * The function `f` can optionally take a `Caller` as its first parameter,
439 * but otherwise its arguments are translated to WebAssembly types:
440 *
441 * * `int32_t`, `uint32_t` - `i32`
442 * * `int64_t`, `uint64_t` - `i64`
443 * * `float` - `f32`
444 * * `double` - `f64`
445 * * `std::optional<Func>` - `funcref`
446 * * `std::optional<ExternRef>` - `externref`
447 * * `wasmtime::V128` - `v128`
448 *
449 * The function may only take these arguments and if it takes any other kinds
450 * of arguments then it will fail to compile.
451 *
452 * The function may return a few different flavors of return values:
453 *
454 * * `void` - interpreted as returning nothing
455 * * Any type above - interpreted as a singular return value.
456 * * `std::tuple<T...>` where `T` is one of the valid argument types -
457 * interpreted as returning multiple values.
458 * * `Result<T, Trap>` where `T` is another valid return type - interpreted as
459 * a function that returns `T` to wasm but is optionally allowed to also
460 * raise a trap.
461 *
462 * It's recommended, if possible, to use this function over the `Func`
463 * constructor since this is generally easier to work with and also enables
464 * a faster path for WebAssembly to call this function.
465 */
466 template <typename F,
467 std::enable_if_t<WasmHostFunc<F>::Params::valid, bool> = true,
468 std::enable_if_t<WasmHostFunc<F>::Results::valid, bool> = true>
wrap(Store::Context cx,F f)469 static Func wrap(Store::Context cx, F f) {
470 using HostFunc = WasmHostFunc<F>;
471 auto params = HostFunc::Params::types();
472 auto results = HostFunc::Results::types();
473 auto ty = FuncType::from_iters(params, results);
474 wasmtime_func_t func;
475 wasmtime_func_new_unchecked(cx.ptr, ty.ptr.get(), raw_callback_unchecked<F>,
476 std::make_unique<F>(f).release(),
477 raw_finalize<F>, &func);
478 return func;
479 }
480
481 /**
482 * \brief Invoke a WebAssembly function.
483 *
484 * This function will execute this WebAssembly function. This function muts be
485 * defined within the `cx`'s store provided. The `params` argument is the list
486 * of parameters that are passed to the wasm function, and the types of the
487 * values within `params` must match the type signature of this function.
488 *
489 * This may return one of three values:
490 *
491 * * First the function could succeed, returning a vector of values
492 * representing the results of the function.
493 * * Otherwise a `Trap` might be generated by the WebAssembly function.
494 * * Finally an `Error` could be returned indicating that `params` were not of
495 * the right type.
496 *
497 * > **Note**: for optimized calls into WebAssembly where the function
498 * > signature is statically known it's recommended to use `Func::typed` and
499 * > `TypedFunc::call`.
500 */
501 template <typename I>
call(Store::Context cx,const I & begin,const I & end) const502 TrapResult<std::vector<Val>> call(Store::Context cx, const I &begin,
503 const I &end) const {
504 std::vector<wasmtime_val_t> raw_params;
505 raw_params.reserve(end - begin);
506 for (auto i = begin; i != end; i++) {
507 raw_params.push_back(i->val);
508 }
509 size_t nresults = this->type(cx)->results().size();
510 std::vector<wasmtime_val_t> raw_results(nresults);
511
512 wasm_trap_t *trap = nullptr;
513 auto *error =
514 wasmtime_func_call(cx.ptr, &func, raw_params.data(), raw_params.size(),
515 raw_results.data(), raw_results.capacity(), &trap);
516 if (error != nullptr) {
517 return TrapError(Error(error));
518 }
519 if (trap != nullptr) {
520 return TrapError(Trap(trap));
521 }
522
523 std::vector<Val> results;
524 results.reserve(nresults);
525 for (size_t i = 0; i < nresults; i++) {
526 results.push_back(raw_results[i]);
527 }
528 return results;
529 }
530
531 /**
532 * \brief Helper function for `call(Store::Context cx, const I &begin, const I
533 * &end)`
534 *
535 * \see call(Store::Context cx, const I &begin, const I &end)
536 */
call(Store::Context cx,const std::vector<Val> & params) const537 TrapResult<std::vector<Val>> call(Store::Context cx,
538 const std::vector<Val> ¶ms) const {
539 return this->call(cx, params.begin(), params.end());
540 }
541
542 /**
543 * \brief Helper function for `call(Store::Context cx, const I &begin, const I
544 * &end)`
545 *
546 * \see call(Store::Context cx, const I &begin, const I &end)
547 */
548 TrapResult<std::vector<Val>>
call(Store::Context cx,const std::initializer_list<Val> & params) const549 call(Store::Context cx, const std::initializer_list<Val> ¶ms) const {
550 return this->call(cx, params.begin(), params.end());
551 }
552
553 /// Returns the type of this function.
type(Store::Context cx) const554 FuncType type(Store::Context cx) const {
555 return wasmtime_func_type(cx.ptr, &func);
556 }
557
558 /**
559 * \brief Statically checks this function against the provided types.
560 *
561 * This function will check whether it takes the statically known `Params`
562 * and returns the statically known `Results`. If the type check succeeds then
563 * a `TypedFunc` is returned which enables a faster method of invoking
564 * WebAssembly functions.
565 *
566 * The `Params` and `Results` specified as template parameters here are the
567 * parameters and results of the wasm function. They can either be a bare
568 * type which means that the wasm takes/returns one value, or they can be a
569 * `std::tuple<T...>` of types to represent multiple arguments or multiple
570 * returns.
571 *
572 * The valid types for this function are those mentioned as the arguments
573 * for `Func::wrap`.
574 */
575 template <typename Params, typename Results,
576 std::enable_if_t<WasmTypeList<Params>::valid, bool> = true,
577 std::enable_if_t<WasmTypeList<Results>::valid, bool> = true>
typed(Store::Context cx) const578 Result<TypedFunc<Params, Results>, Trap> typed(Store::Context cx) const {
579 auto ty = this->type(cx);
580 if (!WasmTypeList<Params>::matches(ty->params()) ||
581 !WasmTypeList<Results>::matches(ty->results())) {
582 return Trap("static type for this function does not match actual type");
583 }
584 TypedFunc<Params, Results> ret(*this);
585 return ret;
586 }
587
588 /// Returns the raw underlying C API function this is using.
capi() const589 const wasmtime_func_t &capi() const { return func; }
590 };
591
592 /**
593 * \brief A version of a WebAssembly `Func` where the type signature of the
594 * function is statically known.
595 */
596 template <typename Params, typename Results> class TypedFunc {
597 friend class Func;
598 Func f;
TypedFunc(Func func)599 TypedFunc(Func func) : f(func) {}
600
601 public:
602 /**
603 * \brief Calls this function with the provided parameters.
604 *
605 * This function is akin to `Func::call` except that since static type
606 * information is available it statically takes its parameters and statically
607 * returns its results.
608 *
609 * Note that this function still may return a `Trap` indicating that calling
610 * the WebAssembly function failed.
611 */
call(Store::Context cx,const Params & params) const612 TrapResult<Results> call(Store::Context cx, const Params ¶ms) const {
613 std::array<wasmtime_val_raw_t, std::max(WasmTypeList<Params>::size,
614 WasmTypeList<Results>::size)>
615 storage;
616 wasmtime_val_raw_t *ptr = storage.data();
617 if (ptr == nullptr)
618 ptr = reinterpret_cast<wasmtime_val_raw_t *>(alignof(wasmtime_val_raw_t));
619 WasmTypeList<Params>::store(cx, ptr, params);
620 wasm_trap_t *trap = nullptr;
621 auto *error = wasmtime_func_call_unchecked(cx.capi(), &f.func, ptr,
622 storage.size(), &trap);
623 if (error != nullptr) {
624 return TrapError(Error(error));
625 }
626 if (trap != nullptr) {
627 return TrapError(Trap(trap));
628 }
629 return WasmTypeList<Results>::load(cx, ptr);
630 }
631
632 /// Returns the underlying un-typed `Func` for this function.
func() const633 const Func &func() const { return f; }
634 };
635
Val(std::optional<Func> func)636 inline Val::Val(std::optional<Func> func) : val{} {
637 val.kind = WASMTIME_FUNCREF;
638 if (func) {
639 val.of.funcref = (*func).func;
640 } else {
641 wasmtime_funcref_set_null(&val.of.funcref);
642 }
643 }
644
Val(Func func)645 inline Val::Val(Func func) : Val(std::optional(func)) {}
Val(ExternRef ptr)646 inline Val::Val(ExternRef ptr) : Val(std::optional(ptr)) {}
Val(AnyRef ptr)647 inline Val::Val(AnyRef ptr) : Val(std::optional(ptr)) {}
648
funcref() const649 inline std::optional<Func> Val::funcref() const {
650 if (val.kind != WASMTIME_FUNCREF) {
651 std::abort();
652 }
653 if (val.of.funcref.store_id == 0) {
654 return std::nullopt;
655 }
656 return Func(val.of.funcref);
657 }
658
659 /// Definition for the `funcref` native wasm type
660 template <> struct detail::WasmType<std::optional<Func>> {
661 /// @private
662 static const bool valid = true;
663 /// @private
664 static const ValKind kind = ValKind::FuncRef;
665 /// @private
storewasmtime::detail::WasmType666 static void store(Store::Context cx, wasmtime_val_raw_t *p,
667 const std::optional<Func> func) {
668 if (func) {
669 p->funcref = wasmtime_func_to_raw(cx.capi(), &func->capi());
670 } else {
671 p->funcref = 0;
672 }
673 }
674 /// @private
loadwasmtime::detail::WasmType675 static std::optional<Func> load(Store::Context cx, wasmtime_val_raw_t *p) {
676 if (p->funcref == 0) {
677 return std::nullopt;
678 }
679 wasmtime_func_t ret;
680 wasmtime_func_from_raw(cx.capi(), p->funcref, &ret);
681 return ret;
682 }
683 };
684
685 } // namespace wasmtime
686
687 #endif // WASMTIME_FUNC_HH
688