rustpython_vm/scope.rs
1use crate::{VirtualMachine, builtins::PyDictRef, function::ArgMapping};
2use alloc::fmt;
3
4#[derive(Clone)]
5pub struct Scope {
6 pub locals: Option<ArgMapping>,
7 pub globals: PyDictRef,
8}
9
10impl fmt::Debug for Scope {
11 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12 // TODO: have a more informative Debug impl that DOESN'T recurse and cause a stack overflow
13 f.write_str("Scope")
14 }
15}
16
17impl Scope {
18 #[inline]
19 pub fn new(locals: Option<ArgMapping>, globals: PyDictRef) -> Self {
20 Self { locals, globals }
21 }
22
23 pub fn with_builtins(
24 locals: Option<ArgMapping>,
25 globals: PyDictRef,
26 vm: &VirtualMachine,
27 ) -> Self {
28 if !globals.contains_key("__builtins__", vm) {
29 globals
30 .set_item("__builtins__", vm.builtins.clone().into(), vm)
31 .unwrap();
32 }
33 // For module-level code, locals defaults to globals
34 let locals = locals.or_else(|| Some(ArgMapping::from_dict_exact(globals.clone())));
35 Self::new(locals, globals)
36 }
37
38 // pub fn get_locals(&self) -> &Py<PyDict> {
39 // match self.locals.first() {
40 // Some(dict) => dict,
41 // None => &self.globals,
42 // }
43 // }
44
45 // pub fn get_only_locals(&self) -> Option<PyDictRef> {
46 // self.locals.first().cloned()
47 // }
48
49 // pub fn new_child_scope_with_locals(&self, locals: PyDictRef) -> Scope {
50 // let mut new_locals = Vec::with_capacity(self.locals.len() + 1);
51 // new_locals.push(locals);
52 // new_locals.extend_from_slice(&self.locals);
53 // Scope {
54 // locals: new_locals,
55 // globals: self.globals.clone(),
56 // }
57 // }
58
59 // pub fn new_child_scope(&self, ctx: &Context) -> Scope {
60 // self.new_child_scope_with_locals(ctx.new_dict())
61 // }
62
63 // #[cfg_attr(feature = "flame-it", flame("Scope"))]
64 // pub fn load_name(&self, vm: &VirtualMachine, name: impl PyName) -> Option<PyObjectRef> {
65 // for dict in self.locals.iter() {
66 // if let Some(value) = dict.get_item_option(name.clone(), vm).unwrap() {
67 // return Some(value);
68 // }
69 // }
70
71 // // Fall back to loading a global after all scopes have been searched!
72 // self.load_global(vm, name)
73 // }
74
75 // #[cfg_attr(feature = "flame-it", flame("Scope"))]
76 // /// Load a local name. Only check the local dictionary for the given name.
77 // pub fn load_local(&self, vm: &VirtualMachine, name: impl PyName) -> Option<PyObjectRef> {
78 // self.get_locals().get_item_option(name, vm).unwrap()
79 // }
80
81 // #[cfg_attr(feature = "flame-it", flame("Scope"))]
82 // pub fn load_cell(&self, vm: &VirtualMachine, name: impl PyName) -> Option<PyObjectRef> {
83 // for dict in self.locals.iter().skip(1) {
84 // if let Some(value) = dict.get_item_option(name.clone(), vm).unwrap() {
85 // return Some(value);
86 // }
87 // }
88 // None
89 // }
90
91 // pub fn store_cell(&self, vm: &VirtualMachine, name: impl PyName, value: PyObjectRef) {
92 // // find the innermost outer scope that contains the symbol name
93 // if let Some(locals) = self
94 // .locals
95 // .iter()
96 // .rev()
97 // .find(|l| l.contains_key(name.clone(), vm))
98 // {
99 // // add to the symbol
100 // locals.set_item(name, value, vm).unwrap();
101 // } else {
102 // // somewhat limited solution -> fallback to the old rustpython strategy
103 // // and store the next outer scope
104 // // This case is usually considered as a failure case, but kept for the moment
105 // // to support the scope propagation for named expression assignments to so far
106 // // unknown names in comprehensions. We need to consider here more context
107 // // information for correct handling.
108 // self.locals
109 // .get(1)
110 // .expect("no outer scope for non-local")
111 // .set_item(name, value, vm)
112 // .unwrap();
113 // }
114 // }
115
116 // pub fn store_name(&self, vm: &VirtualMachine, key: impl PyName, value: PyObjectRef) {
117 // self.get_locals().set_item(key, value, vm).unwrap();
118 // }
119
120 // pub fn delete_name(&self, vm: &VirtualMachine, key: impl PyName) -> PyResult {
121 // self.get_locals().del_item(key, vm)
122 // }
123
124 // #[cfg_attr(feature = "flame-it", flame("Scope"))]
125 // /// Load a global name.
126 // pub fn load_global(&self, vm: &VirtualMachine, name: impl PyName) -> Option<PyObjectRef> {
127 // if let Some(value) = self.globals.get_item_option(name.clone(), vm).unwrap() {
128 // Some(value)
129 // } else {
130 // vm.builtins.get_attr(name, vm).ok()
131 // }
132 // }
133
134 // pub fn store_global(&self, vm: &VirtualMachine, name: impl PyName, value: PyObjectRef) {
135 // self.globals.set_item(name, value, vm).unwrap();
136 // }
137}
138
139// mod sealed {
140// pub trait Sealed {}
141// impl Sealed for &str {}
142// impl Sealed for super::PyStrRef {}
143// }
144// pub trait PyName:
145// sealed::Sealed + crate::dict_inner::DictKey + Clone + ToPyObject
146// {
147// }
148// impl PyName for str {}
149// impl PyName for Py<PyStr> {}