wasmi_validation/
util.rs

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