Skip to content

Commit 0e23e70

Browse files
committed
Restructure scope to distinguish between locals and globals.
1 parent 56bb6d2 commit 0e23e70

File tree

14 files changed

+172
-118
lines changed

14 files changed

+172
-118
lines changed

src/main.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustpython_parser::error::ParseError;
1212
use rustpython_vm::{
1313
compile,
1414
error::CompileError,
15-
frame::ScopeRef,
15+
frame::Scope,
1616
import,
1717
obj::objstr,
1818
print_exception,
@@ -82,8 +82,7 @@ fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> Py
8282
vm.new_exception(syntax_error, err.to_string())
8383
})?;
8484
// trace!("Code object: {:?}", code_obj.borrow());
85-
let builtins = vm.get_builtin_scope();
86-
let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables
85+
let vars = Scope::new(None, vm.ctx.new_dict()); // Keep track of local variables
8786
vm.run_code_obj(code_obj, vars)
8887
}
8988

@@ -121,7 +120,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult {
121120
}
122121
}
123122

124-
fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: ScopeRef) -> Result<(), CompileError> {
123+
fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: Scope) -> Result<(), CompileError> {
125124
match compile::compile(
126125
source,
127126
&compile::Mode::Single,
@@ -165,8 +164,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
165164
"Welcome to the magnificent Rust Python {} interpreter",
166165
crate_version!()
167166
);
168-
let builtins = vm.get_builtin_scope();
169-
let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables
167+
let vars = Scope::new(None, vm.ctx.new_dict());
170168

171169
// Read a single line:
172170
let mut input = String::new();

vm/src/builtins.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::obj::objiter;
1414
use crate::obj::objstr;
1515
use crate::obj::objtype;
1616

17-
use crate::frame::{Scope, ScopeRef};
17+
use crate::frame::Scope;
1818
use crate::pyobject::{
1919
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol,
2020
};
@@ -245,7 +245,7 @@ fn make_scope(
245245
vm: &mut VirtualMachine,
246246
globals: Option<&PyObjectRef>,
247247
locals: Option<&PyObjectRef>,
248-
) -> PyResult<ScopeRef> {
248+
) -> PyResult<Scope> {
249249
let dict_type = vm.ctx.dict_type();
250250
let globals = match globals {
251251
Some(arg) => {
@@ -269,16 +269,16 @@ fn make_scope(
269269
};
270270

271271
let current_scope = vm.current_scope();
272-
let parent = match globals {
273-
Some(dict) => Some(Scope::new(dict.clone(), Some(vm.get_builtin_scope()))),
274-
None => current_scope.parent.clone(),
272+
let globals = match globals {
273+
Some(dict) => dict.clone(),
274+
None => current_scope.globals.clone(),
275275
};
276276
let locals = match locals {
277-
Some(dict) => dict.clone(),
278-
None => current_scope.locals.clone(),
277+
Some(dict) => Some(dict.clone()),
278+
None => current_scope.get_only_locals(),
279279
};
280280

281-
Ok(Scope::new(locals, parent))
281+
Ok(Scope::new(locals, globals))
282282
}
283283

284284
fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

vm/src/eval.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ extern crate rustpython_parser;
33
use std::error::Error;
44

55
use crate::compile;
6-
use crate::frame::ScopeRef;
6+
use crate::frame::Scope;
77
use crate::pyobject::PyResult;
88
use crate::vm::VirtualMachine;
99

10-
pub fn eval(vm: &mut VirtualMachine, source: &str, scope: ScopeRef, source_path: &str) -> PyResult {
10+
pub fn eval(vm: &mut VirtualMachine, source: &str, scope: Scope, source_path: &str) -> PyResult {
1111
match compile::compile(
1212
source,
1313
&compile::Mode::Eval,
@@ -34,7 +34,7 @@ mod tests {
3434
fn test_print_42() {
3535
let source = String::from("print('Hello world')\n");
3636
let mut vm = VirtualMachine::new();
37-
let vars = vm.context().new_scope(None);
37+
let vars = Scope::new(None, vm.ctx.new_dict());
3838
let _result = eval(&mut vm, &source, vars, "<unittest>");
3939

4040
// TODO: check result?

vm/src/frame.rs

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use crate::obj::objlist;
2020
use crate::obj::objstr;
2121
use crate::obj::objtype;
2222
use crate::pyobject::{
23-
DictProtocol, IdProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult,
24-
TypeProtocol,
23+
DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef,
24+
PyResult, TypeProtocol,
2525
};
2626
use crate::vm::VirtualMachine;
2727

@@ -30,16 +30,97 @@ use crate::vm::VirtualMachine;
3030
* When a name is looked up, it is check in its scope.
3131
*/
3232
#[derive(Debug)]
33+
pub struct Locals {
34+
dict: PyObjectRef,
35+
parent: Option<Rc<Locals>>,
36+
}
37+
38+
#[derive(Debug, Clone)]
3339
pub struct Scope {
34-
pub locals: PyObjectRef, // Variables
40+
pub locals: Option<Rc<Locals>>, // Variables
41+
pub globals: PyObjectRef,
3542
// TODO: pub locals: RefCell<PyAttributes>, // Variables
36-
pub parent: Option<Rc<Scope>>, // Parent scope
43+
// pub parent: Option<Rc<Scope>>, // Parent scope
3744
}
38-
pub type ScopeRef = Rc<Scope>;
3945

4046
impl Scope {
41-
pub fn new(locals: PyObjectRef, parent: Option<ScopeRef>) -> ScopeRef {
42-
Rc::new(Scope { locals, parent })
47+
pub fn new(locals: Option<PyObjectRef>, globals: PyObjectRef) -> Scope {
48+
let locals = match locals {
49+
Some(dict) => Some(Rc::new(Locals {
50+
dict: dict,
51+
parent: None,
52+
})),
53+
None => None,
54+
};
55+
Scope { locals, globals }
56+
}
57+
58+
pub fn get_locals(&self) -> PyObjectRef {
59+
match self.locals {
60+
Some(ref locals) => locals.dict.clone(),
61+
None => self.globals.clone(),
62+
}
63+
}
64+
65+
pub fn get_only_locals(&self) -> Option<PyObjectRef> {
66+
match self.locals {
67+
Some(ref locals) => Some(locals.dict.clone()),
68+
None => None,
69+
}
70+
}
71+
72+
pub fn child_scope_with_locals(&self, locals: PyObjectRef) -> Scope {
73+
Scope {
74+
locals: Some(Rc::new(Locals {
75+
dict: locals,
76+
parent: self.locals.clone(),
77+
})),
78+
globals: self.globals.clone(),
79+
}
80+
}
81+
82+
pub fn child_scope(&self, ctx: &PyContext) -> Scope {
83+
self.child_scope_with_locals(ctx.new_dict())
84+
}
85+
}
86+
87+
// TODO: Merge with existing Attribute protocol.
88+
pub trait AttributeProtocol2 {
89+
fn get_attr(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
90+
fn set_attr(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
91+
fn del_attr(&self, vm: &VirtualMachine, name: &str);
92+
}
93+
94+
impl AttributeProtocol2 for Scope {
95+
fn get_attr(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
96+
// Lookup name in scope and put it onto the stack!
97+
let mut locals = self.locals.clone();
98+
loop {
99+
match locals {
100+
Some(new_locals) => {
101+
if let Some(value) = new_locals.dict.get_item(name) {
102+
return Some(value);
103+
} else {
104+
locals = new_locals.parent.clone()
105+
}
106+
}
107+
None => break,
108+
}
109+
}
110+
111+
if let Some(value) = self.globals.get_item(name) {
112+
return Some(value);
113+
}
114+
115+
vm.builtins.get_item(name)
116+
}
117+
118+
fn set_attr(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) {
119+
self.get_locals().set_item(&vm.ctx, key, value)
120+
}
121+
122+
fn del_attr(&self, _vm: &VirtualMachine, key: &str) {
123+
self.get_locals().del_item(key)
43124
}
44125
}
45126

@@ -71,7 +152,7 @@ pub struct Frame {
71152
// We need 1 stack per frame
72153
stack: RefCell<Vec<PyObjectRef>>, // The main data frame of the stack machine
73154
blocks: RefCell<Vec<Block>>, // Block frames, for controlling loops and exceptions
74-
pub scope: ScopeRef, // Variables
155+
pub scope: Scope, // Variables
75156
pub lasti: RefCell<usize>, // index of last instruction ran
76157
}
77158

@@ -85,7 +166,7 @@ pub enum ExecutionResult {
85166
pub type FrameResult = Result<Option<ExecutionResult>, PyObjectRef>;
86167

87168
impl Frame {
88-
pub fn new(code: PyObjectRef, scope: ScopeRef) -> Frame {
169+
pub fn new(code: PyObjectRef, scope: Scope) -> Frame {
89170
//populate the globals and locals
90171
//TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95
91172
/*
@@ -741,9 +822,7 @@ impl Frame {
741822
let obj = import_module(vm, current_path, module)?;
742823

743824
for (k, v) in obj.get_key_value_pairs().iter() {
744-
self.scope
745-
.locals
746-
.set_item(&vm.ctx, &objstr::get_value(k), v.clone());
825+
self.scope.set_attr(&vm, &objstr::get_value(k), v.clone());
747826
}
748827
Ok(None)
749828
}
@@ -865,35 +944,26 @@ impl Frame {
865944

866945
fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
867946
let obj = self.pop_value();
868-
self.scope.locals.set_item(&vm.ctx, name, obj);
947+
self.scope.set_attr(&vm, name, obj);
869948
Ok(None)
870949
}
871950

872951
fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
873-
let name = vm.ctx.new_str(name.to_string());
874-
vm.call_method(&self.scope.locals, "__delitem__", vec![name])?;
952+
self.scope.del_attr(vm, name);
875953
Ok(None)
876954
}
877955

878956
fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
879-
// Lookup name in scope and put it onto the stack!
880-
let mut scope = self.scope.clone();
881-
loop {
882-
if scope.locals.contains_key(name) {
883-
let obj = scope.locals.get_item(name).unwrap();
884-
self.push_value(obj);
885-
return Ok(None);
957+
match self.scope.get_attr(&vm, name) {
958+
Some(value) => {
959+
self.push_value(value);
960+
Ok(None)
886961
}
887-
match &scope.parent {
888-
Some(parent_scope) => {
889-
scope = parent_scope.clone();
890-
}
891-
None => {
892-
let name_error_type = vm.ctx.exceptions.name_error.clone();
893-
let msg = format!("name '{}' is not defined", name);
894-
let name_error = vm.new_exception(name_error_type, msg);
895-
return Err(name_error);
896-
}
962+
None => {
963+
let name_error_type = vm.ctx.exceptions.name_error.clone();
964+
let msg = format!("name '{}' is not defined", name);
965+
let name_error = vm.new_exception(name_error_type, msg);
966+
Err(name_error)
897967
}
898968
}
899969
}
@@ -1144,7 +1214,7 @@ impl fmt::Debug for Frame {
11441214
.map(|elem| format!("\n > {:?}", elem))
11451215
.collect::<Vec<_>>()
11461216
.join("");
1147-
let local_str = match self.scope.locals.payload::<PyDict>() {
1217+
let local_str = match self.scope.get_locals().payload::<PyDict>() {
11481218
Some(dict) => objdict::get_key_value_pairs_from_content(&dict.entries.borrow())
11491219
.iter()
11501220
.map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1))

vm/src/import.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::error::Error;
66
use std::path::PathBuf;
77

88
use crate::compile;
9+
use crate::frame::Scope;
910
use crate::obj::{objsequence, objstr};
1011
use crate::pyobject::{AttributeProtocol, DictProtocol, PyResult};
1112
use crate::util;
@@ -41,13 +42,10 @@ fn import_uncached_module(
4142
})?;
4243
// trace!("Code object: {:?}", code_obj);
4344

44-
let builtins = vm.get_builtin_scope();
45-
let scope = vm.ctx.new_scope(Some(builtins));
46-
scope
47-
.locals
48-
.set_item(&vm.ctx, "__name__", vm.new_str(module.to_string()));
49-
vm.run_code_obj(code_obj, scope.clone())?;
50-
Ok(vm.ctx.new_module(module, scope))
45+
let attrs = vm.ctx.new_dict();
46+
attrs.set_item(&vm.ctx, "__name__", vm.new_str(module.to_string()));
47+
vm.run_code_obj(code_obj, Scope::new(None, attrs.clone()))?;
48+
Ok(vm.ctx.new_module(module, attrs))
5149
}
5250

5351
pub fn import_module(

vm/src/macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ macro_rules! no_kwargs {
116116
macro_rules! py_module {
117117
( $ctx:expr, $module_name:expr, { $($name:expr => $value:expr),* $(,)* }) => {
118118
{
119-
let py_mod = $ctx.new_module($module_name, $ctx.new_scope(None));
119+
let py_mod = $ctx.new_module($module_name, $ctx.new_dict());
120120
$(
121121
$ctx.set_attr(&py_mod, $name, $value);
122122
)*

vm/src/obj/objdict.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::function::PyRef;
12
use std::cell::{Cell, RefCell};
23
use std::collections::HashMap;
34
use std::ops::{Deref, DerefMut};
@@ -18,6 +19,7 @@ pub struct PyDict {
1819
// TODO: should be private
1920
pub entries: RefCell<DictContentType>,
2021
}
22+
pub type PyDictRef = PyRef<PyDict>;
2123

2224
impl PyObjectPayload2 for PyDict {
2325
fn required_type(ctx: &PyContext) -> PyObjectRef {
@@ -29,7 +31,7 @@ pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref<Target = DictContent
2931
obj.payload::<PyDict>().unwrap().entries.borrow()
3032
}
3133

32-
fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut<Target = DictContentType> + 'a {
34+
pub fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut<Target = DictContentType> + 'a {
3335
obj.payload::<PyDict>().unwrap().entries.borrow_mut()
3436
}
3537

vm/src/obj/objframe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
3030
fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
3131
arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]);
3232
let frame = get_value(frame);
33-
Ok(frame.scope.locals.clone())
33+
Ok(frame.scope.get_locals().clone())
3434
}
3535

3636
fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {

vm/src/obj/objmodule.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
use crate::frame::ScopeRef;
21
use crate::function::PyRef;
32
use crate::pyobject::{DictProtocol, PyContext, PyObjectPayload2, PyObjectRef, PyResult};
43
use crate::vm::VirtualMachine;
54

65
#[derive(Clone, Debug)]
76
pub struct PyModule {
87
pub name: String,
9-
pub scope: ScopeRef,
8+
pub dict: PyObjectRef,
109
}
1110
pub type PyModuleRef = PyRef<PyModule>;
1211

@@ -17,10 +16,9 @@ impl PyObjectPayload2 for PyModule {
1716
}
1817

1918
impl PyModuleRef {
20-
fn dir(self, vm: &mut VirtualMachine) -> PyResult {
19+
fn dir(self: PyModuleRef, vm: &mut VirtualMachine) -> PyResult {
2120
let keys = self
22-
.scope
23-
.locals
21+
.dict
2422
.get_key_value_pairs()
2523
.iter()
2624
.map(|(k, _v)| k.clone())

0 commit comments

Comments
 (0)