1use crate::stdlib::{any::Any, cell::RefCell, collections::HashMap, prelude::*, rc::Rc};
2use crate::{
3 any_box,
4 hint_processor::builtin_hint_processor::dict_manager::DictManager,
5 vm::errors::{exec_scope_errors::ExecScopeError, hint_errors::HintError},
6};
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 #[cfg(target_arch = "wasm32")]
200 use wasm_bindgen_test::*;
201
202 #[test]
203 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
204 fn initialize_execution_scopes() {
205 let scopes = ExecutionScopes::new();
206 assert_eq!(scopes.data.len(), 1);
207 }
208
209 #[test]
210 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
211 fn get_local_variables_test() {
212 let var_name = String::from("a");
213 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
214
215 let scope = HashMap::from([(var_name, var_value)]);
216
217 let scopes = ExecutionScopes { data: vec![scope] };
218 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
219 assert_eq!(
220 scopes
221 .get_local_variables()
222 .unwrap()
223 .get("a")
224 .unwrap()
225 .downcast_ref::<Felt252>(),
226 Some(&Felt252::from(2))
227 );
228 }
229
230 #[test]
231 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
232 fn enter_new_scope_test() {
233 let var_name = String::from("a");
234 let var_value: Box<dyn Any> = Box::new(Felt252::from(2_i32));
235
236 let new_scope = HashMap::from([(var_name, var_value)]);
237
238 let mut scopes = ExecutionScopes {
239 data: vec![HashMap::from([(
240 String::from("b"),
241 (Box::new(Felt252::ONE) as Box<dyn Any>),
242 )])],
243 };
244
245 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
246 assert_eq!(
247 scopes
248 .get_local_variables()
249 .unwrap()
250 .get("b")
251 .unwrap()
252 .downcast_ref::<Felt252>(),
253 Some(&Felt252::ONE)
254 );
255
256 scopes.enter_scope(new_scope);
257
258 assert!(scopes.get_local_variables().unwrap().get("b").is_none());
260
261 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
262 assert_eq!(
263 scopes
264 .get_local_variables()
265 .unwrap()
266 .get("a")
267 .unwrap()
268 .downcast_ref::<Felt252>(),
269 Some(&Felt252::from(2))
270 );
271 }
272
273 #[test]
274 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
275 fn exit_scope_test() {
276 let var_name = String::from("a");
277 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
278
279 let new_scope = HashMap::from([(var_name, var_value)]);
280
281 let mut scopes = ExecutionScopes::new();
283
284 scopes.enter_scope(new_scope);
286
287 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
288 assert_eq!(
289 scopes
290 .get_local_variables()
291 .unwrap()
292 .get("a")
293 .unwrap()
294 .downcast_ref::<Felt252>(),
295 Some(&Felt252::from(2))
296 );
297
298 let exit_scope_result = scopes.exit_scope();
300
301 assert!(exit_scope_result.is_ok());
302
303 assert!(scopes.get_local_variables().unwrap().get("a").is_none());
305
306 assert!(scopes.get_local_variables().unwrap().is_empty());
308 }
309
310 #[test]
311 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
312 fn assign_local_variable_test() {
313 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
314
315 let mut scopes = ExecutionScopes::new();
316
317 scopes.assign_or_update_variable("a", var_value);
318
319 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
320 assert_eq!(
321 scopes
322 .get_local_variables()
323 .unwrap()
324 .get("a")
325 .unwrap()
326 .downcast_ref::<Felt252>(),
327 Some(&Felt252::from(2))
328 );
329 }
330
331 #[test]
332 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
333 fn re_assign_local_variable_test() {
334 let var_name = String::from("a");
335 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
336
337 let scope = HashMap::from([(var_name, var_value)]);
338
339 let mut scopes = ExecutionScopes { data: vec![scope] };
340
341 let var_value_new: Box<dyn Any> = Box::new(Felt252::from(3));
342
343 scopes.assign_or_update_variable("a", var_value_new);
344
345 assert_eq!(scopes.get_local_variables().unwrap().len(), 1);
346 assert_eq!(
347 scopes
348 .get_local_variables()
349 .unwrap()
350 .get("a")
351 .unwrap()
352 .downcast_ref::<Felt252>(),
353 Some(&Felt252::from(3))
354 );
355 }
356
357 #[test]
358 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
359 fn delete_local_variable_test() {
360 let var_name = String::from("a");
361 let var_value: Box<dyn Any> = Box::new(Felt252::from(2));
362
363 let scope = HashMap::from([(var_name, var_value)]);
364
365 let mut scopes = ExecutionScopes { data: vec![scope] };
366
367 assert!(scopes
368 .get_local_variables()
369 .unwrap()
370 .contains_key(&String::from("a")));
371
372 scopes.delete_variable("a");
373
374 assert!(!scopes
375 .get_local_variables()
376 .unwrap()
377 .contains_key(&String::from("a")));
378 }
379
380 #[test]
381 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
382 fn exit_main_scope_gives_error_test() {
383 let mut scopes = ExecutionScopes::new();
384
385 assert!(scopes.exit_scope().is_err());
386 }
387
388 #[test]
389 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
390 fn get_listu64_test() {
391 let list_u64: Box<dyn Any> = Box::new(vec![20_u64, 18_u64]);
392
393 let mut scopes = ExecutionScopes::default();
394
395 scopes.insert_box("list_u64", list_u64);
396
397 assert_matches!(
398 scopes.get_list::<u64>("list_u64"),
399 Ok(x) if x == vec![20_u64, 18_u64]
400 );
401
402 assert_matches!(
403 scopes.get_list::<u64>("no_variable"),
404 Err(HintError::VariableNotInScopeError(
405 x
406 )) if *x == *"no_variable".to_string()
407 );
408 }
409
410 #[test]
411 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
412 fn get_u64_test() {
413 let u64: Box<dyn Any> = Box::new(9_u64);
414
415 let mut scopes = ExecutionScopes::new();
416
417 scopes.assign_or_update_variable("u64", u64);
418
419 assert_matches!(scopes.get_ref::<u64>("u64"), Ok(&9_u64));
420 assert_matches!(scopes.get_mut_ref::<u64>("u64"), Ok(&mut 9_u64));
421
422 assert_matches!(
423 scopes.get_mut_ref::<u64>("no_variable"),
424 Err(HintError::VariableNotInScopeError(
425 x
426 )) if *x == *"no_variable".to_string()
427 );
428 assert_matches!(
429 scopes.get_ref::<u64>("no_variable"),
430 Err(HintError::VariableNotInScopeError(
431 x
432 )) if *x == *"no_variable".to_string()
433 );
434 }
435
436 #[test]
437 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
438 fn get_mut_int_ref_test() {
439 let bigint: Box<dyn Any> = Box::new(Felt252::from(12));
440
441 let mut scopes = ExecutionScopes::new();
442 scopes.assign_or_update_variable("bigint", bigint);
443
444 assert_matches!(
445 scopes.get_mut_ref::<Felt252>("bigint"),
446 Ok(x) if x == &mut Felt252::from(12)
447 );
448 }
449
450 #[test]
451 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
452 fn get_any_boxed_test() {
453 let list_u64: Box<dyn Any> = Box::new(vec![20_u64, 18_u64]);
454
455 let mut scopes = ExecutionScopes::default();
456
457 scopes.assign_or_update_variable("list_u64", list_u64);
458
459 assert_eq!(
460 scopes
461 .get_any_boxed_ref("list_u64")
462 .unwrap()
463 .downcast_ref::<Vec<u64>>(),
464 Some(&vec![20_u64, 18_u64])
465 );
466
467 assert_eq!(
468 scopes
469 .get_any_boxed_mut("list_u64")
470 .unwrap()
471 .downcast_mut::<Vec<u64>>(),
472 Some(&mut vec![20_u64, 18_u64])
473 );
474
475 assert!(scopes.get_any_boxed_mut("no_variable").is_err());
476 assert!(scopes.get_any_boxed_ref("no_variable").is_err());
477 }
478}