1use crate::{
2 any_box,
3 hint_processor::builtin_hint_processor::dict_manager::DictManager,
4 vm::errors::{exec_scope_errors::ExecScopeError, hint_errors::HintError},
5};
6use std::{any::Any, cell::RefCell, collections::HashMap, rc::Rc};
7
8#[derive(Debug)]
9pub struct ExecutionScopes {
10 pub data: Vec<HashMap<String, Box<dyn Any>>>,
11}
12
13impl ExecutionScopes {
14 pub fn new() -> ExecutionScopes {
15 ExecutionScopes {
16 data: vec![HashMap::new()],
17 }
18 }
19
20 pub fn enter_scope(&mut self, new_scope_locals: HashMap<String, Box<dyn Any>>) {
21 self.data.push(new_scope_locals);
22 }
23
24 pub fn exit_scope(&mut self) -> Result<(), ExecScopeError> {
25 if self.data.len() == 1 {
26 return Err(ExecScopeError::ExitMainScopeError);
27 }
28 self.data.pop();
29
30 Ok(())
31 }
32
33 pub fn get_local_variables_mut(
35 &mut self,
36 ) -> Result<&mut HashMap<String, Box<dyn Any>>, HintError> {
37 self.data
38 .last_mut()
39 .ok_or(HintError::FromScopeError(ExecScopeError::NoScopeError))
40 }
41
42 pub fn get_local_variables(&self) -> Result<&HashMap<String, Box<dyn Any>>, HintError> {
44 self.data
45 .last()
46 .ok_or(HintError::FromScopeError(ExecScopeError::NoScopeError))
47 }
48
49 pub fn delete_variable(&mut self, var_name: &str) {
51 if let Ok(local_variables) = self.get_local_variables_mut() {
52 local_variables.remove(var_name);
53 }
54 }
55
56 pub fn assign_or_update_variable(&mut self, var_name: &str, var_value: Box<dyn Any>) {
58 if let Ok(local_variables) = self.get_local_variables_mut() {
59 local_variables.insert(var_name.to_string(), var_value);
60 }
61 }
62
63 pub fn get<T: Any + Clone>(&self, name: &str) -> Result<T, HintError> {
65 let mut val: Option<T> = None;
66 if let Some(variable) = self.get_local_variables()?.get(name) {
67 if let Some(int) = variable.downcast_ref::<T>() {
68 val = Some(int.clone());
69 }
70 }
71 val.ok_or_else(|| HintError::VariableNotInScopeError(name.to_string().into_boxed_str()))
72 }
73
74 pub fn get_ref<T: Any>(&self, name: &str) -> Result<&T, HintError> {
76 let mut val: Option<&T> = None;
77 if let Some(variable) = self.get_local_variables()?.get(name) {
78 if let Some(int) = variable.downcast_ref::<T>() {
79 val = Some(int);
80 }
81 }
82 val.ok_or_else(|| HintError::VariableNotInScopeError(name.to_string().into_boxed_str()))
83 }
84
85 pub fn get_mut_ref<T: Any>(&mut self, name: &str) -> Result<&mut T, HintError> {
87 let mut val: Option<&mut T> = None;
88 if let Some(variable) = self.get_local_variables_mut()?.get_mut(name) {
89 if let Some(int) = variable.downcast_mut::<T>() {
90 val = Some(int);
91 }
92 }
93 val.ok_or_else(|| HintError::VariableNotInScopeError(name.to_string().into_boxed_str()))
94 }
95
96 pub fn get_any_boxed_ref(&self, name: &str) -> Result<&Box<dyn Any>, HintError> {
98 if let Some(variable) = self.get_local_variables()?.get(name) {
99 return Ok(variable);
100 }
101 Err(HintError::VariableNotInScopeError(
102 name.to_string().into_boxed_str(),
103 ))
104 }
105
106 pub fn get_any_boxed_mut(&mut self, name: &str) -> Result<&mut Box<dyn Any>, HintError> {
108 if let Some(variable) = self.get_local_variables_mut()?.get_mut(name) {
109 return Ok(variable);
110 }
111 Err(HintError::VariableNotInScopeError(
112 name.to_string().into_boxed_str(),
113 ))
114 }
115
116 pub fn get_list<T: Any + Clone>(&self, name: &str) -> Result<Vec<T>, HintError> {
118 let mut val: Option<Vec<T>> = None;
119 if let Some(variable) = self.get_local_variables()?.get(name) {
120 if let Some(list) = variable.downcast_ref::<Vec<T>>() {
121 val = Some(list.clone());
122 }
123 }
124 val.ok_or_else(|| HintError::VariableNotInScopeError(name.to_string().into_boxed_str()))
125 }
126
127 pub fn get_list_ref<T: Any>(&self, name: &str) -> Result<&Vec<T>, HintError> {
129 let mut val: Option<&Vec<T>> = None;
130 if let Some(variable) = self.get_local_variables()?.get(name) {
131 if let Some(list) = variable.downcast_ref::<Vec<T>>() {
132 val = Some(list);
133 }
134 }
135 val.ok_or_else(|| HintError::VariableNotInScopeError(name.to_string().into_boxed_str()))
136 }
137
138 pub fn get_mut_list_ref<T: Any>(&mut self, name: &str) -> Result<&mut Vec<T>, HintError> {
140 let mut val: Option<&mut Vec<T>> = None;
141 if let Some(variable) = self.get_local_variables_mut()?.get_mut(name) {
142 if let Some(list) = variable.downcast_mut::<Vec<T>>() {
143 val = Some(list);
144 }
145 }
146 val.ok_or_else(|| HintError::VariableNotInScopeError(name.to_string().into_boxed_str()))
147 }
148
149 pub fn get_dict_manager(&self) -> Result<Rc<RefCell<DictManager>>, HintError> {
151 let mut val: Option<Rc<RefCell<DictManager>>> = None;
152 if let Some(variable) = self.get_local_variables()?.get("dict_manager") {
153 if let Some(dict_manager) = variable.downcast_ref::<Rc<RefCell<DictManager>>>() {
154 val = Some(dict_manager.clone());
155 }
156 }
157 val.ok_or_else(|| {
158 HintError::VariableNotInScopeError("dict_manager".to_string().into_boxed_str())
159 })
160 }
161
162 pub fn get_mut_dict_ref<K: Any, V: Any>(
164 &mut self,
165 name: &str,
166 ) -> Result<&mut HashMap<K, V>, HintError> {
167 let mut val: Option<&mut HashMap<K, V>> = None;
168 if let Some(variable) = self.get_local_variables_mut()?.get_mut(name) {
169 if let Some(dict) = variable.downcast_mut::<HashMap<K, V>>() {
170 val = Some(dict);
171 }
172 }
173 val.ok_or_else(|| HintError::VariableNotInScopeError(name.to_string().into_boxed_str()))
174 }
175
176 pub fn insert_box(&mut self, name: &str, value: Box<dyn Any>) {
178 self.assign_or_update_variable(name, value);
179 }
180
181 pub fn insert_value<T: 'static>(&mut self, name: &str, value: T) {
183 self.assign_or_update_variable(name, any_box!(value));
184 }
185}
186
187impl Default for ExecutionScopes {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use crate::Felt252;
197 use assert_matches::assert_matches;
198
199 #[test]
200 fn initialize_execution_scopes() {
201 let scopes = ExecutionScopes::new();
202 assert_eq!(scopes.data.len(), 1);
203 }
204
205 #[test]
206 fn get_local_variables_test() {
207 let var_name = String::from("a");
208 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
209
210 let scope = HashMap::from([(var_name, var_value)]);
211
212 let scopes = ExecutionScopes { data: vec![scope] };
213 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
214 assert_eq!(
215 scopes
216 .get_local_variables()
217 .unwrap()
218 .get("a")
219 .unwrap()
220 .downcast_ref::<Felt252>(),
221 Some(&Felt252::from(2))
222 );
223 }
224
225 #[test]
226 fn enter_new_scope_test() {
227 let var_name = String::from("a");
228 let var_value: Box<dyn Any> = Box::new(Felt252::from(2_i32));
229
230 let new_scope = HashMap::from([(var_name, var_value)]);
231
232 let mut scopes = ExecutionScopes {
233 data: vec![HashMap::from([(
234 String::from("b"),
235 (Box::new(Felt252::ONE) as Box<dyn Any>),
236 )])],
237 };
238
239 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
240 assert_eq!(
241 scopes
242 .get_local_variables()
243 .unwrap()
244 .get("b")
245 .unwrap()
246 .downcast_ref::<Felt252>(),
247 Some(&Felt252::ONE)
248 );
249
250 scopes.enter_scope(new_scope);
251
252 assert!(scopes.get_local_variables().unwrap().get("b").is_none());
254
255 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
256 assert_eq!(
257 scopes
258 .get_local_variables()
259 .unwrap()
260 .get("a")
261 .unwrap()
262 .downcast_ref::<Felt252>(),
263 Some(&Felt252::from(2))
264 );
265 }
266
267 #[test]
268 fn exit_scope_test() {
269 let var_name = String::from("a");
270 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
271
272 let new_scope = HashMap::from([(var_name, var_value)]);
273
274 let mut scopes = ExecutionScopes::new();
276
277 scopes.enter_scope(new_scope);
279
280 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
281 assert_eq!(
282 scopes
283 .get_local_variables()
284 .unwrap()
285 .get("a")
286 .unwrap()
287 .downcast_ref::<Felt252>(),
288 Some(&Felt252::from(2))
289 );
290
291 let exit_scope_result = scopes.exit_scope();
293
294 assert!(exit_scope_result.is_ok());
295
296 assert!(scopes.get_local_variables().unwrap().get("a").is_none());
298
299 assert!(scopes.get_local_variables().unwrap().is_empty());
301 }
302
303 #[test]
304 fn assign_local_variable_test() {
305 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
306
307 let mut scopes = ExecutionScopes::new();
308
309 scopes.assign_or_update_variable("a", var_value);
310
311 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
312 assert_eq!(
313 scopes
314 .get_local_variables()
315 .unwrap()
316 .get("a")
317 .unwrap()
318 .downcast_ref::<Felt252>(),
319 Some(&Felt252::from(2))
320 );
321 }
322
323 #[test]
324 fn re_assign_local_variable_test() {
325 let var_name = String::from("a");
326 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
327
328 let scope = HashMap::from([(var_name, var_value)]);
329
330 let mut scopes = ExecutionScopes { data: vec![scope] };
331
332 let var_value_new: Box<dyn Any> = Box::new(Felt252::from(3));
333
334 scopes.assign_or_update_variable("a", var_value_new);
335
336 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
337 assert_eq!(
338 scopes
339 .get_local_variables()
340 .unwrap()
341 .get("a")
342 .unwrap()
343 .downcast_ref::<Felt252>(),
344 Some(&Felt252::from(3))
345 );
346 }
347
348 #[test]
349 fn delete_local_variable_test() {
350 let var_name = String::from("a");
351 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
352
353 let scope = HashMap::from([(var_name, var_value)]);
354
355 let mut scopes = ExecutionScopes { data: vec![scope] };
356
357 assert!(scopes
358 .get_local_variables()
359 .unwrap()
360 .contains_key(&String::from("a")));
361
362 scopes.delete_variable("a");
363
364 assert!(!scopes
365 .get_local_variables()
366 .unwrap()
367 .contains_key(&String::from("a")));
368 }
369
370 #[test]
371 fn exit_main_scope_gives_error_test() {
372 let mut scopes = ExecutionScopes::new();
373
374 assert!(scopes.exit_scope().is_err());
375 }
376
377 #[test]
378 fn get_listu64_test() {
379 let list_u64: Box<dyn Any> = Box::new(vec![20_u64, 18_u64]);
380
381 let mut scopes = ExecutionScopes::default();
382
383 scopes.insert_box("list_u64", list_u64);
384
385 assert_matches!(
386 scopes.get_list::<u64>("list_u64"),
387 Ok(x) if x == vec![20_u64, 18_u64]
388 );
389
390 assert_matches!(
391 scopes.get_list::<u64>("no_variable"),
392 Err(HintError::VariableNotInScopeError(
393 x
394 )) if *x == *"no_variable".to_string()
395 );
396 }
397
398 #[test]
399 fn get_u64_test() {
400 let u64: Box<dyn Any> = Box::new(9_u64);
401
402 let mut scopes = ExecutionScopes::new();
403
404 scopes.assign_or_update_variable("u64", u64);
405
406 assert_matches!(scopes.get_ref::<u64>("u64"), Ok(&9_u64));
407 assert_matches!(scopes.get_mut_ref::<u64>("u64"), Ok(&mut 9_u64));
408
409 assert_matches!(
410 scopes.get_mut_ref::<u64>("no_variable"),
411 Err(HintError::VariableNotInScopeError(
412 x
413 )) if *x == *"no_variable".to_string()
414 );
415 assert_matches!(
416 scopes.get_ref::<u64>("no_variable"),
417 Err(HintError::VariableNotInScopeError(
418 x
419 )) if *x == *"no_variable".to_string()
420 );
421 }
422
423 #[test]
424 fn get_mut_int_ref_test() {
425 let bigint: Box<dyn Any> = Box::new(Felt252::from(12));
426
427 let mut scopes = ExecutionScopes::new();
428 scopes.assign_or_update_variable("bigint", bigint);
429
430 assert_matches!(
431 scopes.get_mut_ref::<Felt252>("bigint"),
432 Ok(x) if x == &mut Felt252::from(12)
433 );
434 }
435
436 #[test]
437 fn get_any_boxed_test() {
438 let list_u64: Box<dyn Any> = Box::new(vec![20_u64, 18_u64]);
439
440 let mut scopes = ExecutionScopes::default();
441
442 scopes.assign_or_update_variable("list_u64", list_u64);
443
444 assert_eq!(
445 scopes
446 .get_any_boxed_ref("list_u64")
447 .unwrap()
448 .downcast_ref::<Vec<u64>>(),
449 Some(&vec![20_u64, 18_u64])
450 );
451
452 assert_eq!(
453 scopes
454 .get_any_boxed_mut("list_u64")
455 .unwrap()
456 .downcast_mut::<Vec<u64>>(),
457 Some(&mut vec![20_u64, 18_u64])
458 );
459
460 assert!(scopes.get_any_boxed_mut("no_variable").is_err());
461 assert!(scopes.get_any_boxed_ref("no_variable").is_err());
462 }
463}