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