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