Skip to content

Commit e8ec497

Browse files
Merge pull request RustPython#637 from RustPython/scope_globals_locals
Scope globals locals
2 parents 1751333 + 3fbf627 commit e8ec497

File tree

14 files changed

+222
-134
lines changed

14 files changed

+222
-134
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 = vm.ctx.new_scope(); // 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 = vm.ctx.new_scope();
170168

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

tests/snippets/test_exec.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,34 @@
1313

1414
exec("assert max(1, 5, square(5)) == 25", None)
1515

16-
#
17-
# These doesn't work yet:
18-
#
1916
# Local environment shouldn't replace global environment:
20-
#
21-
# exec("assert max(1, 5, square(5)) == 25", None, {})
22-
#
17+
exec("assert max(1, 5, square(5)) == 25", None, {})
18+
2319
# Closures aren't available if local scope is replaced:
24-
#
25-
# def g():
26-
# seven = "seven"
27-
# def f():
28-
# try:
29-
# exec("seven", None, {})
30-
# except NameError:
31-
# pass
32-
# else:
33-
# raise NameError("seven shouldn't be in scope")
34-
# f()
35-
# g()
20+
def g():
21+
seven = "seven"
22+
def f():
23+
try:
24+
exec("seven", None, {})
25+
except NameError:
26+
pass
27+
else:
28+
raise NameError("seven shouldn't be in scope")
29+
f()
30+
g()
3631

3732
try:
3833
exec("", 1)
3934
except TypeError:
4035
pass
4136
else:
4237
raise TypeError("exec should fail unless globals is a dict or None")
38+
39+
g = globals()
40+
g['x'] = 2
41+
exec('x += 2')
42+
assert x == 4
43+
assert g['x'] == x
44+
45+
exec("del x")
46+
assert 'x' not in g

vm/src/builtins.rs

Lines changed: 12 additions & 9 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 {
@@ -303,7 +303,9 @@ fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
303303
vm.get_attribute(obj.clone(), attr.clone())
304304
}
305305

306-
// builtin_globals
306+
fn builtin_globals(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
307+
Ok(vm.current_scope().globals.clone())
308+
}
307309

308310
fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
309311
arg_check!(
@@ -743,6 +745,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
743745
"filter" => ctx.filter_type(),
744746
"format" => ctx.new_rustfunc(builtin_format),
745747
"getattr" => ctx.new_rustfunc(builtin_getattr),
748+
"globals" => ctx.new_rustfunc(builtin_globals),
746749
"hasattr" => ctx.new_rustfunc(builtin_hasattr),
747750
"hash" => ctx.new_rustfunc(builtin_hash),
748751
"hex" => ctx.new_rustfunc(builtin_hex),

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 = vm.ctx.new_scope();
3838
let _result = eval(&mut vm, &source, vars, "<unittest>");
3939

4040
// TODO: check result?

vm/src/frame.rs

Lines changed: 125 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,120 @@ use crate::vm::VirtualMachine;
3232
* When a name is looked up, it is check in its scope.
3333
*/
3434
#[derive(Debug)]
35+
struct RcListNode<T> {
36+
elem: T,
37+
next: Option<Rc<RcListNode<T>>>,
38+
}
39+
40+
#[derive(Debug, Clone)]
41+
struct RcList<T> {
42+
head: Option<Rc<RcListNode<T>>>,
43+
}
44+
45+
struct Iter<'a, T: 'a> {
46+
next: Option<&'a RcListNode<T>>,
47+
}
48+
49+
impl<T> RcList<T> {
50+
pub fn new() -> Self {
51+
RcList { head: None }
52+
}
53+
54+
pub fn insert(self, elem: T) -> Self {
55+
RcList {
56+
head: Some(Rc::new(RcListNode {
57+
elem,
58+
next: self.head,
59+
})),
60+
}
61+
}
62+
63+
pub fn iter(&self) -> Iter<T> {
64+
Iter {
65+
next: self.head.as_ref().map(|node| &**node),
66+
}
67+
}
68+
}
69+
70+
impl<'a, T> Iterator for Iter<'a, T> {
71+
type Item = &'a T;
72+
73+
fn next(&mut self) -> Option<Self::Item> {
74+
self.next.map(|node| {
75+
self.next = node.next.as_ref().map(|node| &**node);
76+
&node.elem
77+
})
78+
}
79+
}
80+
81+
#[derive(Debug, Clone)]
3582
pub struct Scope {
36-
pub locals: PyObjectRef, // Variables
37-
// TODO: pub locals: RefCell<PyAttributes>, // Variables
38-
pub parent: Option<Rc<Scope>>, // Parent scope
83+
locals: RcList<PyObjectRef>,
84+
pub globals: PyObjectRef,
3985
}
40-
pub type ScopeRef = Rc<Scope>;
4186

4287
impl Scope {
43-
pub fn new(locals: PyObjectRef, parent: Option<ScopeRef>) -> ScopeRef {
44-
Rc::new(Scope { locals, parent })
88+
pub fn new(locals: Option<PyObjectRef>, globals: PyObjectRef) -> Scope {
89+
let locals = match locals {
90+
Some(dict) => RcList::new().insert(dict),
91+
None => RcList::new(),
92+
};
93+
Scope { locals, globals }
94+
}
95+
96+
pub fn get_locals(&self) -> PyObjectRef {
97+
match self.locals.iter().next() {
98+
Some(dict) => dict.clone(),
99+
None => self.globals.clone(),
100+
}
101+
}
102+
103+
pub fn get_only_locals(&self) -> Option<PyObjectRef> {
104+
match self.locals.iter().next() {
105+
Some(dict) => Some(dict.clone()),
106+
None => None,
107+
}
108+
}
109+
110+
pub fn child_scope_with_locals(&self, locals: PyObjectRef) -> Scope {
111+
Scope {
112+
locals: self.locals.clone().insert(locals),
113+
globals: self.globals.clone(),
114+
}
115+
}
116+
117+
pub fn child_scope(&self, ctx: &PyContext) -> Scope {
118+
self.child_scope_with_locals(ctx.new_dict())
119+
}
120+
}
121+
122+
pub trait NameProtocol {
123+
fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef>;
124+
fn store_name(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef);
125+
fn delete_name(&self, vm: &VirtualMachine, name: &str);
126+
}
127+
128+
impl NameProtocol for Scope {
129+
fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
130+
for dict in self.locals.iter() {
131+
if let Some(value) = dict.get_item(name) {
132+
return Some(value);
133+
}
134+
}
135+
136+
if let Some(value) = self.globals.get_item(name) {
137+
return Some(value);
138+
}
139+
140+
vm.builtins.get_item(name)
141+
}
142+
143+
fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) {
144+
self.get_locals().set_item(&vm.ctx, key, value)
145+
}
146+
147+
fn delete_name(&self, _vm: &VirtualMachine, key: &str) {
148+
self.get_locals().del_item(key)
45149
}
46150
}
47151

