wip descr_set

This commit is contained in:
Noah
2021-02-03 09:07:32 -06:00
parent 79fa1b104f
commit ac320f572d
8 changed files with 105 additions and 66 deletions

View File

@@ -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<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
let zelf = PyRef::<Self>::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
)))
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
let zelf = PyRef::<Self>::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()))
}
}
}
}

View File

@@ -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<Self>, 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(())
}
/*

View File

@@ -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);
};
}

View File

@@ -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));
}

View File

@@ -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<PyObjectRef>, Option<PyObjectRef>, &VirtualMachine) -> PyResult;
pub(crate) type DescrSetFunc =
fn(PyObjectRef, PyObjectRef, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>;
pub(crate) type HashFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<PyHash>;
pub(crate) type CmpFunc = fn(
&PyObjectRef,
@@ -67,6 +69,7 @@ pub struct PyTypeSlots {
pub del: AtomicCell<Option<DelFunc>>,
pub call: AtomicCell<Option<GenericMethod>>,
pub descr_get: AtomicCell<Option<DescrGetFunc>>,
pub descr_set: AtomicCell<Option<DescrSetFunc>>,
pub hash: AtomicCell<Option<HashFunc>>,
pub cmp: AtomicCell<Option<CmpFunc>>,
pub getattro: AtomicCell<Option<GetattroFunc>>,

View File

@@ -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);