1 use super::invoke_wasm_and_catch_traps;
2 use crate::prelude::*;
3 use crate::runtime::vm::VMFuncRef;
4 use crate::store::{AutoAssertNoGc, StoreOpaque};
5 use crate::{
6     AsContext, AsContextMut, Engine, Func, FuncType, HeapType, NoFunc, RefType, StoreContextMut,
7     ValRaw, ValType,
8 };
9 use core::ffi::c_void;
10 use core::marker;
11 use core::mem::{self, MaybeUninit};
12 use core::ptr::{self, NonNull};
13 use wasmtime_environ::VMSharedTypeIndex;
14 
15 /// A statically typed WebAssembly function.
16 ///
17 /// Values of this type represent statically type-checked WebAssembly functions.
18 /// The function within a [`TypedFunc`] is statically known to have `Params` as its
19 /// parameters and `Results` as its results.
20 ///
21 /// This structure is created via [`Func::typed`] or [`TypedFunc::new_unchecked`].
22 /// For more documentation about this see those methods.
23 pub struct TypedFunc<Params, Results> {
24     _a: marker::PhantomData<fn(Params) -> Results>,
25     ty: FuncType,
26     func: Func,
27 }
28 
29 impl<Params, Results> Clone for TypedFunc<Params, Results> {
clone(&self) -> TypedFunc<Params, Results>30     fn clone(&self) -> TypedFunc<Params, Results> {
31         Self {
32             _a: marker::PhantomData,
33             ty: self.ty.clone(),
34             func: self.func,
35         }
36     }
37 }
38 
39 impl<Params, Results> TypedFunc<Params, Results>
40 where
41     Params: WasmParams,
42     Results: WasmResults,
43 {
44     /// An unchecked version of [`Func::typed`] which does not perform a
45     /// typecheck and simply assumes that the type declared here matches the
46     /// type of this function.
47     ///
48     /// The semantics of this function are the same as [`Func::typed`] except
49     /// that no error is returned because no typechecking is done.
50     ///
51     /// # Unsafety
52     ///
53     /// This function only safe to call if `typed` would otherwise return `Ok`
54     /// for the same `Params` and `Results` specified. If `typed` would return
55     /// an error then the returned `TypedFunc` is memory unsafe to invoke.
new_unchecked(store: impl AsContext, func: Func) -> TypedFunc<Params, Results>56     pub unsafe fn new_unchecked(store: impl AsContext, func: Func) -> TypedFunc<Params, Results> {
57         let store = store.as_context().0;
58         unsafe { Self::_new_unchecked(store, func) }
59     }
60 
_new_unchecked( store: &StoreOpaque, func: Func, ) -> TypedFunc<Params, Results>61     pub(crate) unsafe fn _new_unchecked(
62         store: &StoreOpaque,
63         func: Func,
64     ) -> TypedFunc<Params, Results> {
65         let ty = func.load_ty(store);
66         TypedFunc {
67             _a: marker::PhantomData,
68             ty,
69             func,
70         }
71     }
72 
73     /// Returns the underlying [`Func`] that this is wrapping, losing the static
74     /// type information in the process.
func(&self) -> &Func75     pub fn func(&self) -> &Func {
76         &self.func
77     }
78 
79     /// Invokes this WebAssembly function with the specified parameters.
80     ///
81     /// Returns either the results of the call, or a [`Trap`] if one happened.
82     ///
83     /// For more information, see the [`Func::typed`] and [`Func::call`]
84     /// documentation.
85     ///
86     /// # Errors
87     ///
88     /// For more information on errors see the documentation on [`Func::call`].
89     ///
90     /// # Panics
91     ///
92     /// Panics if `store` does not contain this function.
93     ///
94     /// [`Trap`]: crate::Trap
95     #[inline]
call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results>96     pub fn call(&self, mut store: impl AsContextMut, params: Params) -> Result<Results> {
97         let mut store = store.as_context_mut();
98         store.0.validate_sync_call()?;
99         let func = self.func.vm_func_ref(store.0);
100         unsafe { Self::call_raw(&mut store, &self.ty, func, params) }
101     }
102 
103     /// Invokes this WebAssembly function with the specified parameters.
104     ///
105     /// Returns either the results of the call, or a [`Trap`] if one happened.
106     ///
107     /// For more information, see the [`Func::typed`] and [`Func::call_async`]
108     /// documentation.
109     ///
110     /// # Errors
111     ///
112     /// For more information on errors see the documentation on [`Func::call`].
113     ///
114     /// # Panics
115     ///
116     /// This function will panic if it is called when the underlying [`Func`] is
117     /// connected to a synchronous store.
118     ///
119     /// [`Trap`]: crate::Trap
120     #[cfg(feature = "async")]
call_async( &self, mut store: impl AsContextMut<Data: Send>, params: Params, ) -> Result<Results> where Params: Sync, Results: Sync,121     pub async fn call_async(
122         &self,
123         mut store: impl AsContextMut<Data: Send>,
124         params: Params,
125     ) -> Result<Results>
126     where
127         Params: Sync,
128         Results: Sync,
129     {
130         let mut store = store.as_context_mut();
131 
132         store
133             .on_fiber(|store| {
134                 let func = self.func.vm_func_ref(store.0);
135                 unsafe { Self::call_raw(store, &self.ty, func, params) }
136             })
137             .await?
138     }
139 
140     /// Do a raw call of a typed function.
141     ///
142     /// # Safety
143     ///
144     /// `func` must be of the given type, and it additionally must be a valid
145     /// store-owned pointer within the `store` provided.
call_raw<T>( store: &mut StoreContextMut<'_, T>, ty: &FuncType, func: ptr::NonNull<VMFuncRef>, params: Params, ) -> Result<Results>146     pub(crate) unsafe fn call_raw<T>(
147         store: &mut StoreContextMut<'_, T>,
148         ty: &FuncType,
149         func: ptr::NonNull<VMFuncRef>,
150         params: Params,
151     ) -> Result<Results> {
152         // double-check that params/results match for this function's type in
153         // debug mode.
154         //
155         // SAFETY: this function requires that `ptr` is a valid function
156         // pointer.
157         unsafe {
158             if cfg!(debug_assertions) {
159                 Self::debug_typecheck(store.0, func.as_ref().type_index);
160             }
161         }
162 
163         // Validate that all runtime values flowing into this store indeed
164         // belong within this store, otherwise it would be unsafe for store
165         // values to cross each other.
166 
167         union Storage<T: Copy, U: Copy> {
168             params: MaybeUninit<T>,
169             results: U,
170         }
171 
172         let mut storage = Storage::<Params::ValRawStorage, Results::ValRawStorage> {
173             params: MaybeUninit::uninit(),
174         };
175 
176         {
177             let mut store = AutoAssertNoGc::new(store.0);
178             // SAFETY: it's safe to use a union field here as the field itself
179             // is `MaybeUninit<_>` meaning nothing is accidentally considered
180             // initialized.
181             let dst: &mut MaybeUninit<_> = unsafe { &mut storage.params };
182             params.store(&mut store, ty, dst)?;
183         }
184 
185         // Try to capture only a single variable (a tuple) in the closure below.
186         // This means the size of the closure is one pointer and is much more
187         // efficient to move in memory. This closure is actually invoked on the
188         // other side of a C++ shim, so it can never be inlined enough to make
189         // the memory go away, so the size matters here for performance.
190         let mut captures = (func, storage);
191 
192         let result = invoke_wasm_and_catch_traps(store, |caller, vm| {
193             let (func_ref, storage) = &mut captures;
194             let storage_len = mem::size_of_val::<Storage<_, _>>(storage) / mem::size_of::<ValRaw>();
195             let storage: *mut Storage<_, _> = storage;
196             let storage = storage.cast::<ValRaw>();
197             let storage = core::ptr::slice_from_raw_parts_mut(storage, storage_len);
198             let storage = NonNull::new(storage).unwrap();
199 
200             // SAFETY: this function's own contract is that `func_ref` is safe
201             // to call and additionally that the params/results are correctly
202             // ascribed for this function call to be safe.
203             unsafe { VMFuncRef::array_call(*func_ref, vm, caller, storage) }
204         });
205 
206         let (_, storage) = captures;
207         result?;
208 
209         let mut store = AutoAssertNoGc::new(store.0);
210         // SAFETY: this function is itself unsafe to ensure that the result type
211         // ascription is correct for `Results` and matches the actual function.
212         // Additionally given the correct type ascription all of the `results`
213         // accessed here should be validly initialized.
214         unsafe { Ok(Results::load(&mut store, &storage.results)) }
215     }
216 
217     /// Purely a debug-mode assertion, not actually used in release builds.
debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex)218     fn debug_typecheck(store: &StoreOpaque, func: VMSharedTypeIndex) {
219         let ty = FuncType::from_shared_type_index(store.engine(), func);
220         Params::typecheck(store.engine(), ty.params(), TypeCheckPosition::Param)
221             .expect("params should match");
222         Results::typecheck(store.engine(), ty.results(), TypeCheckPosition::Result)
223             .expect("results should match");
224     }
225 }
226 
227 #[doc(hidden)]
228 #[derive(Copy, Clone)]
229 pub enum TypeCheckPosition {
230     Param,
231     Result,
232 }
233 
234 /// A trait implemented for types which can be arguments and results for
235 /// closures passed to [`Func::wrap`] as well as parameters to [`Func::typed`].
236 ///
237 /// This trait should not be implemented by user types. This trait may change at
238 /// any time internally. The types which implement this trait, however, are
239 /// stable over time.
240 ///
241 /// For more information see [`Func::wrap`] and [`Func::typed`]
242 pub unsafe trait WasmTy: Send {
243     // Do a "static" (aka at time of `func.typed::<P, R>()`) ahead-of-time type
244     // check for this type at the given position. You probably don't need to
245     // override this trait method.
246     #[doc(hidden)]
247     #[inline]
typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()>248     fn typecheck(engine: &Engine, actual: ValType, position: TypeCheckPosition) -> Result<()> {
249         let expected = Self::valtype();
250         debug_assert!(expected.comes_from_same_engine(engine));
251         debug_assert!(actual.comes_from_same_engine(engine));
252         match position {
253             // The caller is expecting to receive a `T` and the callee is
254             // actually returning a `U`, so ensure that `U <: T`.
255             TypeCheckPosition::Result => actual.ensure_matches(engine, &expected),
256             // The caller is expecting to pass a `T` and the callee is expecting
257             // to receive a `U`, so ensure that `T <: U`.
258             TypeCheckPosition::Param => match (expected.as_ref(), actual.as_ref()) {
259                 // ... except that this technically-correct check would overly
260                 // restrict the usefulness of our typed function APIs for the
261                 // specific case of concrete reference types. Let's work through
262                 // an example.
263                 //
264                 // Consider functions that take a `(ref param $some_func_type)`
265                 // parameter:
266                 //
267                 // * We cannot have a static `wasmtime::SomeFuncTypeRef` type
268                 //   that implements `WasmTy` specifically for `(ref null
269                 //   $some_func_type)` because Wasm modules, and their types,
270                 //   are loaded dynamically at runtime.
271                 //
272                 // * Therefore the embedder's only option for `T <: (ref null
273                 //   $some_func_type)` is `T = (ref null nofunc)` aka
274                 //   `Option<wasmtime::NoFunc>`.
275                 //
276                 // * But that static type means they can *only* pass in the null
277                 //   function reference as an argument to the typed function.
278                 //   This is way too restrictive! For ergonomics, we want them
279                 //   to be able to pass in a `wasmtime::Func` whose type is
280                 //   `$some_func_type`!
281                 //
282                 // To lift this constraint and enable better ergonomics for
283                 // embedders, we allow `top(T) <: top(U)` -- i.e. they are part
284                 // of the same type hierarchy and a dynamic cast could possibly
285                 // succeed -- for the specific case of concrete heap type
286                 // parameters, and fall back to dynamic type checks on the
287                 // arguments passed to each invocation, as necessary.
288                 (Some(expected_ref), Some(actual_ref)) if actual_ref.heap_type().is_concrete() => {
289                     expected_ref
290                         .heap_type()
291                         .top()
292                         .ensure_matches(engine, &actual_ref.heap_type().top())
293                 }
294                 _ => expected.ensure_matches(engine, &actual),
295             },
296         }
297     }
298 
299     // The value type that this Type represents.
300     #[doc(hidden)]
valtype() -> ValType301     fn valtype() -> ValType;
302 
303     #[doc(hidden)]
may_gc() -> bool304     fn may_gc() -> bool {
305         match Self::valtype() {
306             ValType::Ref(_) => true,
307             ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => false,
308         }
309     }
310 
311     // Dynamic checks that this value is being used with the correct store
312     // context.
313     #[doc(hidden)]
compatible_with_store(&self, store: &StoreOpaque) -> bool314     fn compatible_with_store(&self, store: &StoreOpaque) -> bool;
315 
316     // Dynamic checks that `self <: actual` for concrete type arguments. See the
317     // comment above in `WasmTy::typecheck`.
318     //
319     // Only ever called for concrete reference type arguments, so any type which
320     // is not in a type hierarchy with concrete reference types can implement
321     // this with `unreachable!()`.
322     #[doc(hidden)]
dynamic_concrete_type_check( &self, store: &StoreOpaque, nullable: bool, actual: &HeapType, ) -> Result<()>323     fn dynamic_concrete_type_check(
324         &self,
325         store: &StoreOpaque,
326         nullable: bool,
327         actual: &HeapType,
328     ) -> Result<()>;
329 
330     // Is this a GC-managed reference that actually points to a GC object? That
331     // is, `self` is *not* an `i31`, null reference, or uninhabited type.
332     //
333     // Note that it is okay if this returns false positives (i.e. `true` for
334     // `Rooted<AnyRef>` without actually looking up the rooted `anyref` in the
335     // store and reflecting on it to determine whether it is actually an
336     // `i31`). However, it is not okay if this returns false negatives.
337     #[doc(hidden)]
338     #[inline]
is_vmgcref_and_points_to_object(&self) -> bool339     fn is_vmgcref_and_points_to_object(&self) -> bool {
340         Self::valtype().is_vmgcref_type_and_points_to_object()
341     }
342 
343     // Store `self` into `ptr`.
344     //
345     // NB: We _must not_ trigger a GC when passing refs from host code into Wasm
346     // (e.g. returned from a host function or passed as arguments to a Wasm
347     // function). After insertion into the activations table, the reference is
348     // no longer rooted. If multiple references are being sent from the host
349     // into Wasm and we allowed GCs during insertion, then the following events
350     // could happen:
351     //
352     // * Reference A is inserted into the activations table. This does not
353     //   trigger a GC, but does fill the table to capacity.
354     //
355     // * The caller's reference to A is removed. Now the only reference to A is
356     //   from the activations table.
357     //
358     // * Reference B is inserted into the activations table. Because the table
359     //   is at capacity, a GC is triggered.
360     //
361     // * A is reclaimed because the only reference keeping it alive was the
362     //   activation table's reference (it isn't inside any Wasm frames on the
363     //   stack yet, so stack scanning and stack maps don't increment its
364     //   reference count).
365     //
366     // * We transfer control to Wasm, giving it A and B. Wasm uses A. That's a
367     //   use-after-free bug.
368     //
369     // In conclusion, to prevent uses-after-free bugs, we cannot GC while
370     // converting types into their raw ABI forms.
371     #[doc(hidden)]
store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>372     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>;
373 
374     // Load a version of `Self` from the `ptr` provided.
375     //
376     // # Safety
377     //
378     // This function is unsafe as it's up to the caller to ensure that `ptr` is
379     // valid for this given type.
380     #[doc(hidden)]
load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self381     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self;
382 }
383 
384 macro_rules! integers {
385     ($($primitive:ident/$get_primitive:ident => $ty:ident)*) => ($(
386         unsafe impl WasmTy for $primitive {
387             #[inline]
388             fn valtype() -> ValType {
389                 ValType::$ty
390             }
391             #[inline]
392             fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
393                 true
394             }
395             #[inline]
396             fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
397                 unreachable!()
398             }
399             #[inline]
400             fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
401                 ptr.write(ValRaw::$primitive(self));
402                 Ok(())
403             }
404             #[inline]
405             unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
406                 ptr.$get_primitive()
407             }
408         }
409     )*)
410 }
411 
412 integers! {
413     i32/get_i32 => I32
414     i64/get_i64 => I64
415     u32/get_u32 => I32
416     u64/get_u64 => I64
417 }
418 
419 macro_rules! floats {
420     ($($float:ident/$int:ident/$get_float:ident => $ty:ident)*) => ($(
421         unsafe impl WasmTy for $float {
422             #[inline]
423             fn valtype() -> ValType {
424                 ValType::$ty
425             }
426             #[inline]
427             fn compatible_with_store(&self, _: &StoreOpaque) -> bool {
428                 true
429             }
430             #[inline]
431             fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
432                 unreachable!()
433             }
434             #[inline]
435             fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
436                 ptr.write(ValRaw::$float(self.to_bits()));
437                 Ok(())
438             }
439             #[inline]
440             unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
441                 $float::from_bits(ptr.$get_float())
442             }
443         }
444     )*)
445 }
446 
447 floats! {
448     f32/u32/get_f32 => F32
449     f64/u64/get_f64 => F64
450 }
451 
452 unsafe impl WasmTy for NoFunc {
453     #[inline]
valtype() -> ValType454     fn valtype() -> ValType {
455         ValType::Ref(RefType::new(false, HeapType::NoFunc))
456     }
457 
458     #[inline]
compatible_with_store(&self, _store: &StoreOpaque) -> bool459     fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
460         match self._inner {}
461     }
462 
463     #[inline]
dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()>464     fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
465         match self._inner {}
466     }
467 
468     #[inline]
is_vmgcref_and_points_to_object(&self) -> bool469     fn is_vmgcref_and_points_to_object(&self) -> bool {
470         match self._inner {}
471     }
472 
473     #[inline]
store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()>474     fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
475         match self._inner {}
476     }
477 
478     #[inline]
load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self479     unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
480         unreachable!("NoFunc is uninhabited")
481     }
482 }
483 
484 unsafe impl WasmTy for Option<NoFunc> {
485     #[inline]
valtype() -> ValType486     fn valtype() -> ValType {
487         ValType::Ref(RefType::new(true, HeapType::NoFunc))
488     }
489 
490     #[inline]
compatible_with_store(&self, _store: &StoreOpaque) -> bool491     fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
492         true
493     }
494 
495     #[inline]
dynamic_concrete_type_check( &self, _: &StoreOpaque, nullable: bool, ty: &HeapType, ) -> Result<()>496     fn dynamic_concrete_type_check(
497         &self,
498         _: &StoreOpaque,
499         nullable: bool,
500         ty: &HeapType,
501     ) -> Result<()> {
502         if nullable {
503             // `(ref null nofunc) <: (ref null $f)` for all function types `$f`.
504             Ok(())
505         } else {
506             bail!("argument type mismatch: expected non-nullable (ref {ty}), found null reference")
507         }
508     }
509 
510     #[inline]
store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>511     fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
512         ptr.write(ValRaw::funcref(ptr::null_mut()));
513         Ok(())
514     }
515 
516     #[inline]
load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self517     unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
518         None
519     }
520 }
521 
522 unsafe impl WasmTy for Func {
523     #[inline]
valtype() -> ValType524     fn valtype() -> ValType {
525         ValType::Ref(RefType::new(false, HeapType::Func))
526     }
527 
528     #[inline]
compatible_with_store(&self, store: &StoreOpaque) -> bool529     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
530         self.store == store.id()
531     }
532 
533     #[inline]
dynamic_concrete_type_check( &self, store: &StoreOpaque, _nullable: bool, expected: &HeapType, ) -> Result<()>534     fn dynamic_concrete_type_check(
535         &self,
536         store: &StoreOpaque,
537         _nullable: bool,
538         expected: &HeapType,
539     ) -> Result<()> {
540         let expected = expected.unwrap_concrete_func();
541         self.ensure_matches_ty(store, expected)
542             .context("argument type mismatch for reference to concrete type")
543     }
544 
545     #[inline]
store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>546     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
547         let abi = self.vm_func_ref(store);
548         ptr.write(ValRaw::funcref(abi.cast::<c_void>().as_ptr()));
549         Ok(())
550     }
551 
552     #[inline]
load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self553     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
554         let p = NonNull::new(ptr.get_funcref()).unwrap().cast();
555 
556         // SAFETY: it's an unsafe contract of `load` that it's only provided
557         // valid wasm values owned by `store`.
558         unsafe { Func::from_vm_func_ref(store.id(), p) }
559     }
560 }
561 
562 unsafe impl WasmTy for Option<Func> {
563     #[inline]
valtype() -> ValType564     fn valtype() -> ValType {
565         ValType::FUNCREF
566     }
567 
568     #[inline]
compatible_with_store(&self, store: &StoreOpaque) -> bool569     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
570         if let Some(f) = self {
571             f.compatible_with_store(store)
572         } else {
573             true
574         }
575     }
576 
dynamic_concrete_type_check( &self, store: &StoreOpaque, nullable: bool, expected: &HeapType, ) -> Result<()>577     fn dynamic_concrete_type_check(
578         &self,
579         store: &StoreOpaque,
580         nullable: bool,
581         expected: &HeapType,
582     ) -> Result<()> {
583         if let Some(f) = self {
584             let expected = expected.unwrap_concrete_func();
585             f.ensure_matches_ty(store, expected)
586                 .context("argument type mismatch for reference to concrete type")
587         } else if nullable {
588             Ok(())
589         } else {
590             bail!(
591                 "argument type mismatch: expected non-nullable (ref {expected}), found null reference"
592             )
593         }
594     }
595 
596     #[inline]
store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()>597     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
598         let raw = if let Some(f) = self {
599             f.vm_func_ref(store).as_ptr()
600         } else {
601             ptr::null_mut()
602         };
603         ptr.write(ValRaw::funcref(raw.cast::<c_void>()));
604         Ok(())
605     }
606 
607     #[inline]
load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self608     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
609         let ptr = NonNull::new(ptr.get_funcref())?.cast();
610 
611         // SAFETY: it's an unsafe contract of `load` that it's only provided
612         // valid wasm values owned by `store`.
613         unsafe { Some(Func::from_vm_func_ref(store.id(), ptr)) }
614     }
615 }
616 
617 /// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of
618 /// parameters for wasm functions.
619 ///
620 /// This is implemented for bare types that can be passed to wasm as well as
621 /// tuples of those types.
622 pub unsafe trait WasmParams: Send {
623     #[doc(hidden)]
624     type ValRawStorage: Copy;
625 
626     #[doc(hidden)]
typecheck( engine: &Engine, params: impl ExactSizeIterator<Item = crate::ValType>, position: TypeCheckPosition, ) -> Result<()>627     fn typecheck(
628         engine: &Engine,
629         params: impl ExactSizeIterator<Item = crate::ValType>,
630         position: TypeCheckPosition,
631     ) -> Result<()>;
632 
633     #[doc(hidden)]
vmgcref_pointing_to_object_count(&self) -> usize634     fn vmgcref_pointing_to_object_count(&self) -> usize;
635 
636     #[doc(hidden)]
store( self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType, dst: &mut MaybeUninit<Self::ValRawStorage>, ) -> Result<()>637     fn store(
638         self,
639         store: &mut AutoAssertNoGc<'_>,
640         func_ty: &FuncType,
641         dst: &mut MaybeUninit<Self::ValRawStorage>,
642     ) -> Result<()>;
643 }
644 
645 // Forward an impl from `T` to `(T,)` for convenience if there's only one
646 // parameter.
647 unsafe impl<T> WasmParams for T
648 where
649     T: WasmTy,
650 {
651     type ValRawStorage = <(T,) as WasmParams>::ValRawStorage;
652 
typecheck( engine: &Engine, params: impl ExactSizeIterator<Item = crate::ValType>, position: TypeCheckPosition, ) -> Result<()>653     fn typecheck(
654         engine: &Engine,
655         params: impl ExactSizeIterator<Item = crate::ValType>,
656         position: TypeCheckPosition,
657     ) -> Result<()> {
658         <(T,) as WasmParams>::typecheck(engine, params, position)
659     }
660 
661     #[inline]
vmgcref_pointing_to_object_count(&self) -> usize662     fn vmgcref_pointing_to_object_count(&self) -> usize {
663         T::is_vmgcref_and_points_to_object(self) as usize
664     }
665 
666     #[inline]
store( self, store: &mut AutoAssertNoGc<'_>, func_ty: &FuncType, dst: &mut MaybeUninit<Self::ValRawStorage>, ) -> Result<()>667     fn store(
668         self,
669         store: &mut AutoAssertNoGc<'_>,
670         func_ty: &FuncType,
671         dst: &mut MaybeUninit<Self::ValRawStorage>,
672     ) -> Result<()> {
673         <(T,) as WasmParams>::store((self,), store, func_ty, dst)
674     }
675 }
676 
677 macro_rules! impl_wasm_params {
678     ($n:tt $($t:ident)*) => {
679         #[allow(non_snake_case, reason = "macro-generated code")]
680         unsafe impl<$($t: WasmTy,)*> WasmParams for ($($t,)*) {
681             type ValRawStorage = [ValRaw; $n];
682 
683             fn typecheck(
684                 _engine: &Engine,
685                 mut params: impl ExactSizeIterator<Item = crate::ValType>,
686                 _position: TypeCheckPosition,
687             ) -> Result<()> {
688                 let mut _n = 0;
689 
690                 $(
691                     match params.next() {
692                         Some(t) => {
693                             _n += 1;
694                             $t::typecheck(_engine, t, _position)?
695                         },
696                         None => bail!("expected {} types, found {}", $n, params.len() + _n),
697                     }
698                 )*
699 
700                 match params.next() {
701                     None => Ok(()),
702                     Some(_) => {
703                         _n += 1;
704                         bail!("expected {} types, found {}", $n, params.len() + _n)
705                     },
706                 }
707             }
708 
709             #[inline]
710             fn vmgcref_pointing_to_object_count(&self) -> usize {
711                 let ($($t,)*) = self;
712                 0 $(
713                     + $t.is_vmgcref_and_points_to_object() as usize
714                 )*
715             }
716 
717 
718             #[inline]
719             fn store(
720                 self,
721                 _store: &mut AutoAssertNoGc<'_>,
722                 _func_ty: &FuncType,
723                 _ptr: &mut MaybeUninit<Self::ValRawStorage>,
724             ) -> Result<()> {
725                 let ($($t,)*) = self;
726 
727                 let mut _i = 0;
728                 $(
729                     if !$t.compatible_with_store(_store) {
730                         bail!("attempt to pass cross-`Store` value to Wasm as function argument");
731                     }
732 
733                     if $t::valtype().is_ref() {
734                         let param_ty = _func_ty.param(_i).unwrap();
735                         let ref_ty = param_ty.unwrap_ref();
736                         let heap_ty = ref_ty.heap_type();
737                         if heap_ty.is_concrete() {
738                             $t.dynamic_concrete_type_check(_store, ref_ty.is_nullable(), heap_ty)?;
739                         }
740                     }
741 
742                     let dst = map_maybe_uninit!(_ptr[_i]);
743                     $t.store(_store, dst)?;
744 
745                     _i += 1;
746                 )*
747                 Ok(())
748             }
749         }
750     };
751 }
752 
753 for_each_function_signature!(impl_wasm_params);
754 
755 /// A trait used for [`Func::typed`] and with [`TypedFunc`] to represent the set of
756 /// results for wasm functions.
757 pub unsafe trait WasmResults: WasmParams {
758     #[doc(hidden)]
load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self759     unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self;
760 }
761 
762 // Forwards from a bare type `T` to the 1-tuple type `(T,)`
763 unsafe impl<T: WasmTy> WasmResults for T {
load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self764     unsafe fn load(store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
765         // SAFETY: the one-element tuple and single-type impls behave the same
766         // way.
767         unsafe { <(T,) as WasmResults>::load(store, abi).0 }
768     }
769 }
770 
771 macro_rules! impl_wasm_results {
772     ($n:tt $($t:ident)*) => {
773         #[allow(non_snake_case, reason = "macro-generated code")]
774         unsafe impl<$($t: WasmTy,)*> WasmResults for ($($t,)*) {
775             unsafe fn load(_store: &mut AutoAssertNoGc<'_>, abi: &Self::ValRawStorage) -> Self {
776                 let [$($t,)*] = abi;
777 
778                 (
779                     // SAFETY: this is forwarding the unsafe contract of the outer
780                     // function to the inner functions here.
781                     $(unsafe { $t::load(_store, $t) },)*
782                 )
783             }
784         }
785     };
786 }
787 
788 for_each_function_signature!(impl_wasm_results);
789