diff --git a/src/main.rs b/src/main.rs index 1dae97a524..4be5c9ece6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use rustpython_parser::error::ParseError; use rustpython_vm::{ compile, error::CompileError, - frame::ScopeRef, + frame::Scope, import, obj::objstr, print_exception, @@ -82,8 +82,7 @@ fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> Py vm.new_exception(syntax_error, err.to_string()) })?; // trace!("Code object: {:?}", code_obj.borrow()); - let builtins = vm.get_builtin_scope(); - let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables + let vars = vm.ctx.new_scope(); // Keep track of local variables vm.run_code_obj(code_obj, vars) } @@ -121,7 +120,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult { } } -fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: ScopeRef) -> Result<(), CompileError> { +fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: Scope) -> Result<(), CompileError> { match compile::compile( source, &compile::Mode::Single, @@ -165,8 +164,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult { "Welcome to the magnificent Rust Python {} interpreter", crate_version!() ); - let builtins = vm.get_builtin_scope(); - let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables + let vars = vm.ctx.new_scope(); // Read a single line: let mut input = String::new(); diff --git a/tests/snippets/test_exec.py b/tests/snippets/test_exec.py index 37ba33ff13..61932c24cb 100644 --- a/tests/snippets/test_exec.py +++ b/tests/snippets/test_exec.py @@ -13,26 +13,21 @@ exec("assert max(1, 5, square(5)) == 25", None) -# -# These doesn't work yet: -# # Local environment shouldn't replace global environment: -# -# exec("assert max(1, 5, square(5)) == 25", None, {}) -# +exec("assert max(1, 5, square(5)) == 25", None, {}) + # Closures aren't available if local scope is replaced: -# -# def g(): -# seven = "seven" -# def f(): -# try: -# exec("seven", None, {}) -# except NameError: -# pass -# else: -# raise NameError("seven shouldn't be in scope") -# f() -# g() +def g(): + seven = "seven" + def f(): + try: + exec("seven", None, {}) + except NameError: + pass + else: + raise NameError("seven shouldn't be in scope") + f() +g() try: exec("", 1) @@ -40,3 +35,12 @@ pass else: raise TypeError("exec should fail unless globals is a dict or None") + +g = globals() +g['x'] = 2 +exec('x += 2') +assert x == 4 +assert g['x'] == x + +exec("del x") +assert 'x' not in g diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index ec79c55710..5a0c151957 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -14,7 +14,7 @@ use crate::obj::objiter; use crate::obj::objstr; use crate::obj::objtype; -use crate::frame::{Scope, ScopeRef}; +use crate::frame::Scope; use crate::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol, }; @@ -245,7 +245,7 @@ fn make_scope( vm: &mut VirtualMachine, globals: Option<&PyObjectRef>, locals: Option<&PyObjectRef>, -) -> PyResult { +) -> PyResult { let dict_type = vm.ctx.dict_type(); let globals = match globals { Some(arg) => { @@ -269,16 +269,16 @@ fn make_scope( }; let current_scope = vm.current_scope(); - let parent = match globals { - Some(dict) => Some(Scope::new(dict.clone(), Some(vm.get_builtin_scope()))), - None => current_scope.parent.clone(), + let globals = match globals { + Some(dict) => dict.clone(), + None => current_scope.globals.clone(), }; let locals = match locals { - Some(dict) => dict.clone(), - None => current_scope.locals.clone(), + Some(dict) => Some(dict.clone()), + None => current_scope.get_only_locals(), }; - Ok(Scope::new(locals, parent)) + Ok(Scope::new(locals, globals)) } fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -303,7 +303,9 @@ fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { vm.get_attribute(obj.clone(), attr.clone()) } -// builtin_globals +fn builtin_globals(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult { + Ok(vm.current_scope().globals.clone()) +} fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( @@ -743,6 +745,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "filter" => ctx.filter_type(), "format" => ctx.new_rustfunc(builtin_format), "getattr" => ctx.new_rustfunc(builtin_getattr), + "globals" => ctx.new_rustfunc(builtin_globals), "hasattr" => ctx.new_rustfunc(builtin_hasattr), "hash" => ctx.new_rustfunc(builtin_hash), "hex" => ctx.new_rustfunc(builtin_hex), diff --git a/vm/src/eval.rs b/vm/src/eval.rs index 98e9c54717..7f44cde89b 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -3,11 +3,11 @@ extern crate rustpython_parser; use std::error::Error; use crate::compile; -use crate::frame::ScopeRef; +use crate::frame::Scope; use crate::pyobject::PyResult; use crate::vm::VirtualMachine; -pub fn eval(vm: &mut VirtualMachine, source: &str, scope: ScopeRef, source_path: &str) -> PyResult { +pub fn eval(vm: &mut VirtualMachine, source: &str, scope: Scope, source_path: &str) -> PyResult { match compile::compile( source, &compile::Mode::Eval, @@ -34,7 +34,7 @@ mod tests { fn test_print_42() { let source = String::from("print('Hello world')\n"); let mut vm = VirtualMachine::new(); - let vars = vm.context().new_scope(None); + let vars = vm.ctx.new_scope(); let _result = eval(&mut vm, &source, vars, ""); // TODO: check result? diff --git a/vm/src/frame.rs b/vm/src/frame.rs index bb8e7088e0..0d87886370 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -32,16 +32,120 @@ use crate::vm::VirtualMachine; * When a name is looked up, it is check in its scope. */ #[derive(Debug)] +struct RcListNode { + elem: T, + next: Option>>, +} + +#[derive(Debug, Clone)] +struct RcList { + head: Option>>, +} + +struct Iter<'a, T: 'a> { + next: Option<&'a RcListNode>, +} + +impl RcList { + pub fn new() -> Self { + RcList { head: None } + } + + pub fn insert(self, elem: T) -> Self { + RcList { + head: Some(Rc::new(RcListNode { + elem, + next: self.head, + })), + } + } + + pub fn iter(&self) -> Iter { + Iter { + next: self.head.as_ref().map(|node| &**node), + } + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.next.map(|node| { + self.next = node.next.as_ref().map(|node| &**node); + &node.elem + }) + } +} + +#[derive(Debug, Clone)] pub struct Scope { - pub locals: PyObjectRef, // Variables - // TODO: pub locals: RefCell, // Variables - pub parent: Option>, // Parent scope + locals: RcList, + pub globals: PyObjectRef, } -pub type ScopeRef = Rc; impl Scope { - pub fn new(locals: PyObjectRef, parent: Option) -> ScopeRef { - Rc::new(Scope { locals, parent }) + pub fn new(locals: Option, globals: PyObjectRef) -> Scope { + let locals = match locals { + Some(dict) => RcList::new().insert(dict), + None => RcList::new(), + }; + Scope { locals, globals } + } + + pub fn get_locals(&self) -> PyObjectRef { + match self.locals.iter().next() { + Some(dict) => dict.clone(), + None => self.globals.clone(), + } + } + + pub fn get_only_locals(&self) -> Option { + match self.locals.iter().next() { + Some(dict) => Some(dict.clone()), + None => None, + } + } + + pub fn child_scope_with_locals(&self, locals: PyObjectRef) -> Scope { + Scope { + locals: self.locals.clone().insert(locals), + globals: self.globals.clone(), + } + } + + pub fn child_scope(&self, ctx: &PyContext) -> Scope { + self.child_scope_with_locals(ctx.new_dict()) + } +} + +pub trait NameProtocol { + fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option; + fn store_name(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef); + fn delete_name(&self, vm: &VirtualMachine, name: &str); +} + +impl NameProtocol for Scope { + fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option { + for dict in self.locals.iter() { + if let Some(value) = dict.get_item(name) { + return Some(value); + } + } + + if let Some(value) = self.globals.get_item(name) { + return Some(value); + } + + vm.builtins.get_item(name) + } + + fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) { + self.get_locals().set_item(&vm.ctx, key, value) + } + + fn delete_name(&self, _vm: &VirtualMachine, key: &str) { + self.get_locals().del_item(key) } } @@ -73,7 +177,7 @@ pub struct Frame { // We need 1 stack per frame stack: RefCell>, // The main data frame of the stack machine blocks: RefCell>, // Block frames, for controlling loops and exceptions - pub scope: ScopeRef, // Variables + pub scope: Scope, // Variables pub lasti: RefCell, // index of last instruction ran } @@ -93,7 +197,7 @@ pub enum ExecutionResult { pub type FrameResult = Result, PyObjectRef>; impl Frame { - pub fn new(code: PyObjectRef, scope: ScopeRef) -> Frame { + pub fn new(code: PyObjectRef, scope: Scope) -> Frame { //populate the globals and locals //TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95 /* @@ -735,9 +839,7 @@ impl Frame { let obj = import_module(vm, current_path, module)?; for (k, v) in obj.get_key_value_pairs().iter() { - self.scope - .locals - .set_item(&vm.ctx, &objstr::get_value(k), v.clone()); + self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); } Ok(None) } @@ -859,35 +961,26 @@ impl Frame { fn store_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { let obj = self.pop_value(); - self.scope.locals.set_item(&vm.ctx, name, obj); + self.scope.store_name(&vm, name, obj); Ok(None) } fn delete_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - let name = vm.ctx.new_str(name.to_string()); - vm.call_method(&self.scope.locals, "__delitem__", vec![name])?; + self.scope.delete_name(vm, name); Ok(None) } fn load_name(&self, vm: &mut VirtualMachine, name: &str) -> FrameResult { - // Lookup name in scope and put it onto the stack! - let mut scope = self.scope.clone(); - loop { - if scope.locals.contains_key(name) { - let obj = scope.locals.get_item(name).unwrap(); - self.push_value(obj); - return Ok(None); + match self.scope.load_name(&vm, name) { + Some(value) => { + self.push_value(value); + Ok(None) } - match &scope.parent { - Some(parent_scope) => { - scope = parent_scope.clone(); - } - None => { - let name_error_type = vm.ctx.exceptions.name_error.clone(); - let msg = format!("name '{}' is not defined", name); - let name_error = vm.new_exception(name_error_type, msg); - return Err(name_error); - } + None => { + let name_error_type = vm.ctx.exceptions.name_error.clone(); + let msg = format!("name '{}' is not defined", name); + let name_error = vm.new_exception(name_error_type, msg); + Err(name_error) } } } @@ -1138,7 +1231,7 @@ impl fmt::Debug for Frame { .map(|elem| format!("\n > {:?}", elem)) .collect::>() .join(""); - let local_str = match self.scope.locals.payload::() { + let local_str = match self.scope.get_locals().payload::() { Some(dict) => objdict::get_key_value_pairs_from_content(&dict.entries.borrow()) .iter() .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) diff --git a/vm/src/import.rs b/vm/src/import.rs index 3fbcf0f2fd..3031ed38b7 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -6,6 +6,7 @@ use std::error::Error; use std::path::PathBuf; use crate::compile; +use crate::frame::Scope; use crate::obj::{objsequence, objstr}; use crate::pyobject::{AttributeProtocol, DictProtocol, PyResult}; use crate::util; @@ -41,13 +42,10 @@ fn import_uncached_module( })?; // trace!("Code object: {:?}", code_obj); - let builtins = vm.get_builtin_scope(); - let scope = vm.ctx.new_scope(Some(builtins)); - scope - .locals - .set_item(&vm.ctx, "__name__", vm.new_str(module.to_string())); - vm.run_code_obj(code_obj, scope.clone())?; - Ok(vm.ctx.new_module(module, scope)) + let attrs = vm.ctx.new_dict(); + attrs.set_item(&vm.ctx, "__name__", vm.new_str(module.to_string())); + vm.run_code_obj(code_obj, Scope::new(None, attrs.clone()))?; + Ok(vm.ctx.new_module(module, attrs)) } pub fn import_module( diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 861e1db42a..788914b519 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -116,7 +116,7 @@ macro_rules! no_kwargs { macro_rules! py_module { ( $ctx:expr, $module_name:expr, { $($name:expr => $value:expr),* $(,)* }) => { { - let py_mod = $ctx.new_module($module_name, $ctx.new_scope(None)); + let py_mod = $ctx.new_module($module_name, $ctx.new_dict()); $( $ctx.set_attr(&py_mod, $name, $value); )* diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index fd1aafa2af..fe27b5b28f 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -1,3 +1,4 @@ +use crate::function::PyRef; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; @@ -18,6 +19,7 @@ pub struct PyDict { // TODO: should be private pub entries: RefCell, } +pub type PyDictRef = PyRef; impl PyObjectPayload2 for PyDict { fn required_type(ctx: &PyContext) -> PyObjectRef { @@ -29,7 +31,7 @@ pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref().unwrap().entries.borrow() } -fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { +pub fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut + 'a { obj.payload::().unwrap().entries.borrow_mut() } diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index 459b5a0c3d..e77f143590 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -28,7 +28,7 @@ fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { fn frame_flocals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); let frame = get_value(frame); - Ok(frame.scope.locals.clone()) + Ok(frame.scope.get_locals().clone()) } fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index af3eb9b9fc..2f278ca076 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,4 +1,4 @@ -use crate::frame::ScopeRef; +use crate::frame::Scope; use crate::pyobject::{ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyResult, TypeProtocol, @@ -9,12 +9,12 @@ use crate::vm::VirtualMachine; pub struct PyFunction { // TODO: these shouldn't be public pub code: PyObjectRef, - pub scope: ScopeRef, + pub scope: Scope, pub defaults: PyObjectRef, } impl PyFunction { - pub fn new(code: PyObjectRef, scope: ScopeRef, defaults: PyObjectRef) -> Self { + pub fn new(code: PyObjectRef, scope: Scope, defaults: PyObjectRef) -> Self { PyFunction { code, scope, diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index bbd109cce5..4f7a641b99 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,4 +1,3 @@ -use crate::frame::ScopeRef; use crate::function::PyRef; use crate::pyobject::{DictProtocol, PyContext, PyObjectPayload2, PyObjectRef, PyResult}; use crate::vm::VirtualMachine; @@ -6,7 +5,7 @@ use crate::vm::VirtualMachine; #[derive(Clone, Debug)] pub struct PyModule { pub name: String, - pub scope: ScopeRef, + pub dict: PyObjectRef, } pub type PyModuleRef = PyRef; @@ -17,10 +16,9 @@ impl PyObjectPayload2 for PyModule { } impl PyModuleRef { - fn dir(self, vm: &mut VirtualMachine) -> PyResult { + fn dir(self: PyModuleRef, vm: &mut VirtualMachine) -> PyResult { let keys = self - .scope - .locals + .dict .get_key_value_pairs() .iter() .map(|(k, _v)| k.clone()) diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 19b01d3905..994a7ca298 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -12,7 +12,7 @@ use num_traits::{One, Zero}; use crate::bytecode; use crate::exceptions; -use crate::frame::{Frame, Scope, ScopeRef}; +use crate::frame::{Frame, Scope}; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objbytearray; @@ -577,17 +577,16 @@ impl PyContext { objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() } - pub fn new_scope(&self, parent: Option) -> ScopeRef { - let locals = self.new_dict(); - Rc::new(Scope { locals, parent }) + pub fn new_scope(&self) -> Scope { + Scope::new(None, self.new_dict()) } - pub fn new_module(&self, name: &str, scope: ScopeRef) -> PyObjectRef { + pub fn new_module(&self, name: &str, dict: PyObjectRef) -> PyObjectRef { PyObject::new( PyObjectPayload::AnyRustValue { value: Box::new(PyModule { name: name.to_string(), - scope, + dict, }), }, self.module_type.clone(), @@ -606,7 +605,7 @@ impl PyContext { ) } - pub fn new_frame(&self, code: PyObjectRef, scope: ScopeRef) -> PyObjectRef { + pub fn new_frame(&self, code: PyObjectRef, scope: Scope) -> PyObjectRef { PyObject::new( PyObjectPayload::AnyRustValue { value: Box::new(Frame::new(code, scope)), @@ -634,7 +633,7 @@ impl PyContext { pub fn new_function( &self, code_obj: PyObjectRef, - scope: ScopeRef, + scope: Scope, defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( @@ -688,8 +687,8 @@ impl PyContext { } pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) { - if let Some(PyModule { ref scope, .. }) = obj.payload::() { - scope.locals.set_item(self, attr_name, value) + if let Some(PyModule { ref dict, .. }) = obj.payload::() { + dict.set_item(self, attr_name, value) } else if let Some(ref dict) = obj.dict { dict.borrow_mut().insert(attr_name.to_string(), value); } else { @@ -808,8 +807,8 @@ impl AttributeProtocol for PyObjectRef { return None; } - if let Some(PyModule { ref scope, .. }) = self.payload::() { - return scope.locals.get_item(attr_name); + if let Some(PyModule { ref dict, .. }) = self.payload::() { + return dict.get_item(attr_name); } if let Some(ref dict) = self.dict { @@ -825,8 +824,8 @@ impl AttributeProtocol for PyObjectRef { || mro.iter().any(|d| class_has_item(d, attr_name)); } - if let Some(PyModule { ref scope, .. }) = self.payload::() { - return scope.locals.contains_key(attr_name); + if let Some(PyModule { ref dict, .. }) = self.payload::() { + return dict.contains_key(attr_name); } if let Some(ref dict) = self.dict { @@ -842,6 +841,7 @@ pub trait DictProtocol { fn get_item(&self, k: &str) -> Option; fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)>; fn set_item(&self, ctx: &PyContext, key: &str, v: PyObjectRef); + fn del_item(&self, key: &str); } impl DictProtocol for PyObjectRef { @@ -856,8 +856,8 @@ impl DictProtocol for PyObjectRef { fn get_item(&self, k: &str) -> Option { if let Some(dict) = self.payload::() { objdict::content_get_key_str(&dict.entries.borrow(), k) - } else if let Some(PyModule { ref scope, .. }) = self.payload::() { - scope.locals.get_item(k) + } else if let Some(PyModule { ref dict, .. }) = self.payload::() { + dict.get_item(k) } else { panic!("TODO {:?}", k) } @@ -866,8 +866,8 @@ impl DictProtocol for PyObjectRef { fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { if let Some(_) = self.payload::() { objdict::get_key_value_pairs(self) - } else if let Some(PyModule { ref scope, .. }) = self.payload::() { - scope.locals.get_key_value_pairs() + } else if let Some(PyModule { ref dict, .. }) = self.payload::() { + dict.get_key_value_pairs() } else { panic!("TODO") } @@ -878,12 +878,17 @@ impl DictProtocol for PyObjectRef { if let Some(dict) = self.payload::() { let key = ctx.new_str(key.to_string()); objdict::set_item_in_content(&mut dict.entries.borrow_mut(), &key, &v); - } else if let Some(PyModule { ref scope, .. }) = self.payload::() { - scope.locals.set_item(ctx, key, v); + } else if let Some(PyModule { ref dict, .. }) = self.payload::() { + dict.set_item(ctx, key, v); } else { panic!("TODO {:?}", self); } } + + fn del_item(&self, key: &str) { + let mut elements = objdict::get_mut_elements(self); + elements.remove(key).unwrap(); + } } pub trait BufferProtocol { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 230162352f..0f95bf66c6 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -14,7 +14,7 @@ use std::sync::{Mutex, MutexGuard}; use crate::builtins; use crate::bytecode; use crate::frame::ExecutionResult; -use crate::frame::{Scope, ScopeRef}; +use crate::frame::Scope; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; @@ -23,7 +23,6 @@ use crate::obj::objfunction::{PyFunction, PyMethod}; use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objlist::PyList; -use crate::obj::objmodule::PyModule; use crate::obj::objsequence; use crate::obj::objstr; use crate::obj::objtuple::PyTuple; @@ -75,7 +74,7 @@ impl VirtualMachine { } } - pub fn run_code_obj(&mut self, code: PyObjectRef, scope: ScopeRef) -> PyResult { + pub fn run_code_obj(&mut self, code: PyObjectRef, scope: Scope) -> PyResult { let frame = self.ctx.new_frame(code, scope); self.run_frame_full(frame) } @@ -95,7 +94,7 @@ impl VirtualMachine { result } - pub fn current_scope(&self) -> &ScopeRef { + pub fn current_scope(&self) -> &Scope { let current_frame = &self.frames[self.frames.len() - 1]; let frame = objframe::get_value(current_frame); &frame.scope @@ -211,19 +210,13 @@ impl VirtualMachine { } pub fn get_locals(&self) -> PyObjectRef { - let scope = self.current_scope(); - scope.locals.clone() + self.current_scope().get_locals().clone() } pub fn context(&self) -> &PyContext { &self.ctx } - pub fn get_builtin_scope(&self) -> ScopeRef { - let PyModule { ref scope, .. } = self.builtins.payload::().unwrap(); - scope.clone() - } - // Container of the virtual machine state: pub fn to_str(&mut self, obj: &PyObjectRef) -> PyResult { self.call_method(&obj, "__str__", vec![]) @@ -323,13 +316,13 @@ impl VirtualMachine { fn invoke_python_function( &mut self, code: &PyObjectRef, - scope: &ScopeRef, + scope: &Scope, defaults: &PyObjectRef, args: PyFuncArgs, ) -> PyResult { let code_object = objcode::get_value(code); - let scope = self.ctx.new_scope(Some(scope.clone())); - self.fill_locals_from_args(&code_object, &scope.locals, args, defaults)?; + let scope = scope.child_scope(&self.ctx); + self.fill_locals_from_args(&code_object, &scope.get_locals(), args, defaults)?; // Construct frame: let frame = self.ctx.new_frame(code.clone(), scope); @@ -349,10 +342,7 @@ impl VirtualMachine { defaults: _, }) = &function.payload() { - let scope = Rc::new(Scope { - locals, - parent: Some(scope.clone()), - }); + let scope = scope.child_scope_with_locals(locals); let frame = self.ctx.new_frame(code.clone(), scope); return self.run_frame_full(frame); } diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 216ae76799..18de108957 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -4,8 +4,8 @@ use crate::wasm_builtins; use js_sys::{Object, Reflect, SyntaxError, TypeError}; use rustpython_vm::{ compile, - frame::ScopeRef, - pyobject::{DictProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult}, + frame::{NameProtocol, Scope}, + pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult}, VirtualMachine, }; use std::cell::RefCell; @@ -19,7 +19,7 @@ impl HeldRcInner for T {} pub(crate) struct StoredVirtualMachine { pub vm: VirtualMachine, - pub scope: ScopeRef, + pub scope: Scope, /// you can put a Rc in here, keep it as a Weak, and it'll be held only for /// as long as the StoredVM is alive held_rcs: Vec>, @@ -28,8 +28,7 @@ pub(crate) struct StoredVirtualMachine { impl StoredVirtualMachine { fn new(id: String, inject_browser_module: bool) -> StoredVirtualMachine { let mut vm = VirtualMachine::new(); - let builtin = vm.get_builtin_scope(); - let scope = vm.context().new_scope(Some(builtin)); + let scope = vm.ctx.new_scope(); if inject_browser_module { setup_browser_module(&mut vm); } @@ -259,7 +258,7 @@ impl WASMVirtualMachine { .. }| { let value = convert::js_to_py(vm, value); - scope.locals.set_item(&vm.ctx, &name, value); + scope.store_name(&vm, &name, value); }, ) } @@ -299,9 +298,7 @@ impl WASMVirtualMachine { ) .into()); }; - scope - .locals - .set_item(&vm.ctx, "print", vm.ctx.new_rustfunc(print_fn)); + scope.store_name(&vm, "print", vm.ctx.new_rustfunc(print_fn)); Ok(()) }, )? @@ -320,7 +317,7 @@ impl WASMVirtualMachine { let mod_name = name.clone(); let stdlib_init_fn = move |ctx: &PyContext| { - let py_mod = ctx.new_module(&name, ctx.new_scope(None)); + let py_mod = ctx.new_module(&name, ctx.new_dict()); for (key, value) in module_items.clone() { ctx.set_attr(&py_mod, &key, value); }