diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 7431ac439..78538e0fb 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -957,9 +957,8 @@ pub fn builtin_build_class_( function.invoke_with_scope(vec![].into(), &scope, vm)?; - let class = vm.call_method( + let class = vm.invoke( metaclass.as_object(), - "__call__", vec![name_obj, bases, namespace.into_object()], )?; cells.set_item("__class__", class.clone(), vm)?; diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 444d87ccc..421984e90 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -59,7 +59,6 @@ pub mod macros; mod builtins; pub mod cformat; -mod descriptor; mod dictdatatype; #[cfg(feature = "rustpython-compiler")] pub mod eval; @@ -75,6 +74,7 @@ mod pyhash; pub mod pyobject; pub mod scope; mod sequence; +mod slots; pub mod stdlib; mod sysmodule; pub mod types; diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 7dca09f4d..4737d137a 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -169,7 +169,7 @@ macro_rules! py_namespace { { let namespace = $vm.ctx.new_namespace(); $( - $vm.set_attr(&namespace, $name, $value).unwrap(); + $vm.__module_set_attr(&namespace, $name, $value).unwrap(); )* namespace } diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index 68780abd0..124eb4099 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -1,11 +1,11 @@ use std::fmt; -use crate::descriptor::PyBuiltinDescriptor; use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc}; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; +use crate::slots::{PyBuiltinCallable, PyBuiltinDescriptor}; use crate::vm::VirtualMachine; #[pyclass] @@ -35,14 +35,15 @@ impl PyBuiltinFunction { } } -#[pyimpl] -impl PyBuiltinFunction { - #[pymethod(name = "__call__")] - pub fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { +impl PyBuiltinCallable for PyBuiltinFunction { + fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { (self.value)(vm, args) } } +#[pyimpl(with(PyBuiltinCallable))] +impl PyBuiltinFunction {} + #[pyclass] pub struct PyBuiltinMethod { function: PyBuiltinFunction, @@ -87,14 +88,15 @@ impl PyBuiltinDescriptor for PyBuiltinMethod { } } -#[pyimpl(with(PyBuiltinDescriptor))] -impl PyBuiltinMethod { - #[pymethod(name = "__call__")] - pub fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { - self.function.call(args, vm) +impl PyBuiltinCallable for PyBuiltinMethod { + fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { + (self.function.value)(vm, args) } } +#[pyimpl(with(PyBuiltinDescriptor, PyBuiltinCallable))] +impl PyBuiltinMethod {} + pub fn init(context: &PyContext) { PyBuiltinFunction::extend_class(context, &context.types.builtin_function_or_method_type); PyBuiltinMethod::extend_class(context, &context.types.method_descriptor_type); diff --git a/vm/src/obj/objclassmethod.rs b/vm/src/obj/objclassmethod.rs index 2057c728d..34b652503 100644 --- a/vm/src/obj/objclassmethod.rs +++ b/vm/src/obj/objclassmethod.rs @@ -1,9 +1,9 @@ use super::objtype::PyClassRef; -use crate::descriptor::PyBuiltinDescriptor; use crate::function::OptionalArg; use crate::pyobject::{ PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; +use crate::slots::PyBuiltinDescriptor; use crate::vm::VirtualMachine; /// classmethod(function) -> method diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index ebeb004cd..f3b0e1fdb 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -4,7 +4,6 @@ use super::objstr::PyStringRef; use super::objtuple::PyTupleRef; use super::objtype::PyClassRef; use crate::bytecode; -use crate::descriptor::PyBuiltinDescriptor; use crate::frame::Frame; use crate::function::{OptionalArg, PyFuncArgs}; use crate::obj::objcoroutine::PyCoroutine; @@ -14,6 +13,7 @@ use crate::pyobject::{ TypeProtocol, }; use crate::scope::Scope; +use crate::slots::{PyBuiltinCallable, PyBuiltinDescriptor}; use crate::vm::VirtualMachine; pub type PyFunctionRef = PyRef; @@ -242,9 +242,10 @@ impl PyValue for PyFunction { #[pyimpl(with(PyBuiltinDescriptor))] impl PyFunction { + #[pyslot] #[pymethod(magic)] - fn call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { - vm.invoke(&zelf, args) + fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { + self.invoke(args, vm) } #[pyproperty(name = "__code__")] @@ -263,6 +264,7 @@ impl PyFunction { } } +#[pyclass] #[derive(Debug)] pub struct PyBoundMethod { // TODO: these shouldn't be public @@ -270,11 +272,22 @@ pub struct PyBoundMethod { pub function: PyObjectRef, } +impl PyBuiltinCallable for PyBoundMethod { + fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { + let args = args.insert(self.object.clone()); + vm.invoke(&self.function, args) + } +} + impl PyBoundMethod { pub fn new(object: PyObjectRef, function: PyObjectRef) -> Self { PyBoundMethod { object, function } } +} +#[pyimpl(with(PyBuiltinCallable))] +impl PyBoundMethod { + #[pymethod(magic)] fn getattribute(&self, name: PyStringRef, vm: &VirtualMachine) -> PyResult { vm.get_attribute(self.function.clone(), name) } @@ -291,7 +304,5 @@ pub fn init(context: &PyContext) { PyFunction::extend_class(context, function_type); let method_type = &context.types.bound_method_type; - extend_class!(context, method_type, { - "__getattribute__" => context.new_method(PyBoundMethod::getattribute), - }); + PyBoundMethod::extend_class(context, method_type); } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 0a1f1d554..e9aaa7622 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -77,7 +77,7 @@ fn object_hash(zelf: PyObjectRef, _vm: &VirtualMachine) -> pyhash::PyHash { zelf.get_id() as pyhash::PyHash } -fn object_setattr( +pub(crate) fn object_setattr( obj: PyObjectRef, attr_name: PyStringRef, value: PyObjectRef, diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 8445243bf..26e53666b 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -4,12 +4,12 @@ use std::cell::RefCell; use super::objtype::PyClassRef; -use crate::descriptor::PyBuiltinDescriptor; use crate::function::{IntoPyNativeFunc, OptionalArg}; use crate::pyobject::{ IdProtocol, PyClassImpl, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; +use crate::slots::PyBuiltinDescriptor; use crate::vm::VirtualMachine; // Read-only property, doesn't have __set__ or __delete__ diff --git a/vm/src/obj/objstaticmethod.rs b/vm/src/obj/objstaticmethod.rs index 3cc2af92e..09c3608a6 100644 --- a/vm/src/obj/objstaticmethod.rs +++ b/vm/src/obj/objstaticmethod.rs @@ -1,7 +1,7 @@ use super::objtype::PyClassRef; -use crate::descriptor::PyBuiltinDescriptor; use crate::function::OptionalArg; use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::slots::PyBuiltinDescriptor; use crate::vm::VirtualMachine; #[pyclass(name = "staticmethod")] diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index d5fe3e349..07625211c 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -9,11 +9,12 @@ use super::objproperty::PropertyBuilder; use super::objstr::PyStringRef; use super::objtuple::PyTuple; use super::objweakref::PyWeak; -use crate::function::{PyFuncArgs, PyNativeFunc}; +use crate::function::PyFuncArgs; use crate::pyobject::{ IdProtocol, PyAttributes, PyContext, PyIterable, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; +use crate::slots::PyClassSlots; use crate::vm::VirtualMachine; #[derive(Debug)] @@ -26,18 +27,6 @@ pub struct PyClass { pub slots: RefCell, } -#[derive(Default)] -pub struct PyClassSlots { - pub new: Option, - pub descr_get: Option, -} - -impl fmt::Debug for PyClassSlots { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("PyClassSlots") - } -} - impl fmt::Display for PyClass { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.name, f) @@ -255,6 +244,7 @@ pub fn init(ctx: &PyContext) { extend_class!(&ctx, &ctx.types.type_type, { "mro" => ctx.new_method(type_mro), "__call__" => ctx.new_method(type_call), + (slot call) => type_call, "__dict__" => PropertyBuilder::new(ctx) .add_getter(type_dict) diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index e9ff727e8..f2764c012 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -1,12 +1,14 @@ use super::objtype::PyClassRef; -use crate::function::OptionalArg; +use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - PyContext, PyObject, PyObjectPayload, PyObjectRef, PyRef, PyResult, PyValue, + PyClassImpl, PyContext, PyObject, PyObjectPayload, PyObjectRef, PyRef, PyResult, PyValue, }; +use crate::slots::PyBuiltinCallable; use crate::vm::VirtualMachine; use std::rc::{Rc, Weak}; +#[pyclass] #[derive(Debug)] pub struct PyWeak { referent: Weak>, @@ -32,25 +34,27 @@ impl PyValue for PyWeak { pub type PyWeakRef = PyRef; -impl PyWeakRef { +impl PyBuiltinCallable for PyWeak { + fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { + args.bind::<()>(vm)?; + Ok(self.referent.upgrade().unwrap_or_else(|| vm.get_none())) + } +} + +#[pyimpl(with(PyBuiltinCallable))] +impl PyWeak { // TODO callbacks - fn create( + #[pyslot] + fn tp_new( cls: PyClassRef, referent: PyObjectRef, _callback: OptionalArg, vm: &VirtualMachine, - ) -> PyResult { + ) -> PyResult> { PyWeak::downgrade(&referent).into_ref_with_type(vm, cls) } - - fn call(self, vm: &VirtualMachine) -> PyObjectRef { - self.referent.upgrade().unwrap_or_else(|| vm.get_none()) - } } pub fn init(context: &PyContext) { - extend_class!(context, &context.types.weakref_type, { - (slot new) => PyWeakRef::create, - "__call__" => context.new_method(PyWeakRef::call) - }); + PyWeak::extend_class(context, &context.types.weakref_type); } diff --git a/vm/src/descriptor.rs b/vm/src/slots.rs similarity index 50% rename from vm/src/descriptor.rs rename to vm/src/slots.rs index f298224cf..ecb9660e5 100644 --- a/vm/src/descriptor.rs +++ b/vm/src/slots.rs @@ -1,10 +1,30 @@ -use crate::function::OptionalArg; +use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc}; use crate::pyobject::{IdProtocol, PyObjectRef, PyRef, PyResult, PyValue}; use crate::VirtualMachine; +#[derive(Default)] +pub struct PyClassSlots { + pub new: Option, + pub call: Option, + pub descr_get: Option, +} + +impl std::fmt::Debug for PyClassSlots { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("PyClassSlots") + } +} + +#[pyimpl] +pub trait PyBuiltinCallable: PyValue { + #[pymethod(magic)] + #[pyslot] + fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult; +} + #[pyimpl] pub trait PyBuiltinDescriptor: PyValue { - #[pymethod(name = "__get__")] + #[pymethod(magic)] #[pyslot(descr_get)] fn get( zelf: PyRef, diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 38762ca77..023c2473f 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -26,14 +26,13 @@ use crate::frozen; use crate::function::PyFuncArgs; use crate::import; use crate::obj::objbool; -use crate::obj::objbuiltinfunc::{PyBuiltinFunction, PyBuiltinMethod}; use crate::obj::objcode::{PyCode, PyCodeRef}; use crate::obj::objdict::PyDictRef; -use crate::obj::objfunction::{PyBoundMethod, PyFunction}; use crate::obj::objint::PyInt; use crate::obj::objiter; use crate::obj::objlist::PyList; use crate::obj::objmodule::{self, PyModule}; +use crate::obj::objobject; use crate::obj::objstr::{PyString, PyStringRef}; use crate::obj::objtuple::PyTuple; use crate::obj::objtype::{self, PyClassRef}; @@ -656,31 +655,23 @@ impl VirtualMachine { } } - fn _invoke(&self, func_ref: &PyObjectRef, args: PyFuncArgs) -> PyResult { - vm_trace!("Invoke: {:?} {:?}", func_ref, args); - - if let Some(py_func) = func_ref.payload::() { + fn _invoke(&self, callable: &PyObjectRef, args: PyFuncArgs) -> PyResult { + vm_trace!("Invoke: {:?} {:?}", callable, args); + let class = callable.class(); + let slots = class.slots.borrow(); + if let Some(slot_call) = slots.borrow().call.as_ref() { self.trace_event(TraceEvent::Call)?; - let res = py_func.invoke(args, self); + let args = args.insert(callable.clone()); + let result = slot_call(self, args); self.trace_event(TraceEvent::Return)?; - res - } else if let Some(PyBoundMethod { - ref function, - ref object, - }) = func_ref.payload() - { - let args = args.insert(object.clone()); - self.invoke(&function, args) - } else if let Some(builtin_func) = func_ref.payload::() { - builtin_func.as_func()(self, args) - } else if let Some(method) = func_ref.payload::() { - method.as_func()(self, args) - } else if self.is_callable(&func_ref) { - self.call_method(&func_ref, "__call__", args) + result + } else if objtype::class_has_attr(&class, "__call__") { + let result = self.call_method(&callable, "__call__", args); + result } else { Err(self.new_type_error(format!( "'{}' object is not callable", - func_ref.class().name + callable.class().name ))) } } @@ -889,12 +880,8 @@ impl VirtualMachine { } pub fn is_callable(&self, obj: &PyObjectRef) -> bool { - match_class!(match obj { - PyFunction => true, - PyBoundMethod => true, - PyBuiltinFunction => true, - obj => objtype::class_has_attr(&obj.class(), "__call__"), - }) + obj.class().slots.borrow().call.is_some() + || objtype::class_has_attr(&obj.class(), "__call__") } #[inline] @@ -1311,8 +1298,7 @@ impl VirtualMachine { attr_value: impl Into, ) -> PyResult<()> { let val = attr_value.into(); - self.set_attr(module, attr_name, val)?; - Ok(()) + objobject::object_setattr(module.clone(), attr_name.try_into_ref(self)?, val, self) } }