From e2e5c7ab007d96cf8786b102f93ee8ad0fe2fb21 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 16 Jan 2020 02:16:30 +0900 Subject: [PATCH 1/4] Add PyBuiltinCallable and call slot --- vm/src/callable.rs | 10 ++++++++ vm/src/lib.rs | 1 + vm/src/macros.rs | 2 +- vm/src/obj/objbuiltinfunc.rs | 21 +++++++++------- vm/src/obj/objfunction.rs | 22 +++++++++++++---- vm/src/obj/objobject.rs | 2 +- vm/src/obj/objtype.rs | 2 ++ vm/src/obj/objweakref.rs | 3 ++- vm/src/vm.rs | 46 +++++++++++++----------------------- 9 files changed, 62 insertions(+), 47 deletions(-) create mode 100644 vm/src/callable.rs diff --git a/vm/src/callable.rs b/vm/src/callable.rs new file mode 100644 index 000000000..9e18779f3 --- /dev/null +++ b/vm/src/callable.rs @@ -0,0 +1,10 @@ +use crate::function::PyFuncArgs; +use crate::pyobject::{PyResult, PyValue}; +use crate::VirtualMachine; + +#[pyimpl] +pub trait PyBuiltinCallable: PyValue { + #[pymethod(magic)] + #[pyslot] + fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult; +} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 444d87ccc..fb4f1ee66 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -58,6 +58,7 @@ macro_rules! py_compile_bytecode { pub mod macros; mod builtins; +mod callable; pub mod cformat; mod descriptor; mod dictdatatype; 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..68ff0ca51 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -1,5 +1,6 @@ use std::fmt; +use crate::callable::PyBuiltinCallable; use crate::descriptor::PyBuiltinDescriptor; use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc}; use crate::obj::objtype::PyClassRef; @@ -35,14 +36,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 +89,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/objfunction.rs b/vm/src/obj/objfunction.rs index ebeb004cd..33aca496f 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -4,6 +4,7 @@ use super::objstr::PyStringRef; use super::objtuple::PyTupleRef; use super::objtype::PyClassRef; use crate::bytecode; +use crate::callable::PyBuiltinCallable; use crate::descriptor::PyBuiltinDescriptor; use crate::frame::Frame; use crate::function::{OptionalArg, PyFuncArgs}; @@ -242,9 +243,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 +265,7 @@ impl PyFunction { } } +#[pyclass] #[derive(Debug)] pub struct PyBoundMethod { // TODO: these shouldn't be public @@ -270,11 +273,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 +305,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/objtype.rs b/vm/src/obj/objtype.rs index d5fe3e349..7f607bb47 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -29,6 +29,7 @@ pub struct PyClass { #[derive(Default)] pub struct PyClassSlots { pub new: Option, + pub call: Option, pub descr_get: Option, } @@ -255,6 +256,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..16efd5ba4 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -51,6 +51,7 @@ impl PyWeakRef { pub fn init(context: &PyContext) { extend_class!(context, &context.types.weakref_type, { (slot new) => PyWeakRef::create, - "__call__" => context.new_method(PyWeakRef::call) + "__call__" => context.new_method(PyWeakRef::call), + (slot call) => PyWeakRef::call, }); } 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) } } From 80f3da92eee85aac9da0d6b145b47e15fec5bed6 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 26 Jan 2020 02:17:59 +0900 Subject: [PATCH 2/4] vm/src/slots.rs --- vm/src/callable.rs | 10 ---------- vm/src/lib.rs | 3 +-- vm/src/obj/objbuiltinfunc.rs | 3 +-- vm/src/obj/objclassmethod.rs | 2 +- vm/src/obj/objfunction.rs | 3 +-- vm/src/obj/objproperty.rs | 2 +- vm/src/obj/objstaticmethod.rs | 2 +- vm/src/obj/objtype.rs | 16 ++-------------- vm/src/{descriptor.rs => slots.rs} | 24 ++++++++++++++++++++++-- 9 files changed, 30 insertions(+), 35 deletions(-) delete mode 100644 vm/src/callable.rs rename vm/src/{descriptor.rs => slots.rs} (50%) diff --git a/vm/src/callable.rs b/vm/src/callable.rs deleted file mode 100644 index 9e18779f3..000000000 --- a/vm/src/callable.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::function::PyFuncArgs; -use crate::pyobject::{PyResult, PyValue}; -use crate::VirtualMachine; - -#[pyimpl] -pub trait PyBuiltinCallable: PyValue { - #[pymethod(magic)] - #[pyslot] - fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult; -} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index fb4f1ee66..421984e90 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -58,9 +58,7 @@ macro_rules! py_compile_bytecode { pub mod macros; mod builtins; -mod callable; pub mod cformat; -mod descriptor; mod dictdatatype; #[cfg(feature = "rustpython-compiler")] pub mod eval; @@ -76,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/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index 68ff0ca51..124eb4099 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -1,12 +1,11 @@ use std::fmt; -use crate::callable::PyBuiltinCallable; -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] 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 33aca496f..f3b0e1fdb 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -4,8 +4,6 @@ use super::objstr::PyStringRef; use super::objtuple::PyTupleRef; use super::objtype::PyClassRef; use crate::bytecode; -use crate::callable::PyBuiltinCallable; -use crate::descriptor::PyBuiltinDescriptor; use crate::frame::Frame; use crate::function::{OptionalArg, PyFuncArgs}; use crate::obj::objcoroutine::PyCoroutine; @@ -15,6 +13,7 @@ use crate::pyobject::{ TypeProtocol, }; use crate::scope::Scope; +use crate::slots::{PyBuiltinCallable, PyBuiltinDescriptor}; use crate::vm::VirtualMachine; pub type PyFunctionRef = PyRef; 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 7f607bb47..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,19 +27,6 @@ pub struct PyClass { pub slots: RefCell, } -#[derive(Default)] -pub struct PyClassSlots { - pub new: Option, - pub call: 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) 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, From eea157c2f0f70e758d578d3de710c318b92cc3c4 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 26 Jan 2020 02:45:23 +0900 Subject: [PATCH 3/4] Refactor PyWeak to use PyBuiltinCallable --- vm/src/obj/objweakref.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index 16efd5ba4..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,26 +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), - (slot call) => PyWeakRef::call, - }); + PyWeak::extend_class(context, &context.types.weakref_type); } From bebfb67842940d86befed9d4042cdef5e42ea625 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 26 Jan 2020 19:30:58 +0900 Subject: [PATCH 4/4] Use vm.invoke instead of calling `__call__` --- vm/src/builtins.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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)?;