rustpython_vm/
scope.rs

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