diff --git a/vm/src/frame.rs b/vm/src/frame.rs index db913fe1b8..391ab6fe3d 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -11,6 +11,7 @@ use crate::builtins; use crate::bytecode; use crate::import::{import, import_module}; use crate::obj::objbool; +use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; use crate::obj::objdict; use crate::obj::objdict::PyDict; @@ -20,8 +21,8 @@ use crate::obj::objlist; use crate::obj::objstr; use crate::obj::objtype; use crate::pyobject::{ - DictProtocol, IdProtocol, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, - TryFromObject, TypeProtocol, + DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, + PyObjectRef, PyResult, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -75,6 +76,12 @@ pub struct Frame { pub lasti: RefCell, // index of last instruction ran } +impl PyObjectPayload2 for Frame { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.frame_type() + } +} + // Running a frame can result in one of the below: pub enum ExecutionResult { Return(PyObjectRef), @@ -592,8 +599,10 @@ impl Frame { } bytecode::Instruction::LoadBuildClass => { let rustfunc = PyObject::new( - PyObjectPayload::RustFunction { - function: Box::new(builtins::builtin_build_class_), + PyObjectPayload::AnyRustValue { + value: Box::new(PyBuiltinFunction::new(Box::new( + builtins::builtin_build_class_, + ))), }, vm.ctx.type_type(), ); diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index 6cbb5fa66f..3d068df648 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -1,6 +1,7 @@ //! This package contains the python basic/builtin types pub mod objbool; +pub mod objbuiltinfunc; pub mod objbytearray; pub mod objbytes; pub mod objcode; diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs new file mode 100644 index 0000000000..8e3b2c3422 --- /dev/null +++ b/vm/src/obj/objbuiltinfunc.rs @@ -0,0 +1,26 @@ +use std::fmt; + +use crate::pyobject::{PyContext, PyNativeFunc, PyObjectPayload2, PyObjectRef}; + +pub struct PyBuiltinFunction { + // TODO: shouldn't be public + pub value: PyNativeFunc, +} + +impl PyObjectPayload2 for PyBuiltinFunction { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.builtin_function_or_method_type() + } +} + +impl fmt::Debug for PyBuiltinFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "builtin function") + } +} + +impl PyBuiltinFunction { + pub fn new(value: PyNativeFunc) -> Self { + Self { value } + } +} diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index 37acd33385..459b5a0c3d 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -3,9 +3,7 @@ */ use crate::frame::Frame; -use crate::pyobject::{ - PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { @@ -39,9 +37,5 @@ fn frame_fcode(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn get_value(obj: &PyObjectRef) -> &Frame { - if let PyObjectPayload::Frame { frame } = &obj.payload { - frame - } else { - panic!("Inner error getting int {:?}", obj); - } + &obj.payload::().unwrap() } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 26fee403fa..432266225b 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,8 +1,53 @@ +use crate::frame::ScopeRef; use crate::pyobject::{ - AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyResult, TypeProtocol, + AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; +#[derive(Debug)] +pub struct PyFunction { + // TODO: these shouldn't be public + pub code: PyObjectRef, + pub scope: ScopeRef, + pub defaults: PyObjectRef, +} + +impl PyFunction { + pub fn new(code: PyObjectRef, scope: ScopeRef, defaults: PyObjectRef) -> Self { + PyFunction { + code, + scope, + defaults, + } + } +} + +impl PyObjectPayload2 for PyFunction { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.function_type() + } +} + +#[derive(Debug)] +pub struct PyMethod { + // TODO: these shouldn't be public + pub object: PyObjectRef, + pub function: PyObjectRef, +} + +impl PyMethod { + pub fn new(object: PyObjectRef, function: PyObjectRef) -> Self { + PyMethod { object, function } + } +} + +impl PyObjectPayload2 for PyMethod { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.bound_method_type() + } +} + pub fn init(context: &PyContext) { let function_type = &context.function_type; context.set_attr(&function_type, "__get__", context.new_rustfunc(bind_method)); @@ -79,9 +124,9 @@ fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { - match args.args[0].payload { - PyObjectPayload::Function { ref code, .. } => Ok(code.clone()), - _ => Err(vm.new_type_error("no code".to_string())), + match args.args[0].payload() { + Some(PyFunction { ref code, .. }) => Ok(code.clone()), + None => Err(vm.new_type_error("no code".to_string())), } } diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index 05a7a60ae6..1babd5c9dc 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -2,12 +2,24 @@ * The mythical generator. */ -use crate::frame::ExecutionResult; +use crate::frame::{ExecutionResult, Frame}; use crate::pyobject::{ - PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol, + PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult, + TypeProtocol, }; use crate::vm::VirtualMachine; +#[derive(Debug)] +pub struct PyGenerator { + frame: PyObjectRef, +} + +impl PyObjectPayload2 for PyGenerator { + fn required_type(ctx: &PyContext) -> PyObjectRef { + ctx.generator_type() + } +} + pub fn init(context: &PyContext) { let generator_type = &context.generator_type; context.set_attr( @@ -29,7 +41,9 @@ pub fn init(context: &PyContext) { pub fn new_generator(vm: &mut VirtualMachine, frame: PyObjectRef) -> PyResult { Ok(PyObject::new( - PyObjectPayload::Generator { frame }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyGenerator { frame }), + }, vm.ctx.generator_type.clone(), )) } @@ -55,8 +69,8 @@ fn generator_send(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } fn send(vm: &mut VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { - if let PyObjectPayload::Generator { ref frame } = gen.payload { - if let PyObjectPayload::Frame { ref frame } = frame.payload { + if let Some(PyGenerator { ref frame }) = gen.payload() { + if let Some(frame) = frame.payload::() { frame.push_value(value.clone()); } else { panic!("Generator frame isn't a frame."); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 14f7b6e7b1..c9c7ed02ac 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -14,6 +14,7 @@ use crate::bytecode; use crate::exceptions; use crate::frame::{Frame, Scope, ScopeRef}; use crate::obj::objbool; +use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objbytearray; use crate::obj::objbytes; use crate::obj::objcode; @@ -24,7 +25,7 @@ use crate::obj::objenumerate; use crate::obj::objfilter; use crate::obj::objfloat::{self, PyFloat}; use crate::obj::objframe; -use crate::obj::objfunction; +use crate::obj::objfunction::{self, PyFunction, PyMethod}; use crate::obj::objgenerator; use crate::obj::objint::{self, PyInt}; use crate::obj::objiter; @@ -615,8 +616,8 @@ impl PyContext { F: IntoPyNativeFunc, { PyObject::new( - PyObjectPayload::RustFunction { - function: f.into_func(), + PyObjectPayload::AnyRustValue { + value: Box::new(PyBuiltinFunction::new(f.into_func())), }, self.builtin_function_or_method_type(), ) @@ -624,8 +625,8 @@ impl PyContext { pub fn new_frame(&self, code: PyObjectRef, scope: ScopeRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::Frame { - frame: Frame::new(code, scope), + PyObjectPayload::AnyRustValue { + value: Box::new(Frame::new(code, scope)), }, self.frame_type(), ) @@ -657,10 +658,8 @@ impl PyContext { defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( - PyObjectPayload::Function { - code: code_obj, - scope, - defaults, + PyObjectPayload::AnyRustValue { + value: Box::new(PyFunction::new(code_obj, scope, defaults)), }, self.function_type(), ) @@ -668,7 +667,9 @@ impl PyContext { pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( - PyObjectPayload::BoundMethod { function, object }, + PyObjectPayload::AnyRustValue { + value: Box::new(PyMethod::new(object, function)), + }, self.bound_method_type(), ) } @@ -1507,27 +1508,9 @@ pub enum PyObjectPayload { MemoryView { obj: PyObjectRef, }, - Frame { - frame: Frame, - }, - Function { - code: PyObjectRef, - scope: ScopeRef, - defaults: PyObjectRef, - }, - Generator { - frame: PyObjectRef, - }, - BoundMethod { - function: PyObjectRef, - object: PyObjectRef, - }, WeakRef { referent: PyObjectWeakRef, }, - RustFunction { - function: PyNativeFunc, - }, AnyRustValue { value: Box, }, @@ -1548,14 +1531,6 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::WeakRef { .. } => write!(f, "weakref"), PyObjectPayload::Iterator { .. } => write!(f, "iterator"), PyObjectPayload::Slice { .. } => write!(f, "slice"), - PyObjectPayload::Function { .. } => write!(f, "function"), - PyObjectPayload::Generator { .. } => write!(f, "generator"), - PyObjectPayload::BoundMethod { - ref function, - ref object, - } => write!(f, "bound-method: {:?} of {:?}", function, object), - PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), - PyObjectPayload::Frame { .. } => write!(f, "frame"), PyObjectPayload::AnyRustValue { value } => value.fmt(f), } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index bf5ce74332..32a0104d23 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -16,8 +16,10 @@ use crate::bytecode; use crate::frame::ExecutionResult; use crate::frame::{Scope, ScopeRef}; use crate::obj::objbool; +use crate::obj::objbuiltinfunc::PyBuiltinFunction; use crate::obj::objcode; use crate::obj::objframe; +use crate::obj::objfunction::{PyFunction, PyMethod}; use crate::obj::objgenerator; use crate::obj::objiter; use crate::obj::objlist::PyList; @@ -27,8 +29,8 @@ use crate::obj::objstr; use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::pyobject::{ - AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, - PyObjectRef, PyResult, TypeProtocol, + AttributeProtocol, DictProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectRef, PyResult, + TypeProtocol, }; use crate::stdlib; use crate::sysmodule; @@ -289,23 +291,28 @@ impl VirtualMachine { { let args = args.into(); trace!("Invoke: {:?} {:?}", func_ref, args); - match func_ref.payload { - PyObjectPayload::RustFunction { ref function } => function(self, args), - PyObjectPayload::Function { - ref code, - ref scope, - ref defaults, - } => self.invoke_python_function(code, scope, defaults, args), - PyObjectPayload::BoundMethod { - ref function, - ref object, - } => self.invoke(function.clone(), args.insert(object.clone())), - ref payload => { - // TODO: is it safe to just invoke __call__ otherwise? - trace!("invoke __call__ for: {:?}", payload); - self.call_method(&func_ref, "__call__", args) - } + if let Some(PyFunction { + ref code, + ref scope, + ref defaults, + }) = func_ref.payload() + { + return self.invoke_python_function(code, scope, defaults, args); + } + if let Some(PyMethod { + ref function, + ref object, + }) = func_ref.payload() + { + return self.invoke(function.clone(), args.insert(object.clone())); + } + if let Some(PyBuiltinFunction { ref value }) = func_ref.payload() { + return value(self, args); } + + // TODO: is it safe to just invoke __call__ otherwise? + trace!("invoke __call__ for: {:?}", func_ref.payload); + self.call_method(&func_ref, "__call__", args) } fn invoke_python_function( @@ -331,11 +338,11 @@ impl VirtualMachine { } pub fn invoke_with_locals(&mut self, function: PyObjectRef, locals: PyObjectRef) -> PyResult { - if let PyObjectPayload::Function { + if let Some(PyFunction { code, scope, - defaults: _defaults, - } = &function.payload + defaults: _, + }) = &function.payload() { let scope = Rc::new(Scope { locals,