@@ -73,7 +177,7 @@ pub struct Frame {
73177
// We need 1 stack per frame
74178
stack: RefCell<Vec<PyObjectRef>>, // The main data frame of the stack machine
75179
blocks: RefCell<Vec<Block>>, // Block frames, for controlling loops and exceptions
76-
pub scope: ScopeRef, // Variables
180+
pub scope: Scope, // Variables
77181
pub lasti: RefCell<usize>, // index of last instruction ran
78182
}
79183

@@ -93,7 +197,7 @@ pub enum ExecutionResult {
93197
pub type FrameResult = Result<Option<ExecutionResult>, PyObjectRef>;
94198

95199
impl Frame {
96-
pub fn new(code: PyObjectRef, scope: ScopeRef) -> Frame {
200+
pub fn new(code: PyObjectRef, scope: Scope) -> Frame {
97201
//populate the globals and locals
98202
//TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95
99203
/*
@@ -735,9 +839,7 @@ impl Frame {
735839
let obj = import_module(vm, current_path, module)?;
736840

737841
for (k, v) in obj.get_key_value_pairs().iter() {
738-
self.scope
739-
.locals
740-
.set_item(&vm.ctx, &objstr::get_value(k), v.clone());
842+
self.scope.store_name(&vm, &objstr::get_value(k), v.clone());
741843
}
742844
Ok(None)
743845
}
@@ -859,35 +961,26 @@ impl Frame {
859961

860962
fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
861963
let obj = self.pop_value();
862-
self.scope.locals.set_item(&vm.ctx, name, obj);
964+
self.scope.store_name(&vm, name, obj);
863965
Ok(None)
864966
}
865967

866968
fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
867-
let name = vm.ctx.new_str(name.to_string());
868-
vm.call_method(&self.scope.locals, "__delitem__", vec![name])?;
969+
self.scope.delete_name(vm, name);
869970
Ok(None)
870971
}
871972

872973
fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult {
873-
// Lookup name in scope and put it onto the stack!
874-
let mut scope = self.scope.clone();
875-
loop {
876-
if scope.locals.contains_key(name) {
877-
let obj = scope.locals.get_item(name).unwrap();
878-
self.push_value(obj);
879-
return Ok(None);
974+
match self.scope.load_name(&vm, name) {
975+
Some(value) => {
976+
self.push_value(value);
977+
Ok(None)
880978
}
881-
match &scope.parent {
882-
Some(parent_scope) => {
883-
scope = parent_scope.clone();
884-
}
885-
None => {
886-
let name_error_type = vm.ctx.exceptions.name_error.clone();
887-
let msg = format!("name '{}' is not defined", name);
888-
let name_error = vm.new_exception(name_error_type, msg);
889-
return Err(name_error);
890-
}
979+
None => {
980+
let name_error_type = vm.ctx.exceptions.name_error.clone();
981+
let msg = format!("name '{}' is not defined", name);
982+
let name_error = vm.new_exception(name_error_type, msg);
983+
Err(name_error)
891984
}
892985
}
893986
}
@@ -1138,7 +1231,7 @@ impl fmt::Debug for Frame {
11381231
.map(|elem| format!("\n > {:?}", elem))
11391232
.collect::<Vec<_>>()
11401233
.join("");
1141-
let local_str = match self.scope.locals.payload::<PyDict>() {
1234+
let local_str = match self.scope.get_locals().payload::<PyDict>() {
11421235
Some(dict) => objdict::get_key_value_pairs_from_content(&dict.entries.borrow())
11431236
.iter()
11441237
.map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1))

0 commit comments

Comments
 (0)