soroban_wasmi/func/
into_func.rs

1use super::{
2    super::engine::{FuncFinished, FuncParams, FuncResults},
3    TrampolineEntity,
4};
5use crate::{
6    core::{DecodeUntypedSlice, EncodeUntypedSlice, UntypedVal, ValType, F32, F64},
7    Caller,
8    Error,
9    ExternRef,
10    FuncRef,
11    FuncType,
12};
13use core::{array, iter::FusedIterator};
14
15/// Closures and functions that can be used as host functions.
16pub trait IntoFunc<T, Params, Results>: Send + Sync + 'static {
17    /// The parameters of the host function.
18    #[doc(hidden)]
19    type Params: WasmTyList;
20    /// The results of the host function.
21    #[doc(hidden)]
22    type Results: WasmTyList;
23
24    /// Converts the function into its Wasmi signature and its trampoline.
25    #[doc(hidden)]
26    fn into_func(self) -> (FuncType, TrampolineEntity<T>);
27}
28
29macro_rules! impl_into_func {
30    ( $n:literal $( $tuple:ident )* ) => {
31        impl<T, F, $($tuple,)* R> IntoFunc<T, ($($tuple,)*), R> for F
32        where
33            F: Fn($($tuple),*) -> R,
34            F: Send + Sync + 'static,
35            $(
36                $tuple: WasmTy,
37            )*
38            R: WasmRet,
39        {
40            type Params = ($($tuple,)*);
41            type Results = <R as WasmRet>::Ok;
42
43            #[allow(non_snake_case)]
44            fn into_func(self) -> (FuncType, TrampolineEntity<T>) {
45                IntoFunc::into_func(
46                    move |
47                        _: Caller<'_, T>,
48                        $(
49                            $tuple: $tuple,
50                        )*
51                    | {
52                        (self)($($tuple),*)
53                    }
54                )
55            }
56        }
57
58        impl<T, F, $($tuple,)* R> IntoFunc<T, (Caller<'_, T>, $($tuple),*), R> for F
59        where
60            F: Fn(Caller<T>, $($tuple),*) -> R,
61            F: Send + Sync + 'static,
62            $(
63                $tuple: WasmTy,
64            )*
65            R: WasmRet,
66        {
67            type Params = ($($tuple,)*);
68            type Results = <R as WasmRet>::Ok;
69
70            #[allow(non_snake_case)]
71            fn into_func(self) -> (FuncType, TrampolineEntity<T>) {
72                let signature = FuncType::new(
73                    <Self::Params as WasmTyList>::types(),
74                    <Self::Results as WasmTyList>::types(),
75                );
76                let trampoline = TrampolineEntity::new(
77                    move |caller: Caller<T>, params_results: FuncParams| -> Result<FuncFinished, Error> {
78                        let (($($tuple,)*), func_results): (Self::Params, FuncResults) = params_results.decode_params();
79                        let results: Self::Results =
80                            (self)(caller, $($tuple),*).into_fallible()?;
81                        Ok(func_results.encode_results(results))
82                    },
83                );
84                (signature, trampoline)
85            }
86        }
87    };
88}
89for_each_tuple!(impl_into_func);
90
91/// Types and type sequences that can be used as return values of host functions.
92pub trait WasmRet {
93    #[doc(hidden)]
94    type Ok: WasmTyList;
95
96    #[doc(hidden)]
97    fn into_fallible(self) -> Result<<Self as WasmRet>::Ok, Error>;
98}
99
100impl<T1> WasmRet for T1
101where
102    T1: WasmTy,
103{
104    type Ok = T1;
105
106    #[inline]
107    fn into_fallible(self) -> Result<Self::Ok, Error> {
108        Ok(self)
109    }
110}
111
112impl<T1> WasmRet for Result<T1, Error>
113where
114    T1: WasmTy,
115{
116    type Ok = T1;
117
118    #[inline]
119    fn into_fallible(self) -> Result<<Self as WasmRet>::Ok, Error> {
120        self
121    }
122}
123
124macro_rules! impl_wasm_return_type {
125    ( $n:literal $( $tuple:ident )* ) => {
126        impl<$($tuple),*> WasmRet for ($($tuple,)*)
127        where
128            $(
129                $tuple: WasmTy
130            ),*
131        {
132            type Ok = ($($tuple,)*);
133
134            #[inline]
135            fn into_fallible(self) -> Result<Self::Ok, Error> {
136                Ok(self)
137            }
138        }
139
140        impl<$($tuple),*> WasmRet for Result<($($tuple,)*), Error>
141        where
142            $(
143                $tuple: WasmTy
144            ),*
145        {
146            type Ok = ($($tuple,)*);
147
148            #[inline]
149            fn into_fallible(self) -> Result<<Self as WasmRet>::Ok, Error> {
150                self
151            }
152        }
153    };
154}
155for_each_tuple!(impl_wasm_return_type);
156
157/// Types that can be used as parameters or results of host functions.
158pub trait WasmTy: From<UntypedVal> + Into<UntypedVal> + Send {
159    /// Returns the value type of the Wasm type.
160    #[doc(hidden)]
161    fn ty() -> ValType;
162}
163
164macro_rules! impl_wasm_type {
165    ( $( type $rust_type:ty = $wasmi_type:ident );* $(;)? ) => {
166        $(
167            impl WasmTy for $rust_type {
168                #[inline]
169                fn ty() -> ValType {
170                    ValType::$wasmi_type
171                }
172            }
173        )*
174    };
175}
176impl_wasm_type! {
177    type u32 = I32;
178    type u64 = I64;
179    type i32 = I32;
180    type i64 = I64;
181    type F32 = F32;
182    type F64 = F64;
183    type f32 = F32;
184    type f64 = F64;
185    type FuncRef = FuncRef;
186    type ExternRef = ExternRef;
187}
188
189/// A list of [`WasmTy`] types.
190///
191/// # Note
192///
193/// This is a convenience trait that allows to:
194///
195/// - Read host function parameters from a region of the value stack.
196/// - Write host function results into a region of the value stack.
197/// - Iterate over the value types of the Wasm type sequence
198///     - This is useful to construct host function signatures.
199pub trait WasmTyList: DecodeUntypedSlice + EncodeUntypedSlice + Sized + Send {
200    /// The number of Wasm types in the list.
201    #[doc(hidden)]
202    const LEN: usize;
203
204    /// The [`ValType`] sequence as array.
205    #[doc(hidden)]
206    type Types: IntoIterator<IntoIter = Self::TypesIter, Item = ValType>
207        + AsRef<[ValType]>
208        + AsMut<[ValType]>
209        + Copy
210        + Clone;
211
212    /// The iterator type of the sequence of [`ValType`].
213    #[doc(hidden)]
214    type TypesIter: ExactSizeIterator<Item = ValType> + DoubleEndedIterator + FusedIterator;
215
216    /// The [`UntypedVal`] sequence as array.
217    #[doc(hidden)]
218    type Values: IntoIterator<IntoIter = Self::ValuesIter, Item = UntypedVal>
219        + AsRef<[UntypedVal]>
220        + AsMut<[UntypedVal]>
221        + Copy
222        + Clone;
223
224    /// The iterator type of the sequence of [`Val`].
225    ///
226    /// [`Val`]: [`crate::core::Value`]
227    #[doc(hidden)]
228    type ValuesIter: ExactSizeIterator<Item = UntypedVal> + DoubleEndedIterator + FusedIterator;
229
230    /// Returns an array representing the [`ValType`] sequence of `Self`.
231    #[doc(hidden)]
232    fn types() -> Self::Types;
233
234    /// Returns an array representing the [`UntypedVal`] sequence of `self`.
235    #[doc(hidden)]
236    fn values(self) -> Self::Values;
237
238    /// Consumes the [`UntypedVal`] iterator and creates `Self` if possible.
239    ///
240    /// Returns `None` if construction of `Self` is impossible.
241    #[doc(hidden)]
242    fn from_values(values: &[UntypedVal]) -> Option<Self>;
243}
244
245impl<T1> WasmTyList for T1
246where
247    T1: WasmTy,
248{
249    const LEN: usize = 1;
250
251    type Types = [ValType; 1];
252    type TypesIter = array::IntoIter<ValType, 1>;
253    type Values = [UntypedVal; 1];
254    type ValuesIter = array::IntoIter<UntypedVal, 1>;
255
256    #[inline]
257    fn types() -> Self::Types {
258        [<T1 as WasmTy>::ty()]
259    }
260
261    #[inline]
262    fn values(self) -> Self::Values {
263        [<T1 as Into<UntypedVal>>::into(self)]
264    }
265
266    #[inline]
267    fn from_values(values: &[UntypedVal]) -> Option<Self> {
268        if let [value] = *values {
269            return Some(value.into());
270        }
271        None
272    }
273}
274
275macro_rules! impl_wasm_type_list {
276    ( $n:literal $( $tuple:ident )* ) => {
277        impl<$($tuple),*> WasmTyList for ($($tuple,)*)
278        where
279            $(
280                $tuple: WasmTy
281            ),*
282        {
283            const LEN: usize = $n;
284
285            type Types = [ValType; $n];
286            type TypesIter = array::IntoIter<ValType, $n>;
287            type Values = [UntypedVal; $n];
288            type ValuesIter = array::IntoIter<UntypedVal, $n>;
289
290            #[inline]
291            fn types() -> Self::Types {
292                [$(
293                    <$tuple as WasmTy>::ty()
294                ),*]
295            }
296
297            #[inline]
298            #[allow(non_snake_case)]
299            fn values(self) -> Self::Values {
300                let ($($tuple,)*) = self;
301                [$(
302                    <$tuple as Into<UntypedVal>>::into($tuple)
303                ),*]
304            }
305
306            #[inline]
307            #[allow(non_snake_case)]
308            fn from_values(values: &[UntypedVal]) -> Option<Self> {
309                if let [$($tuple),*] = *values {
310                    return Some(
311                        ( $( Into::into($tuple), )* )
312                    )
313                }
314                None
315            }
316        }
317    };
318}
319for_each_tuple!(impl_wasm_type_list);
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324    use std::string::String;
325
326    /// Utility struct helper for the `implements_wasm_results` macro.
327    pub struct ImplementsWasmRet<T> {
328        marker: core::marker::PhantomData<fn() -> T>,
329    }
330    /// Utility trait for the fallback case of the `implements_wasm_results` macro.
331    pub trait ImplementsWasmRetFallback {
332        const VALUE: bool = false;
333    }
334    impl<T> ImplementsWasmRetFallback for ImplementsWasmRet<T> {}
335    /// Utility trait impl for the `true` case of the `implements_wasm_results` macro.
336    impl<T> ImplementsWasmRet<T>
337    where
338        T: WasmRet,
339    {
340        // We need to allow for dead code at this point because
341        // the Rust compiler thinks this function is unused even
342        // though it acts as the specialized case for detection.
343        pub const VALUE: bool = true;
344    }
345    /// Returns `true` if the given type `T` implements the `WasmRet` trait.
346    #[macro_export]
347    #[doc(hidden)]
348    macro_rules! implements_wasm_results {
349        ( $T:ty $(,)? ) => {{
350            #[allow(unused_imports)]
351            use ImplementsWasmRetFallback as _;
352            ImplementsWasmRet::<$T>::VALUE
353        }};
354    }
355
356    #[test]
357    fn into_func_trait_impls() {
358        assert!(!implements_wasm_results!(String));
359        assert!(!implements_wasm_results!(Option<i32>));
360        assert!(implements_wasm_results!(()));
361        assert!(implements_wasm_results!(i32));
362        assert!(implements_wasm_results!((i32,)));
363        assert!(implements_wasm_results!((i32, u32, i64, u64, F32, F64)));
364        assert!(implements_wasm_results!(Result<(), Error>));
365        assert!(implements_wasm_results!(Result<i32, Error>));
366        assert!(implements_wasm_results!(Result<(i32,), Error>));
367        assert!(implements_wasm_results!(Result<(i32, u32, i64, u64, F32, F64), Error>));
368    }
369}