Skip to content

Commit c5789a0

Browse files
committed
Proper construction of scope for exec/eval.
1 parent 5d28f9b commit c5789a0

File tree

4 files changed

+43
-18
lines changed

4 files changed

+43
-18
lines changed

tests/snippets/test_exec.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
exec("def square(x):\n return x * x\n")
2+
assert 16 == square(4)
3+
4+
d = {}
5+
exec("def square(x):\n return x * x\n", {}, d)
6+
assert 16 == d['square'](4)
7+
8+
exec("assert 2 == x", {}, {'x': 2})
9+
exec("assert 2 == x", {'x': 2}, {})
10+
exec("assert 4 == x", {'x': 2}, {'x': 4})
11+
12+
exec("assert max(1, 2) == 2", {}, {})

vm/src/builtins.rs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use crate::frame::{Scope, ScopeRef};
1818
use crate::pyobject::{
1919
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol,
2020
};
21-
use std::rc::Rc;
2221

2322
#[cfg(not(target_arch = "wasm32"))]
2423
use crate::stdlib::io::io_open;
@@ -192,7 +191,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
192191
args,
193192
required = [(source, None)],
194193
optional = [
195-
(_globals, Some(vm.ctx.dict_type())),
194+
(globals, Some(vm.ctx.dict_type())),
196195
(locals, Some(vm.ctx.dict_type()))
197196
]
198197
);
@@ -215,7 +214,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
215214
return Err(vm.new_type_error("code argument must be str or code object".to_string()));
216215
};
217216

218-
let scope = make_scope(vm, locals);
217+
let scope = make_scope(vm, globals, locals);
219218

220219
// Run the source:
221220
vm.run_code_obj(code_obj.clone(), scope)
@@ -229,7 +228,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
229228
args,
230229
required = [(source, None)],
231230
optional = [
232-
(_globals, Some(vm.ctx.dict_type())),
231+
(globals, Some(vm.ctx.dict_type())),
233232
(locals, Some(vm.ctx.dict_type()))
234233
]
235234
);
@@ -252,26 +251,28 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
252251
return Err(vm.new_type_error("source argument must be str or code object".to_string()));
253252
};
254253

255-
let scope = make_scope(vm, locals);
254+
let scope = make_scope(vm, globals, locals);
256255

257256
// Run the code:
258257
vm.run_code_obj(code_obj, scope)
259258
}
260259

261-
fn make_scope(vm: &mut VirtualMachine, locals: Option<&PyObjectRef>) -> ScopeRef {
262-
// handle optional global and locals
263-
let locals = if let Some(locals) = locals {
264-
locals.clone()
265-
} else {
266-
vm.new_dict()
260+
fn make_scope(
261+
vm: &mut VirtualMachine,
262+
globals: Option<&PyObjectRef>,
263+
locals: Option<&PyObjectRef>,
264+
) -> ScopeRef {
265+
let current_scope = vm.current_scope();
266+
let parent = match globals {
267+
Some(dict) => Some(Scope::new(dict.clone(), Some(vm.get_builtin_scope()))),
268+
None => current_scope.parent.clone(),
269+
};
270+
let locals = match locals {
271+
Some(dict) => dict.clone(),
272+
None => current_scope.locals.clone(),
267273
};
268274

269-
// TODO: handle optional globals
270-
// Construct new scope:
271-
Rc::new(Scope {
272-
locals,
273-
parent: None,
274-
})
275+
Scope::new(locals, parent)
275276
}
276277

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

vm/src/frame.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ pub struct Scope {
3535
}
3636
pub type ScopeRef = Rc<Scope>;
3737

38+
impl Scope {
39+
pub fn new(locals: PyObjectRef, parent: Option<ScopeRef>) -> ScopeRef {
40+
Rc::new(Scope { locals, parent })
41+
}
42+
}
43+
3844
#[derive(Clone, Debug)]
3945
struct Block {
4046
/// The type of block.

vm/src/vm.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ impl VirtualMachine {
9090
result
9191
}
9292

93+
pub fn current_scope(&self) -> &ScopeRef {
94+
let current_frame = &self.frames[self.frames.len() - 1];
95+
let frame = objframe::get_value(current_frame);
96+
&frame.scope
97+
}
98+
9399
/// Create a new python string object.
94100
pub fn new_str(&self, s: String) -> PyObjectRef {
95101
self.ctx.new_str(s)
@@ -218,7 +224,7 @@ impl VirtualMachine {
218224
&self.ctx
219225
}
220226

221-
pub fn get_builtin_scope(&mut self) -> ScopeRef {
227+
pub fn get_builtin_scope(&self) -> ScopeRef {
222228
let a2 = &*self.builtins;
223229
match a2.payload {
224230
PyObjectPayload::Module { ref scope, .. } => scope.clone(),

0 commit comments

Comments
 (0)