xwasmi_validation/
util.rs

1use crate::Error;
2#[allow(unused_imports)]
3use alloc::prelude::v1::*;
4use xwasm::elements::{Local, ValueType};
5
6#[cfg(test)]
7use assert_matches::assert_matches;
8
9/// Locals are the concatenation of a slice of function parameters
10/// with function declared local variables.
11///
12/// Local variables are given in the form of groups represented by pairs
13/// of a value_type and a count.
14#[derive(Debug)]
15pub struct Locals<'a> {
16    params: &'a [ValueType],
17    local_groups: &'a [Local],
18    count: u32,
19}
20
21impl<'a> Locals<'a> {
22    /// Create a new wrapper around declared variables and parameters.
23    pub fn new(params: &'a [ValueType], local_groups: &'a [Local]) -> Result<Locals<'a>, Error> {
24        let mut acc = params.len() as u32;
25        for locals_group in local_groups {
26            acc = acc
27                .checked_add(locals_group.count())
28                .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
29        }
30
31        Ok(Locals {
32            params,
33            local_groups,
34            count: acc,
35        })
36    }
37
38    /// Returns parameter count.
39    pub fn param_count(&self) -> u32 {
40        self.params.len() as u32
41    }
42
43    /// Returns total count of all declared locals and paramaterers.
44    pub fn count(&self) -> u32 {
45        self.count
46    }
47
48    /// Returns the type of a local variable (either a declared local or a param).
49    ///
50    /// Returns `Err` in the case of overflow or when idx falls out of range.
51    pub fn type_of_local(&self, idx: u32) -> Result<ValueType, Error> {
52        if let Some(param) = self.params.get(idx as usize) {
53            return Ok(*param);
54        }
55
56        // If an index doesn't point to a param, then we have to look into local declarations.
57        let mut start_idx = self.param_count();
58        for locals_group in self.local_groups {
59            let end_idx = start_idx
60                .checked_add(locals_group.count())
61                .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
62
63            if idx >= start_idx && idx < end_idx {
64                return Ok(locals_group.value_type());
65            }
66
67            start_idx = end_idx;
68        }
69
70        // We didn't find anything, that's an error.
71        // At this moment `start_idx` should hold the count of all locals
72        // (since it's either set to the `end_idx` or equal to `params.len()`)
73        let total_count = start_idx;
74
75        Err(Error(format!(
76            "Trying to access local with index {} when there are only {} locals",
77            idx, total_count
78        )))
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn locals_it_works() {
88        let params = vec![ValueType::I32, ValueType::I64];
89        let local_groups = vec![Local::new(2, ValueType::F32), Local::new(2, ValueType::F64)];
90        let locals = Locals::new(&params, &local_groups).unwrap();
91
92        assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
93        assert_matches!(locals.type_of_local(1), Ok(ValueType::I64));
94        assert_matches!(locals.type_of_local(2), Ok(ValueType::F32));
95        assert_matches!(locals.type_of_local(3), Ok(ValueType::F32));
96        assert_matches!(locals.type_of_local(4), Ok(ValueType::F64));
97        assert_matches!(locals.type_of_local(5), Ok(ValueType::F64));
98        assert_matches!(locals.type_of_local(6), Err(_));
99    }
100
101    #[test]
102    fn locals_no_declared_locals() {
103        let params = vec![ValueType::I32];
104        let locals = Locals::new(&params, &[]).unwrap();
105
106        assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
107        assert_matches!(locals.type_of_local(1), Err(_));
108    }
109
110    #[test]
111    fn locals_no_params() {
112        let local_groups = vec![Local::new(2, ValueType::I32), Local::new(3, ValueType::I64)];
113        let locals = Locals::new(&[], &local_groups).unwrap();
114
115        assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
116        assert_matches!(locals.type_of_local(1), Ok(ValueType::I32));
117        assert_matches!(locals.type_of_local(2), Ok(ValueType::I64));
118        assert_matches!(locals.type_of_local(3), Ok(ValueType::I64));
119        assert_matches!(locals.type_of_local(4), Ok(ValueType::I64));
120        assert_matches!(locals.type_of_local(5), Err(_));
121    }
122
123    #[test]
124    fn locals_u32_overflow() {
125        let local_groups = vec![
126            Local::new(u32::max_value(), ValueType::I32),
127            Local::new(1, ValueType::I64),
128        ];
129        assert_matches!(Locals::new(&[], &local_groups), Err(_));
130    }
131}