wasmi/func/
func_type.rs

1use crate::{core::ValType, func::FuncError, Val};
2use core::fmt;
3use std::{sync::Arc, vec::Vec};
4
5/// A function type representing a function's parameter and result types.
6///
7/// # Note
8///
9/// Can be cloned cheaply.
10#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
11pub struct FuncType {
12    /// The inner function type internals.
13    inner: FuncTypeInner,
14}
15
16/// Internal details of [`FuncType`].
17#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
18pub enum FuncTypeInner {
19    /// Stores the value types of the parameters and results inline.
20    Inline {
21        /// The number of parameters.
22        len_params: u8,
23        /// The number of results.
24        len_results: u8,
25        /// The parameter types, followed by the result types, followed by unspecified elements.
26        params_results: [ValType; Self::INLINE_SIZE],
27    },
28    /// Stores the value types of the parameters and results on the heap.
29    Big {
30        /// The number of parameters.
31        len_params: u32,
32        /// Combined parameter and result types allocated on the heap.
33        params_results: Arc<[ValType]>,
34    },
35}
36
37impl FuncTypeInner {
38    /// The inline buffer size on 32-bit platforms.
39    ///
40    /// # Note
41    ///
42    /// On 32-bit platforms we target a `size_of<FuncTypeInner>()` of 16 bytes.
43    #[cfg(target_pointer_width = "32")]
44    const INLINE_SIZE: usize = 14;
45
46    /// The inline buffer size on 64-bit platforms.
47    ///
48    /// # Note
49    ///
50    /// On 64-bit platforms we target a `size_of<FuncTypeInner>()` of 24 bytes.
51    #[cfg(target_pointer_width = "64")]
52    const INLINE_SIZE: usize = 21;
53
54    /// Creates a new [`FuncTypeInner`].
55    ///
56    /// # Panics
57    ///
58    /// If an out of bounds number of parameters or results are given.
59    pub fn new<P, R>(params: P, results: R) -> Self
60    where
61        P: IntoIterator,
62        R: IntoIterator,
63        <P as IntoIterator>::IntoIter: Iterator<Item = ValType> + ExactSizeIterator,
64        <R as IntoIterator>::IntoIter: Iterator<Item = ValType> + ExactSizeIterator,
65    {
66        let mut params = params.into_iter();
67        let mut results = results.into_iter();
68        let len_params = params.len();
69        let len_results = results.len();
70        if let Some(small) = Self::try_new_small(&mut params, &mut results) {
71            return small;
72        }
73        let mut params_results = params.collect::<Vec<_>>();
74        let len_params = u32::try_from(params_results.len()).unwrap_or_else(|_| {
75            panic!("out of bounds parameters (={len_params}) and results (={len_results}) for FuncType")
76        });
77        params_results.extend(results);
78        Self::Big {
79            params_results: params_results.into(),
80            len_params,
81        }
82    }
83
84    /// Tries to create a [`FuncTypeInner::Inline`] variant from the given inputs.
85    ///
86    /// # Note
87    ///
88    /// - Returns `None` if creation was not possible.
89    /// - Does not mutate `params` or `results` if this method returns `None`.
90    pub fn try_new_small<P, R>(params: &mut P, results: &mut R) -> Option<Self>
91    where
92        P: Iterator<Item = ValType> + ExactSizeIterator,
93        R: Iterator<Item = ValType> + ExactSizeIterator,
94    {
95        let params = params.into_iter();
96        let results = results.into_iter();
97        let len_params = u8::try_from(params.len()).ok()?;
98        let len_results = u8::try_from(results.len()).ok()?;
99        let len = len_params.checked_add(len_results)?;
100        if usize::from(len) > Self::INLINE_SIZE {
101            return None;
102        }
103        let mut params_results = [ValType::I32; Self::INLINE_SIZE];
104        params_results
105            .iter_mut()
106            .zip(params.chain(results))
107            .for_each(|(cell, param_or_result)| {
108                *cell = param_or_result;
109            });
110        Some(Self::Inline {
111            len_params,
112            len_results,
113            params_results,
114        })
115    }
116
117    /// Returns the parameter types of the function type.
118    pub fn params(&self) -> &[ValType] {
119        match self {
120            FuncTypeInner::Inline {
121                len_params,
122                params_results,
123                ..
124            } => &params_results[..usize::from(*len_params)],
125            FuncTypeInner::Big {
126                len_params,
127                params_results,
128            } => &params_results[..(*len_params as usize)],
129        }
130    }
131
132    /// Returns the result types of the function type.
133    pub fn results(&self) -> &[ValType] {
134        match self {
135            FuncTypeInner::Inline {
136                len_params,
137                len_results,
138                params_results,
139                ..
140            } => {
141                let start_results = usize::from(*len_params);
142                let end_results = start_results + usize::from(*len_results);
143                &params_results[start_results..end_results]
144            }
145            FuncTypeInner::Big {
146                len_params,
147                params_results,
148            } => &params_results[(*len_params as usize)..],
149        }
150    }
151
152    /// Returns the number of result types of the function type.
153    pub fn len_results(&self) -> usize {
154        match self {
155            FuncTypeInner::Inline { len_results, .. } => usize::from(*len_results),
156            FuncTypeInner::Big {
157                len_params,
158                params_results,
159            } => {
160                let len_buffer = params_results.len();
161                let len_params = *len_params as usize;
162                len_buffer - len_params
163            }
164        }
165    }
166
167    /// Returns the pair of parameter and result types of the function type.
168    pub(crate) fn params_results(&self) -> (&[ValType], &[ValType]) {
169        match self {
170            FuncTypeInner::Inline {
171                len_params,
172                len_results,
173                params_results,
174            } => {
175                let len_params = usize::from(*len_params);
176                let len_results = usize::from(*len_results);
177                params_results[..len_params + len_results].split_at(len_params)
178            }
179            FuncTypeInner::Big {
180                len_params,
181                params_results,
182            } => params_results.split_at(*len_params as usize),
183        }
184    }
185}
186
187#[test]
188fn size_of_func_type() {
189    #[cfg(target_pointer_width = "32")]
190    assert!(core::mem::size_of::<FuncTypeInner>() <= 16);
191    #[cfg(target_pointer_width = "64")]
192    assert!(core::mem::size_of::<FuncTypeInner>() <= 24);
193}
194
195impl fmt::Debug for FuncType {
196    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197        f.debug_struct("FuncType")
198            .field("params", &self.params())
199            .field("results", &self.results())
200            .finish()
201    }
202}
203
204impl FuncType {
205    /// Creates a new [`FuncType`].
206    pub fn new<P, R>(params: P, results: R) -> Self
207    where
208        P: IntoIterator,
209        R: IntoIterator,
210        <P as IntoIterator>::IntoIter: Iterator<Item = ValType> + ExactSizeIterator,
211        <R as IntoIterator>::IntoIter: Iterator<Item = ValType> + ExactSizeIterator,
212    {
213        Self {
214            inner: FuncTypeInner::new(params, results),
215        }
216    }
217
218    /// Returns the parameter types of the function type.
219    pub fn params(&self) -> &[ValType] {
220        self.inner.params()
221    }
222
223    /// Returns the result types of the function type.
224    pub fn results(&self) -> &[ValType] {
225        self.inner.results()
226    }
227
228    /// Returns the number of result types of the function type.
229    pub fn len_results(&self) -> usize {
230        self.inner.len_results()
231    }
232
233    /// Returns the pair of parameter and result types of the function type.
234    pub(crate) fn params_results(&self) -> (&[ValType], &[ValType]) {
235        self.inner.params_results()
236    }
237
238    /// Returns `Ok` if the number and types of items in `params` matches as expected by the [`FuncType`].
239    ///
240    /// # Errors
241    ///
242    /// - If the number of items in `params` does not match the number of parameters of the function type.
243    /// - If any type of an item in `params` does not match the expected type of the function type.
244    pub(crate) fn match_params<T>(&self, params: &[T]) -> Result<(), FuncError>
245    where
246        T: Ty,
247    {
248        if self.params().len() != params.len() {
249            return Err(FuncError::MismatchingParameterLen);
250        }
251        if self
252            .params()
253            .iter()
254            .copied()
255            .ne(params.iter().map(<T as Ty>::ty))
256        {
257            return Err(FuncError::MismatchingParameterType);
258        }
259        Ok(())
260    }
261
262    /// Returns `Ok` if the number and types of items in `results` matches as expected by the [`FuncType`].
263    ///
264    /// # Note
265    ///
266    /// Only checks types if `check_type` is set to `true`.
267    ///
268    /// # Errors
269    ///
270    /// - If the number of items in `results` does not match the number of results of the function type.
271    /// - If any type of an item in `results` does not match the expected type of the function type.
272    pub(crate) fn match_results<T>(&self, results: &[T], check_type: bool) -> Result<(), FuncError>
273    where
274        T: Ty,
275    {
276        if self.results().len() != results.len() {
277            return Err(FuncError::MismatchingResultLen);
278        }
279        if check_type
280            && self
281                .results()
282                .iter()
283                .copied()
284                .ne(results.iter().map(<T as Ty>::ty))
285        {
286            return Err(FuncError::MismatchingResultType);
287        }
288        Ok(())
289    }
290
291    /// Initializes the values in `outputs` to match the types expected by the [`FuncType`].
292    ///
293    /// # Note
294    ///
295    /// This is required by an implementation detail of how function result passing is current
296    /// implemented in the Wasmi execution engine and might change in the future.
297    ///
298    /// # Panics
299    ///
300    /// If the number of items in `outputs` does not match the number of results of the [`FuncType`].
301    pub(crate) fn prepare_outputs(&self, outputs: &mut [Val]) {
302        assert_eq!(
303            self.results().len(),
304            outputs.len(),
305            "must have the same number of items in outputs as results of the function type"
306        );
307        let init_values = self.results().iter().copied().map(Val::default);
308        outputs
309            .iter_mut()
310            .zip(init_values)
311            .for_each(|(output, init)| *output = init);
312    }
313}
314
315/// Types that have a [`ValType`].
316///
317/// # Note
318///
319/// Primarily used to allow `match_params` and `match_results`
320/// to be called with both [`Val`] and [`ValType`] parameters.
321pub(crate) trait Ty {
322    fn ty(&self) -> ValType;
323}
324
325impl Ty for ValType {
326    fn ty(&self) -> ValType {
327        *self
328    }
329}
330
331impl Ty for Val {
332    fn ty(&self) -> ValType {
333        self.ty()
334    }
335}
336
337#[cfg(test)]
338mod tests {
339    use super::*;
340
341    #[test]
342    fn new_empty_works() {
343        let ft = FuncType::new([], []);
344        assert!(ft.params().is_empty());
345        assert!(ft.results().is_empty());
346        assert_eq!(ft.params(), ft.params_results().0);
347        assert_eq!(ft.results(), ft.params_results().1);
348    }
349
350    #[test]
351    fn new_works() {
352        let types = [
353            &[ValType::I32][..],
354            &[ValType::I64][..],
355            &[ValType::F32][..],
356            &[ValType::F64][..],
357            &[ValType::I32, ValType::I32][..],
358            &[ValType::I32, ValType::I32, ValType::I32][..],
359            &[ValType::I32, ValType::I32, ValType::I32, ValType::I32][..],
360            &[
361                ValType::I32,
362                ValType::I32,
363                ValType::I32,
364                ValType::I32,
365                ValType::I32,
366                ValType::I32,
367                ValType::I32,
368                ValType::I32,
369            ][..],
370            &[ValType::I32, ValType::I64, ValType::F32, ValType::F64][..],
371        ];
372        for params in types {
373            for results in types {
374                let ft = FuncType::new(params.iter().copied(), results.iter().copied());
375                assert_eq!(ft.params(), params);
376                assert_eq!(ft.results(), results);
377                assert_eq!(ft.params(), ft.params_results().0);
378                assert_eq!(ft.results(), ft.params_results().1);
379            }
380        }
381    }
382}