diff --git a/bytecode/src/bytecode.rs b/bytecode/src/bytecode.rs index 74782d248..77ec814b5 100644 --- a/bytecode/src/bytecode.rs +++ b/bytecode/src/bytecode.rs @@ -37,6 +37,7 @@ pub struct CodeObject { /// Jump targets. pub label_map: HashMap, pub locations: Vec, + pub flags: CodeFlags, pub arg_names: Vec, // Names of positional arguments pub varargs: Varargs, // *args or * pub kwonlyarg_names: Vec, @@ -44,20 +45,20 @@ pub struct CodeObject { pub source_path: String, pub first_line_number: usize, pub obj_name: String, // Name of the object that created this code object - pub is_generator: bool, } bitflags! { #[derive(Serialize, Deserialize)] - pub struct FunctionOpArg: u8 { + pub struct CodeFlags: u8 { const HAS_DEFAULTS = 0x01; const HAS_KW_ONLY_DEFAULTS = 0x02; const HAS_ANNOTATIONS = 0x04; const NEW_LOCALS = 0x08; + const IS_GENERATOR = 0x10; } } -impl Default for FunctionOpArg { +impl Default for CodeFlags { fn default() -> Self { Self::NEW_LOCALS } @@ -176,9 +177,7 @@ pub enum Instruction { JumpIfFalseOrPop { target: Label, }, - MakeFunction { - flags: FunctionOpArg, - }, + MakeFunction, CallFunction { typ: CallType, }, @@ -355,6 +354,7 @@ pub enum BlockType { impl CodeObject { pub fn new( + flags: CodeFlags, arg_names: Vec, varargs: Varargs, kwonlyarg_names: Vec, @@ -367,6 +367,7 @@ impl CodeObject { instructions: Vec::new(), label_map: HashMap::new(), locations: Vec::new(), + flags, arg_names, varargs, kwonlyarg_names, @@ -374,7 +375,6 @@ impl CodeObject { source_path, first_line_number, obj_name, - is_generator: false, } } @@ -513,7 +513,7 @@ impl Instruction { JumpIfFalse { target } => w!(JumpIfFalse, label_map[target]), JumpIfTrueOrPop { target } => w!(JumpIfTrueOrPop, label_map[target]), JumpIfFalseOrPop { target } => w!(JumpIfFalseOrPop, label_map[target]), - MakeFunction { flags } => w!(MakeFunction, format!("{:?}", flags)), + MakeFunction => w!(MakeFunction), CallFunction { typ } => w!(CallFunction, format!("{:?}", typ)), ForIter { target } => w!(ForIter, label_map[target]), ReturnValue => w!(ReturnValue), diff --git a/compiler/src/compile.rs b/compiler/src/compile.rs index b6f602194..ca185e669 100644 --- a/compiler/src/compile.rs +++ b/compiler/src/compile.rs @@ -137,6 +137,7 @@ impl Compiler { fn push_new_code_object(&mut self, obj_name: String) { let line_number = self.get_source_line_number(); self.push_output(CodeObject::new( + Default::default(), Vec::new(), Varargs::None, Vec::new(), @@ -595,11 +596,7 @@ impl Compiler { Ok(()) } - fn enter_function( - &mut self, - name: &str, - args: &ast::Parameters, - ) -> Result { + fn enter_function(&mut self, name: &str, args: &ast::Parameters) -> Result<(), CompileError> { let have_defaults = !args.defaults.is_empty(); if have_defaults { // Construct a tuple: @@ -633,8 +630,17 @@ impl Compiler { }); } + let mut flags = bytecode::CodeFlags::default(); + if have_defaults { + flags |= bytecode::CodeFlags::HAS_DEFAULTS; + } + if num_kw_only_defaults > 0 { + flags |= bytecode::CodeFlags::HAS_KW_ONLY_DEFAULTS; + } + let line_number = self.get_source_line_number(); self.push_output(CodeObject::new( + flags, args.args.iter().map(|a| a.arg.clone()).collect(), compile_varargs(&args.vararg), args.kwonlyargs.iter().map(|a| a.arg.clone()).collect(), @@ -645,15 +651,7 @@ impl Compiler { )); self.enter_scope(); - let mut flags = bytecode::FunctionOpArg::default(); - if have_defaults { - flags |= bytecode::FunctionOpArg::HAS_DEFAULTS; - } - if num_kw_only_defaults > 0 { - flags |= bytecode::FunctionOpArg::HAS_KW_ONLY_DEFAULTS; - } - - Ok(flags) + Ok(()) } fn prepare_decorators( @@ -813,7 +811,7 @@ impl Compiler { self.prepare_decorators(decorator_list)?; - let mut flags = self.enter_function(name, args)?; + self.enter_function(name, args)?; let (body, doc_str) = get_doc(body); @@ -832,7 +830,7 @@ impl Compiler { } } - let code = self.pop_code_object(); + let mut code = self.pop_code_object(); self.leave_scope(); // Prepare type annotations: @@ -864,7 +862,7 @@ impl Compiler { } if num_annotations > 0 { - flags |= bytecode::FunctionOpArg::HAS_ANNOTATIONS; + code.flags |= bytecode::CodeFlags::HAS_ANNOTATIONS; self.emit(Instruction::BuildMap { size: num_annotations, unpack: false, @@ -884,7 +882,7 @@ impl Compiler { }); // Turn code object into function object: - self.emit(Instruction::MakeFunction { flags }); + self.emit(Instruction::MakeFunction); self.store_docstring(doc_str); self.apply_decorators(decorator_list); @@ -915,6 +913,7 @@ impl Compiler { self.emit(Instruction::LoadBuildClass); let line_number = self.get_source_line_number(); self.push_output(CodeObject::new( + Default::default(), vec![], Varargs::None, vec![], @@ -950,7 +949,8 @@ impl Compiler { }); self.emit(Instruction::ReturnValue); - let code = self.pop_code_object(); + let mut code = self.pop_code_object(); + code.flags &= !bytecode::CodeFlags::NEW_LOCALS; self.leave_scope(); self.emit(Instruction::LoadConst { @@ -965,9 +965,7 @@ impl Compiler { }); // Turn code object into function object: - self.emit(Instruction::MakeFunction { - flags: bytecode::FunctionOpArg::default() & !bytecode::FunctionOpArg::NEW_LOCALS, - }); + self.emit(Instruction::MakeFunction); self.emit(Instruction::LoadConst { value: bytecode::Constant::String { @@ -1615,7 +1613,7 @@ impl Compiler { Lambda { args, body } => { let name = "".to_string(); // no need to worry about the self.loop_depth because there are no loops in lambda expressions - let flags = self.enter_function(&name, args)?; + self.enter_function(&name, args)?; self.compile_expression(body)?; self.emit(Instruction::ReturnValue); let code = self.pop_code_object(); @@ -1629,7 +1627,7 @@ impl Compiler { value: bytecode::Constant::String { value: name }, }); // Turn code object into function object: - self.emit(Instruction::MakeFunction { flags }); + self.emit(Instruction::MakeFunction); } Comprehension { kind, generators } => { self.compile_comprehension(kind, generators)?; @@ -1810,6 +1808,7 @@ impl Compiler { let line_number = self.get_source_line_number(); // Create magnificent function : self.push_output(CodeObject::new( + Default::default(), vec![".0".to_string()], Varargs::None, vec![], @@ -1945,9 +1944,7 @@ impl Compiler { }); // Turn code object into function object: - self.emit(Instruction::MakeFunction { - flags: bytecode::FunctionOpArg::default(), - }); + self.emit(Instruction::MakeFunction); // Evaluate iterated item: self.compile_expression(&generators[0].iter)?; diff --git a/compiler/src/output_stream.rs b/compiler/src/output_stream.rs index 9d0ece09c..fa358817a 100644 --- a/compiler/src/output_stream.rs +++ b/compiler/src/output_stream.rs @@ -1,4 +1,4 @@ -use rustpython_bytecode::bytecode::{CodeObject, Instruction, Label, Location}; +use rustpython_bytecode::bytecode::{CodeFlags, CodeObject, Instruction, Label, Location}; pub trait OutputStream: From + Into { /// Output an instruction @@ -34,6 +34,6 @@ impl OutputStream for CodeObjectStream { self.code.label_map.insert(label, position); } fn mark_generator(&mut self) { - self.code.is_generator = true; + self.code.flags |= CodeFlags::IS_GENERATOR; } } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 67b3d3f4c..b5e3eca16 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -79,13 +79,18 @@ enum UnwindReason { Continue, } +#[pyclass] pub struct Frame { - pub code: bytecode::CodeObject, + pub code: PyCodeRef, // 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: Scope, // Variables - pub lasti: RefCell, // index of last instruction ran + /// The main data frame of the stack machine + stack: RefCell>, + /// Block frames, for controlling loops and exceptions + blocks: RefCell>, + /// Variables + pub scope: Scope, + /// index of last instruction ran + pub lasti: RefCell, } impl PyValue for Frame { @@ -117,7 +122,7 @@ impl Frame { // locals.extend(callargs); Frame { - code: code.code.clone(), + code, stack: RefCell::new(vec![]), blocks: RefCell::new(vec![]), // save the callargs as locals @@ -400,7 +405,7 @@ impl Frame { Ok(None) } bytecode::Instruction::ForIter { target } => self.execute_for_iter(vm, *target), - bytecode::Instruction::MakeFunction { flags } => self.execute_make_function(vm, *flags), + bytecode::Instruction::MakeFunction => self.execute_make_function(vm), bytecode::Instruction::CallFunction { typ } => self.execute_call_function(vm, typ), bytecode::Instruction::Jump { target } => { self.jump(*target); @@ -1030,27 +1035,25 @@ impl Frame { } } } - fn execute_make_function( - &self, - vm: &VirtualMachine, - flags: bytecode::FunctionOpArg, - ) -> FrameResult { + fn execute_make_function(&self, vm: &VirtualMachine) -> FrameResult { let qualified_name = self .pop_value() .downcast::() .expect("qualified name to be a string"); - let code_obj = self + let code_obj: PyCodeRef = self .pop_value() .downcast() .expect("Second to top value on the stack must be a code object"); - let annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { + let flags = code_obj.flags; + + let annotations = if flags.contains(bytecode::CodeFlags::HAS_ANNOTATIONS) { self.pop_value() } else { vm.ctx.new_dict().into_object() }; - let kw_only_defaults = if flags.contains(bytecode::FunctionOpArg::HAS_KW_ONLY_DEFAULTS) { + let kw_only_defaults = if flags.contains(bytecode::CodeFlags::HAS_KW_ONLY_DEFAULTS) { Some( self.pop_value() .downcast::() @@ -1060,7 +1063,7 @@ impl Frame { None }; - let defaults = if flags.contains(bytecode::FunctionOpArg::HAS_DEFAULTS) { + let defaults = if flags.contains(bytecode::CodeFlags::HAS_DEFAULTS) { Some( self.pop_value() .downcast::() @@ -1073,13 +1076,9 @@ impl Frame { // pop argc arguments // argument: name, args, globals let scope = self.scope.clone(); - let func_obj = vm.ctx.new_function( - code_obj, - scope, - defaults, - kw_only_defaults, - flags.contains(bytecode::FunctionOpArg::NEW_LOCALS), - ); + let func_obj = vm + .ctx + .new_function(code_obj, scope, defaults, kw_only_defaults); let name = qualified_name.as_str().split('.').next_back().unwrap(); vm.set_attr(&func_obj, "__name__", vm.new_str(name.to_string()))?; diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index fdf5250d3..c15fe47f9 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -3,6 +3,7 @@ */ use std::fmt; +use std::ops::Deref; use super::objtype::PyClassRef; use crate::bytecode; @@ -15,6 +16,13 @@ pub struct PyCode { pub code: bytecode::CodeObject, } +impl Deref for PyCode { + type Target = bytecode::CodeObject; + fn deref(&self) -> &Self::Target { + &self.code + } +} + impl PyCode { pub fn new(code: bytecode::CodeObject) -> PyCode { PyCode { code } diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index 2b2d2e2bd..3b447f4f0 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -5,48 +5,47 @@ use super::objcode::PyCodeRef; use super::objdict::PyDictRef; use crate::frame::FrameRef; -use crate::pyobject::{PyContext, PyObjectRef, PyResult}; +use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyResult}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { - extend_class!(context, &context.types.frame_type, { - (slot new) => FrameRef::new, - "__repr__" => context.new_rustfunc(FrameRef::repr), - "f_locals" => context.new_property(FrameRef::flocals), - "f_globals" => context.new_property(FrameRef::f_globals), - "f_code" => context.new_property(FrameRef::fcode), - "f_back" => context.new_property(FrameRef::f_back), - "f_lasti" => context.new_property(FrameRef::f_lasti), - }); + FrameRef::extend_class(context, &context.types.frame_type); } +#[pyimpl] impl FrameRef { - #[allow(clippy::new_ret_no_self)] - fn new(_class: FrameRef, vm: &VirtualMachine) -> PyResult { + #[pyslot(new)] + fn tp_new(_cls: FrameRef, vm: &VirtualMachine) -> PyResult { Err(vm.new_type_error("Cannot directly create frame object".to_string())) } + #[pymethod(name = "__repr__")] fn repr(self, _vm: &VirtualMachine) -> String { "".to_string() } + #[pyproperty] fn f_globals(self, _vm: &VirtualMachine) -> PyDictRef { self.scope.globals.clone() } - fn flocals(self, _vm: &VirtualMachine) -> PyDictRef { + #[pyproperty] + fn f_locals(self, _vm: &VirtualMachine) -> PyDictRef { self.scope.get_locals() } - fn fcode(self, vm: &VirtualMachine) -> PyCodeRef { - vm.ctx.new_code_object(self.code.clone()) + #[pyproperty] + fn f_code(self, _vm: &VirtualMachine) -> PyCodeRef { + self.code.clone() } + #[pyproperty] fn f_back(self, vm: &VirtualMachine) -> PyObjectRef { // TODO: how to retrieve the upper stack frame?? vm.ctx.none() } + #[pyproperty] fn f_lasti(self, vm: &VirtualMachine) -> PyObjectRef { vm.ctx.new_int(*self.lasti.borrow()) } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index e4f597c57..2af9f0362 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -16,7 +16,6 @@ pub struct PyFunction { pub scope: Scope, pub defaults: Option, pub kw_only_defaults: Option, - pub new_locals: bool, } impl PyFunction { @@ -25,14 +24,12 @@ impl PyFunction { scope: Scope, defaults: Option, kw_only_defaults: Option, - new_locals: bool, ) -> Self { PyFunction { code, scope, defaults, kw_only_defaults, - new_locals, } } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index eddde2b0f..40fc7b9ce 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -503,10 +503,9 @@ impl PyContext { scope: Scope, defaults: Option, kw_only_defaults: Option, - new_locals: bool, ) -> PyObjectRef { PyObject::new( - PyFunction::new(code_obj, scope, defaults, kw_only_defaults, new_locals), + PyFunction::new(code_obj, scope, defaults, kw_only_defaults), self.function_type(), Some(self.new_dict()), ) diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 0c7513685..90221fd79 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -668,14 +668,14 @@ impl VirtualMachine { ) -> PyResult { let code = &func.code; - let scope = if func.new_locals { + let scope = if func.code.flags.contains(bytecode::CodeFlags::NEW_LOCALS) { scope.new_child_scope(&self.ctx) } else { scope.clone() }; self.fill_locals_from_args( - &code.code, + &code, &scope.get_locals(), func_args, &func.defaults, @@ -686,7 +686,7 @@ impl VirtualMachine { let frame = Frame::new(code.clone(), scope).into_ref(self); // If we have a generator, create a new generator - if code.code.is_generator { + if code.flags.contains(bytecode::CodeFlags::IS_GENERATOR) { Ok(PyGenerator::new(frame, self).into_object()) } else { self.run_frame_full(frame)