1use crate::Error;
2use alloc::string::String;
3use parity_wasm::elements::{Local, ValueType};
4
5#[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 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 pub fn param_count(&self) -> u32 {
36 self.params.len() as u32
37 }
38
39 pub fn count(&self) -> u32 {
41 self.count
42 }
43
44 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 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 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(¶ms, &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(¶ms, &[]).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}