wasmtime_test_util/
component.rs

1use anyhow::Result;
2use arbitrary::Arbitrary;
3use std::mem::MaybeUninit;
4use wasmtime::component::__internal::{
5    CanonicalAbiInfo, InstanceType, InterfaceType, LiftContext, LowerContext,
6};
7use wasmtime::component::{ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, Val};
8use wasmtime::{AsContextMut, Config, Engine};
9
10pub trait TypedFuncExt<P, R> {
11    fn call_and_post_return(&self, store: impl AsContextMut<Data: Send>, params: P) -> Result<R>;
12}
13
14impl<P, R> TypedFuncExt<P, R> for TypedFunc<P, R>
15where
16    P: ComponentNamedList + Lower,
17    R: ComponentNamedList + Lift + Send + Sync + 'static,
18{
19    fn call_and_post_return(
20        &self,
21        mut store: impl AsContextMut<Data: Send>,
22        params: P,
23    ) -> Result<R> {
24        let result = self.call(&mut store, params)?;
25        self.post_return(&mut store)?;
26        Ok(result)
27    }
28}
29
30pub trait FuncExt {
31    fn call_and_post_return(
32        &self,
33        store: impl AsContextMut<Data: Send>,
34        params: &[Val],
35        results: &mut [Val],
36    ) -> Result<()>;
37}
38
39impl FuncExt for Func {
40    fn call_and_post_return(
41        &self,
42        mut store: impl AsContextMut<Data: Send>,
43        params: &[Val],
44        results: &mut [Val],
45    ) -> Result<()> {
46        self.call(&mut store, params, results)?;
47        self.post_return(&mut store)?;
48        Ok(())
49    }
50}
51
52pub fn config() -> Config {
53    drop(env_logger::try_init());
54
55    let mut config = Config::new();
56    config.wasm_component_model(true);
57
58    // When `WASMTIME_TEST_NO_HOG_MEMORY` is set it means we're in qemu. The
59    // component model tests create a disproportionate number of instances so
60    // try to cut down on virtual memory usage by avoiding 4G reservations.
61    if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
62        config.memory_reservation(0);
63        config.memory_guard_size(0);
64    }
65    config
66}
67
68pub fn engine() -> Engine {
69    Engine::new(&config()).unwrap()
70}
71
72pub fn async_engine() -> Engine {
73    let mut config = config();
74    config.async_support(true);
75    Engine::new(&config).unwrap()
76}
77
78/// Newtype wrapper for `f32` whose `PartialEq` impl considers NaNs equal to each other.
79#[derive(Copy, Clone, Debug, Arbitrary)]
80pub struct Float32(pub f32);
81
82/// Newtype wrapper for `f64` whose `PartialEq` impl considers NaNs equal to each other.
83#[derive(Copy, Clone, Debug, Arbitrary)]
84pub struct Float64(pub f64);
85
86macro_rules! forward_impls {
87    ($($a:ty => $b:ty,)*) => ($(
88        unsafe impl ComponentType for $a {
89            type Lower = <$b as ComponentType>::Lower;
90
91            const ABI: CanonicalAbiInfo = <$b as ComponentType>::ABI;
92
93            #[inline]
94            fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
95                <$b as ComponentType>::typecheck(ty, types)
96            }
97        }
98
99        unsafe impl Lower for $a {
100            fn linear_lower_to_flat<U>(
101                &self,
102                cx: &mut LowerContext<'_, U>,
103                ty: InterfaceType,
104                dst: &mut MaybeUninit<Self::Lower>,
105            ) -> Result<()> {
106                <$b as Lower>::linear_lower_to_flat(&self.0, cx, ty, dst)
107            }
108
109            fn linear_lower_to_memory<U>(&self, cx: &mut LowerContext<'_, U>, ty: InterfaceType, offset: usize) -> Result<()> {
110                <$b as Lower>::linear_lower_to_memory(&self.0, cx, ty, offset)
111            }
112        }
113
114        unsafe impl Lift for $a {
115            fn linear_lift_from_flat(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
116                Ok(Self(<$b as Lift>::linear_lift_from_flat(cx, ty, src)?))
117            }
118
119            fn linear_lift_from_memory(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
120                Ok(Self(<$b as Lift>::linear_lift_from_memory(cx, ty, bytes)?))
121            }
122        }
123
124        impl PartialEq for $a {
125            fn eq(&self, other: &Self) -> bool {
126                self.0 == other.0 || (self.0.is_nan() && other.0.is_nan())
127            }
128        }
129    )*)
130}
131
132forward_impls! {
133    Float32 => f32,
134    Float64 => f64,
135}