twasmi_validation/
util.rs1use crate::Error;
2use alloc::string::String;
3use tetsy_wasm::elements::{Local, ValueType};
4
5#[cfg(test)]
6use assert_matches::assert_matches;
7
8#[derive(Debug)]
14pub struct Locals<'a> {
15 params: &'a [ValueType],
16 local_groups: &'a [Local],
17 count: u32,
18}
19
20impl<'a> Locals<'a> {
21 pub fn new(params: &'a [ValueType], local_groups: &'a [Local]) -> Result<Locals<'a>, Error> {
23 let mut acc = params.len() as u32;
24 for locals_group in local_groups {
25 acc = acc
26 .checked_add(locals_group.count())
27 .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
28 }
29
30 Ok(Locals {
31 params,
32 local_groups,
33 count: acc,
34 })
35 }
36
37 pub fn param_count(&self) -> u32 {
39 self.params.len() as u32
40 }
41
42 pub fn count(&self) -> u32 {
44 self.count
45 }
46
47 pub fn type_of_local(&self, idx: u32) -> Result<ValueType, Error> {
51 if let Some(param) = self.params.get(idx as usize) {
52 return Ok(*param);
53 }
54
55 let mut start_idx = self.param_count();
57 for locals_group in self.local_groups {
58 let end_idx = start_idx
59 .checked_add(locals_group.count())
60 .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?;
61
62 if idx >= start_idx && idx < end_idx {
63 return Ok(locals_group.value_type());
64 }
65
66 start_idx = end_idx;
67 }
68
69 let total_count = start_idx;
73
74 Err(Error(format!(
75 "Trying to access local with index {} when there are only {} locals",
76 idx, total_count
77 )))
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn locals_it_works() {
87 let params = vec![ValueType::I32, ValueType::I64];
88 let local_groups = vec![Local::new(2, ValueType::F32), Local::new(2, ValueType::F64)];
89 let locals = Locals::new(¶ms, &local_groups).unwrap();
90
91 assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
92 assert_matches!(locals.type_of_local(1), Ok(ValueType::I64));
93 assert_matches!(locals.type_of_local(2), Ok(ValueType::F32));
94 assert_matches!(locals.type_of_local(3), Ok(ValueType::F32));
95 assert_matches!(locals.type_of_local(4), Ok(ValueType::F64));
96 assert_matches!(locals.type_of_local(5), Ok(ValueType::F64));
97 assert_matches!(locals.type_of_local(6), Err(_));
98 }
99
100 #[test]
101 fn locals_no_declared_locals() {
102 let params = vec![ValueType::I32];
103 let locals = Locals::new(¶ms, &[]).unwrap();
104
105 assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
106 assert_matches!(locals.type_of_local(1), Err(_));
107 }
108
109 #[test]
110 fn locals_no_params() {
111 let local_groups = vec![Local::new(2, ValueType::I32), Local::new(3, ValueType::I64)];
112 let locals = Locals::new(&[], &local_groups).unwrap();
113
114 assert_matches!(locals.type_of_local(0), Ok(ValueType::I32));
115 assert_matches!(locals.type_of_local(1), Ok(ValueType::I32));
116 assert_matches!(locals.type_of_local(2), Ok(ValueType::I64));
117 assert_matches!(locals.type_of_local(3), Ok(ValueType::I64));
118 assert_matches!(locals.type_of_local(4), Ok(ValueType::I64));
119 assert_matches!(locals.type_of_local(5), Err(_));
120 }
121
122 #[test]
123 fn locals_u32_overflow() {
124 let local_groups = vec![
125 Local::new(u32::max_value(), ValueType::I32),
126 Local::new(1, ValueType::I64),
127 ];
128 assert_matches!(Locals::new(&[], &local_groups), Err(_));
129 }
130}