Skip to main content

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> {}