From ac320f572d02ed847fd9b9be09d8d33efc1a1bfd Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Wed, 3 Feb 2021 09:07:32 -0600 Subject: [PATCH] wip descr_set --- vm/src/builtins/getset.rs | 54 +++++++++++++++++++++---------------- vm/src/builtins/object.rs | 10 ++++--- vm/src/builtins/property.rs | 40 ++++++++++++++++----------- vm/src/builtins/pytype.rs | 51 ++++++++++++++++++++++------------- vm/src/macros.rs | 4 +-- vm/src/pyobject.rs | 4 +-- vm/src/slots.rs | 3 +++ vm/src/vm.rs | 5 +++- 8 files changed, 105 insertions(+), 66 deletions(-) diff --git a/vm/src/builtins/getset.rs b/vm/src/builtins/getset.rs index 5a84c5724e..8b91ca62de 100644 --- a/vm/src/builtins/getset.rs +++ b/vm/src/builtins/getset.rs @@ -303,29 +303,37 @@ impl PyGetSet { impl PyGetSet { // Descriptor methods - #[pymethod(magic)] - fn set(&self, obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - if let Some(ref f) = self.setter { - f(vm, obj, value) - } else { - Err(vm.new_attribute_error(format!( - "attribute '{}' of '{}' objects is not writable", - self.name, - obj.class().name - ))) - } - } - - #[pymethod(magic)] - fn delete(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - if let Some(ref f) = self.deleter { - f(vm, obj) - } else { - Err(vm.new_attribute_error(format!( - "attribute '{}' of '{}' objects is not writable", - self.name, - obj.class().name - ))) + #[pyslot] + fn descr_set( + zelf: PyObjectRef, + obj: PyObjectRef, + value: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + let zelf = PyRef::::try_from_object(vm, zelf)?; + match value { + Some(value) => { + if let Some(ref f) = zelf.setter { + f(vm, obj, value) + } else { + Err(vm.new_attribute_error(format!( + "attribute '{}' of '{}' objects is not writable", + zelf.name, + obj.class().name + ))) + } + } + None => { + if let Some(ref f) = zelf.deleter { + f(vm, obj) + } else { + Err(vm.new_attribute_error(format!( + "attribute '{}' of '{}' objects is not writable", + zelf.name, + obj.class().name + ))) + } + } } } diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index 09722d6a6d..487b2337b7 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -144,8 +144,9 @@ impl PyBaseObject { #[pymethod(magic)] fn delattr(obj: PyObjectRef, attr_name: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { if let Some(attr) = obj.get_class_attr(attr_name.borrow_value()) { - if let Some(descriptor) = attr.get_class_attr("__delete__") { - return vm.invoke(&descriptor, vec![attr, obj]).map(|_| ()); + let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load()); + if let Some(descriptor) = descr_set { + return descriptor(attr, obj, None, vm); } } @@ -292,8 +293,9 @@ pub(crate) fn setattr( vm_trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value); if let Some(attr) = obj.get_class_attr(attr_name.borrow_value()) { - if let Some(descriptor) = attr.get_class_attr("__set__") { - return vm.invoke(&descriptor, vec![attr, obj, value]).map(|_| ()); + let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load()); + if let Some(descriptor) = descr_set { + return descriptor(attr, obj, Some(value), vm); } } diff --git a/vm/src/builtins/property.rs b/vm/src/builtins/property.rs index cf18ac9687..2917434818 100644 --- a/vm/src/builtins/property.rs +++ b/vm/src/builtins/property.rs @@ -6,7 +6,7 @@ use crate::common::lock::PyRwLock; use super::pytype::PyTypeRef; use crate::function::FuncArgs; use crate::pyobject::{ - PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::slots::SlotDescriptor; use crate::vm::VirtualMachine; @@ -111,21 +111,29 @@ impl PyProperty { // Descriptor methods - #[pymethod(name = "__set__")] - fn set(&self, obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { - if let Some(ref setter) = self.setter.read().as_ref() { - vm.invoke(setter, vec![obj, value]) - } else { - Err(vm.new_attribute_error("can't set attribute".to_owned())) - } - } - - #[pymethod(name = "__delete__")] - fn delete(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { - if let Some(ref deleter) = self.deleter.read().as_ref() { - vm.invoke(deleter, (obj,)) - } else { - Err(vm.new_attribute_error("can't delete attribute".to_owned())) + #[pyslot] + fn descr_set( + zelf: PyObjectRef, + obj: PyObjectRef, + value: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + let zelf = PyRef::::try_from_object(vm, zelf)?; + match value { + Some(value) => { + if let Some(ref setter) = zelf.setter.read().as_ref() { + vm.invoke(setter, vec![obj, value]).map(drop) + } else { + Err(vm.new_attribute_error("can't set attribute".to_owned())) + } + } + None => { + if let Some(ref deleter) = zelf.deleter.read().as_ref() { + vm.invoke(deleter, (obj,)).map(drop) + } else { + Err(vm.new_attribute_error("can't delete attribute".to_owned())) + } + } } } diff --git a/vm/src/builtins/pytype.rs b/vm/src/builtins/pytype.rs index cc6565447a..34b2e92247 100644 --- a/vm/src/builtins/pytype.rs +++ b/vm/src/builtins/pytype.rs @@ -167,6 +167,16 @@ impl PyType { } as _; update_slot!(descr_get, func); } + "__set__" | "__delete__" => { + let func: slots::DescrSetFunc = |zelf, obj, value, vm| { + match value { + Some(val) => vm.call_method(&zelf, "__set__", (obj, val)), + None => vm.call_method(&zelf, "__delete__", (obj,)), + } + .map(drop) + } as _; + update_slot!(descr_set, func); + } "__hash__" => { let func: slots::HashFunc = |zelf, vm| { let magic = get_class_magic(&zelf, "__hash__"); @@ -347,9 +357,9 @@ impl PyType { vm: &VirtualMachine, ) -> PyResult<()> { if let Some(attr) = zelf.get_class_attr(attr_name.borrow_value()) { - if let Some(ref descriptor) = attr.get_class_attr("__set__") { - vm.invoke(descriptor, (attr, zelf, value))?; - return Ok(()); + let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load()); + if let Some(descriptor) = descr_set { + return descriptor(attr, zelf.into_object(), Some(value), vm); } } let attr_name = attr_name.borrow_value(); @@ -364,8 +374,9 @@ impl PyType { #[pymethod(magic)] fn delattr(zelf: PyRef, attr_name: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { if let Some(attr) = zelf.get_class_attr(attr_name.borrow_value()) { - if let Some(ref descriptor) = attr.get_class_attr("__delete__") { - return vm.invoke(descriptor, (attr, zelf)).map(|_| ()); + let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load()); + if let Some(set) = descr_set { + return set(attr, zelf.into_object(), None, vm); } } @@ -494,7 +505,7 @@ impl PyType { ) .map_err(|e| vm.new_type_error(e))?; - vm.ctx.add_tp_new_wrapper(&typ); + vm.ctx.add_slot_wrappers(&typ); // avoid deadlock let attributes = typ @@ -558,10 +569,11 @@ impl SlotGetattro for PyType { if let Some(ref attr) = mcl_attr { let attr_class = attr.class(); - if attr_class.has_attr("__set__") { - if let Some(ref descr_get) = - attr_class.mro_find_map(|cls| cls.slots.descr_get.load()) - { + if attr_class + .mro_find_map(|cls| cls.slots.descr_set.load()) + .is_some() + { + if let Some(descr_get) = attr_class.mro_find_map(|cls| cls.slots.descr_get.load()) { let mcl = PyLease::into_pyref(mcl).into_object(); return descr_get(attr.clone(), Some(zelf.into_object()), Some(mcl), vm); } @@ -644,19 +656,22 @@ fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) - let cls = obj.clone_class(); match find_base_dict_descr(&cls, vm) { Some(descr) => { - descr - .get_class_attr("__set__") - .map(|set| vm.invoke(&set, vec![descr, obj, value])) - .unwrap_or_else(|| { - Err(vm.new_type_error(format!( + let descr_set = descr + .class() + .mro_find_map(|cls| cls.slots.descr_set.load()) + .ok_or_else(|| { + vm.new_type_error(format!( "this __dict__ descriptor does not support '{}' objects", cls.name - ))) + )) })?; + descr_set(descr, obj, Some(value), vm) + } + None => { + object::object_set_dict(obj, PyDictRef::try_from_object(vm, value)?, vm)?; + Ok(()) } - None => object::object_set_dict(obj, PyDictRef::try_from_object(vm, value)?, vm)?, } - Ok(()) } /* diff --git a/vm/src/macros.rs b/vm/src/macros.rs index d4317dc17e..847aef3ba4 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -30,7 +30,7 @@ macro_rules! py_class { $($crate::py_class!(@extract_slots($ctx, &mut slots, $name, $value));)* let py_class = $ctx.new_class($class_name, $class_base, slots); $($crate::py_class!(@extract_attrs($ctx, &py_class, $name, $value));)* - $ctx.add_tp_new_wrapper(&py_class); + $ctx.add_slot_wrappers(&py_class); py_class } }; @@ -55,7 +55,7 @@ macro_rules! extend_class { $( $class.set_str_attr($name, $value); )* - $ctx.add_tp_new_wrapper(&$class); + $ctx.add_slot_wrappers(&$class); }; } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 8df2d812c7..06ad503d98 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -359,7 +359,7 @@ impl PyContext { PyObject::new(object::PyBaseObject, class, dict) } - pub fn add_tp_new_wrapper(&self, ty: &PyTypeRef) { + pub fn add_slot_wrappers(&self, ty: &PyTypeRef) { if !ty.attributes.read().contains_key("__new__") { let new_wrapper = self.new_bound_method(self.tp_new_wrapper.clone(), ty.clone().into_object()); @@ -1075,7 +1075,7 @@ pub trait PyClassImpl: PyClassDef { ); } Self::impl_extend_class(ctx, class); - ctx.add_tp_new_wrapper(&class); + ctx.add_slot_wrappers(&class); if let Some(doc) = Self::DOC { class.set_str_attr("__doc__", ctx.new_str(doc)); } diff --git a/vm/src/slots.rs b/vm/src/slots.rs index 1d9dadfe98..be0aa937d1 100644 --- a/vm/src/slots.rs +++ b/vm/src/slots.rs @@ -47,6 +47,8 @@ pub(crate) type GenericMethod = fn(&PyObjectRef, FuncArgs, &VirtualMachine) -> P pub(crate) type DelFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<()>; pub(crate) type DescrGetFunc = fn(PyObjectRef, Option, Option, &VirtualMachine) -> PyResult; +pub(crate) type DescrSetFunc = + fn(PyObjectRef, PyObjectRef, Option, &VirtualMachine) -> PyResult<()>; pub(crate) type HashFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult; pub(crate) type CmpFunc = fn( &PyObjectRef, @@ -67,6 +69,7 @@ pub struct PyTypeSlots { pub del: AtomicCell>, pub call: AtomicCell>, pub descr_get: AtomicCell>, + pub descr_set: AtomicCell>, pub hash: AtomicCell>, pub cmp: AtomicCell>, pub getattro: AtomicCell>, diff --git a/vm/src/vm.rs b/vm/src/vm.rs index e5594011bc..6743f48721 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -1259,7 +1259,10 @@ impl VirtualMachine { let descr_cls = descr.class(); let descr_get = descr_cls.mro_find_map(|cls| cls.slots.descr_get.load()); if let Some(descr_get) = descr_get { - if descr_cls.has_attr("__set__") { + if descr_cls + .mro_find_map(|cls| cls.slots.descr_set.load()) + .is_some() + { drop(descr_cls); let cls = PyLease::into_pyref(obj_cls).into_object(); return descr_get(descr, Some(obj), Some(cls), self).map(Some);