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