Skip to content

Commit a19efa2

Browse files
authored
Merge pull request #3662 from Snowapril/object_protocol
Relocate `PyObject_Generic*` into object protocol
2 parents 94d3777 + df666dd commit a19efa2

File tree

9 files changed

+120
-124
lines changed

9 files changed

+120
-124
lines changed

vm/src/builtins/genericalias.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ impl GetAttr for PyGenericAlias {
369369
fn getattro(zelf: PyRef<Self>, attr: PyStrRef, vm: &VirtualMachine) -> PyResult {
370370
for exc in &ATTR_EXCEPTIONS {
371371
if *(*exc) == attr.to_string() {
372-
return vm.generic_getattribute(zelf.as_object().to_owned(), attr);
372+
return zelf.as_object().generic_getattr(attr, vm);
373373
}
374374
}
375375
zelf.origin().get_attr(attr, vm)

vm/src/builtins/module.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ impl PyModule {
4949
}
5050

5151
fn getattr_inner(zelf: &Py<Self>, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
52-
if let Some(attr) =
53-
vm.generic_getattribute_opt(zelf.to_owned().into(), name.clone(), None)?
52+
if let Some(attr) = zelf
53+
.as_object()
54+
.generic_getattr_opt(name.clone(), None, vm)?
5455
{
5556
return Ok(attr);
5657
}
@@ -66,7 +67,8 @@ impl PyModule {
6667
}
6768

6869
fn name(zelf: PyRef<Self>, vm: &VirtualMachine) -> Option<PyStrRef> {
69-
vm.generic_getattribute_opt(zelf.into(), PyStr::from("__name__").into_ref(vm), None)
70+
zelf.as_object()
71+
.generic_getattr_opt(PyStr::from("__name__").into_ref(vm), None, vm)
7072
.unwrap_or(None)
7173
.and_then(|obj| obj.downcast::<PyStr>().ok())
7274
}

vm/src/builtins/object.rs

+4-50
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,13 @@ impl PyBaseObject {
162162
value: PyObjectRef,
163163
vm: &VirtualMachine,
164164
) -> PyResult<()> {
165-
generic_setattr(&obj, name, Some(value), vm)
165+
obj.generic_setattr(name, Some(value), vm)
166166
}
167167

168168
/// Implement delattr(self, name).
169169
#[pymethod]
170170
fn __delattr__(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
171-
generic_setattr(&obj, name, None, vm)
171+
obj.generic_setattr(name, None, vm)
172172
}
173173

174174
#[pyslot]
@@ -178,7 +178,7 @@ impl PyBaseObject {
178178
value: Option<PyObjectRef>,
179179
vm: &VirtualMachine,
180180
) -> PyResult<()> {
181-
generic_setattr(&*obj, attr_name, value, vm)
181+
obj.generic_setattr(attr_name, value, vm)
182182
}
183183

184184
/// Return str(self).
@@ -288,7 +288,7 @@ impl PyBaseObject {
288288
#[pyslot]
289289
pub(crate) fn getattro(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
290290
vm_trace!("object.__getattribute__({:?}, {:?})", obj, name);
291-
vm.generic_getattribute(obj, name)
291+
obj.as_object().generic_getattr(name, vm)
292292
}
293293

294294
#[pymethod(magic)]
@@ -330,52 +330,6 @@ pub fn object_set_dict(obj: PyObjectRef, dict: PyDictRef, vm: &VirtualMachine) -
330330
.map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned()))
331331
}
332332

333-
pub fn generic_getattr(obj: PyObjectRef, attr_name: PyStrRef, vm: &VirtualMachine) -> PyResult {
334-
vm.generic_getattribute(obj, attr_name)
335-
}
336-
337-
#[cfg_attr(feature = "flame-it", flame)]
338-
pub fn generic_setattr(
339-
obj: &PyObject,
340-
attr_name: PyStrRef,
341-
value: Option<PyObjectRef>,
342-
vm: &VirtualMachine,
343-
) -> PyResult<()> {
344-
vm_trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value);
345-
346-
if let Some(attr) = obj.get_class_attr(attr_name.as_str()) {
347-
let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load());
348-
if let Some(descriptor) = descr_set {
349-
return descriptor(attr, obj.to_owned(), value, vm);
350-
}
351-
}
352-
353-
if let Some(dict) = obj.dict() {
354-
if let Some(value) = value {
355-
dict.set_item(attr_name, value, vm)?;
356-
} else {
357-
dict.del_item(attr_name.clone(), vm).map_err(|e| {
358-
if e.fast_isinstance(&vm.ctx.exceptions.key_error) {
359-
vm.new_attribute_error(format!(
360-
"'{}' object has no attribute '{}'",
361-
obj.class().name(),
362-
attr_name,
363-
))
364-
} else {
365-
e
366-
}
367-
})?;
368-
}
369-
Ok(())
370-
} else {
371-
Err(vm.new_attribute_error(format!(
372-
"'{}' object has no attribute '{}'",
373-
obj.class().name(),
374-
attr_name,
375-
)))
376-
}
377-
}
378-
379333
pub fn init(ctx: &Context) {
380334
PyBaseObject::extend_class(ctx, &ctx.types.object_type);
381335
}

vm/src/builtins/super.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl PySuper {
126126

127127
impl GetAttr for PySuper {
128128
fn getattro(zelf: PyRef<Self>, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
129-
let skip = |zelf: PyRef<Self>, name| vm.generic_getattribute(zelf.into(), name);
129+
let skip = |zelf: PyRef<Self>, name| zelf.as_object().generic_getattr(name, vm);
130130
let (obj, start_type): (PyObjectRef, PyTypeRef) = match zelf.obj.clone() {
131131
Some(o) => o,
132132
None => return skip(zelf, name),

vm/src/builtins/union.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ impl GetAttr for PyUnion {
266266
fn getattro(zelf: PyRef<Self>, attr: PyStrRef, vm: &VirtualMachine) -> PyResult {
267267
for &exc in CLS_ATTRS {
268268
if *exc == attr.to_string() {
269-
return vm.generic_getattribute(zelf.as_object().to_owned(), attr);
269+
return zelf.as_object().generic_getattr(attr, vm);
270270
}
271271
}
272272
zelf.as_object().to_pyobject(vm).get_attr(attr, vm)

vm/src/protocol/object.rs

+105-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
44
use crate::{
55
builtins::{
6-
pystr::IntoPyStrRef, PyBytes, PyDict, PyGenericAlias, PyInt, PyStrRef, PyTupleRef,
7-
PyTypeRef,
6+
pystr::IntoPyStrRef, PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyStrRef,
7+
PyTupleRef, PyTypeRef,
88
},
99
bytesinner::ByteInnerNewOptions,
1010
common::{hash::PyHash, str::to_ascii},
@@ -118,8 +118,6 @@ impl PyObject {
118118
setattro(self, attr_name, attr_value, vm)
119119
}
120120

121-
// PyObject *PyObject_GenericGetAttr(PyObject *o, PyObject *name)
122-
123121
pub fn set_attr(
124122
&self,
125123
attr_name: impl IntoPyStrRef,
@@ -131,6 +129,109 @@ impl PyObject {
131129
}
132130

133131
// int PyObject_GenericSetAttr(PyObject *o, PyObject *name, PyObject *value)
132+
#[cfg_attr(feature = "flame-it", flame)]
133+
pub fn generic_setattr(
134+
&self,
135+
attr_name: PyStrRef,
136+
value: Option<PyObjectRef>,
137+
vm: &VirtualMachine,
138+
) -> PyResult<()> {
139+
vm_trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value);
140+
141+
if let Some(attr) = self.get_class_attr(attr_name.as_str()) {
142+
let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load());
143+
if let Some(descriptor) = descr_set {
144+
return descriptor(attr, self.to_owned(), value, vm);
145+
}
146+
}
147+
148+
if let Some(dict) = self.dict() {
149+
if let Some(value) = value {
150+
dict.set_item(attr_name, value, vm)?;
151+
} else {
152+
dict.del_item(attr_name.clone(), vm).map_err(|e| {
153+
if e.fast_isinstance(&vm.ctx.exceptions.key_error) {
154+
vm.new_attribute_error(format!(
155+
"'{}' object has no attribute '{}'",
156+
self.class().name(),
157+
attr_name,
158+
))
159+
} else {
160+
e
161+
}
162+
})?;
163+
}
164+
Ok(())
165+
} else {
166+
Err(vm.new_attribute_error(format!(
167+
"'{}' object has no attribute '{}'",
168+
self.class().name(),
169+
attr_name,
170+
)))
171+
}
172+
}
173+
174+
pub fn generic_getattr(&self, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
175+
self.generic_getattr_opt(name.clone(), None, vm)?
176+
.ok_or_else(|| vm.new_attribute_error(format!("{} has no attribute '{}'", self, name)))
177+
}
178+
179+
/// CPython _PyObject_GenericGetAttrWithDict
180+
pub fn generic_getattr_opt(
181+
&self,
182+
name_str: PyStrRef,
183+
dict: Option<PyDictRef>,
184+
vm: &VirtualMachine,
185+
) -> PyResult<Option<PyObjectRef>> {
186+
let name = name_str.as_str();
187+
let obj_cls = self.class();
188+
let cls_attr = match obj_cls.get_attr(name) {
189+
Some(descr) => {
190+
let descr_cls = descr.class();
191+
let descr_get = descr_cls.mro_find_map(|cls| cls.slots.descr_get.load());
192+
if let Some(descr_get) = descr_get {
193+
if descr_cls
194+
.mro_find_map(|cls| cls.slots.descr_set.load())
195+
.is_some()
196+
{
197+
drop(descr_cls);
198+
let cls = obj_cls.into_owned().into();
199+
return descr_get(descr, Some(self.to_pyobject(vm)), Some(cls), vm)
200+
.map(Some);
201+
}
202+
}
203+
drop(descr_cls);
204+
Some((descr, descr_get))
205+
}
206+
None => None,
207+
};
208+
209+
let dict = dict.or_else(|| self.dict());
210+
211+
let attr = if let Some(dict) = dict {
212+
dict.get_item_opt(name, vm)?
213+
} else {
214+
None
215+
};
216+
217+
if let Some(obj_attr) = attr {
218+
Ok(Some(obj_attr))
219+
} else if let Some((attr, descr_get)) = cls_attr {
220+
match descr_get {
221+
Some(descr_get) => {
222+
let cls = obj_cls.into_owned().into();
223+
descr_get(attr, Some(self.to_pyobject(vm)), Some(cls), vm).map(Some)
224+
}
225+
None => Ok(Some(attr)),
226+
}
227+
} else if let Some(getter) = obj_cls.get_attr("__getattr__") {
228+
drop(obj_cls);
229+
vm.invoke(&getter, (self.to_pyobject(vm), name_str))
230+
.map(Some)
231+
} else {
232+
Ok(None)
233+
}
234+
}
134235

135236
pub fn del_attr(&self, attr_name: impl IntoPyStrRef, vm: &VirtualMachine) -> PyResult<()> {
136237
let attr_name = attr_name.into_pystr_ref(vm);

vm/src/stdlib/thread.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,8 @@ pub(crate) mod _thread {
326326
if attr.as_str() == "__dict__" {
327327
Ok(ldict.into())
328328
} else {
329-
vm.generic_getattribute_opt(zelf.clone().into(), attr.clone(), Some(ldict))?
329+
zelf.as_object()
330+
.generic_getattr_opt(attr.clone(), Some(ldict), vm)?
330331
.ok_or_else(|| {
331332
vm.new_attribute_error(format!(
332333
"{} has no attribute '{}'",

vm/src/types/slot.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
pub use crate::builtins::object::{generic_getattr, generic_setattr};
21
use crate::common::{hash::PyHash, lock::PyRwLock};
32
use crate::{
43
builtins::{PyInt, PyStrRef, PyType, PyTypeRef},

vm/src/vm/mod.rs

+1-62
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ mod vm_ops;
1717
use crate::{
1818
builtins::{
1919
code::{self, PyCode},
20-
object,
2120
pystr::IntoPyStrRef,
2221
tuple::{PyTuple, PyTupleTyped},
2322
PyBaseExceptionRef, PyDictRef, PyList, PyModule, PyStrRef, PyTypeRef,
@@ -594,66 +593,6 @@ impl VirtualMachine {
594593
Some(self.call_if_get_descriptor(method, obj))
595594
}
596595

597-
pub fn generic_getattribute(&self, obj: PyObjectRef, name: PyStrRef) -> PyResult {
598-
self.generic_getattribute_opt(obj.clone(), name.clone(), None)?
599-
.ok_or_else(|| self.new_attribute_error(format!("{} has no attribute '{}'", obj, name)))
600-
}
601-
602-
/// CPython _PyObject_GenericGetAttrWithDict
603-
pub fn generic_getattribute_opt(
604-
&self,
605-
obj: PyObjectRef,
606-
name_str: PyStrRef,
607-
dict: Option<PyDictRef>,
608-
) -> PyResult<Option<PyObjectRef>> {
609-
let name = name_str.as_str();
610-
let obj_cls = obj.class();
611-
let cls_attr = match obj_cls.get_attr(name) {
612-
Some(descr) => {
613-
let descr_cls = descr.class();
614-
let descr_get = descr_cls.mro_find_map(|cls| cls.slots.descr_get.load());
615-
if let Some(descr_get) = descr_get {
616-
if descr_cls
617-
.mro_find_map(|cls| cls.slots.descr_set.load())
618-
.is_some()
619-
{
620-
drop(descr_cls);
621-
let cls = obj_cls.into_owned().into();
622-
return descr_get(descr, Some(obj), Some(cls), self).map(Some);
623-
}
624-
}
625-
drop(descr_cls);
626-
Some((descr, descr_get))
627-
}
628-
None => None,
629-
};
630-
631-
let dict = dict.or_else(|| obj.dict());
632-
633-
let attr = if let Some(dict) = dict {
634-
dict.get_item_opt(name, self)?
635-
} else {
636-
None
637-
};
638-
639-
if let Some(obj_attr) = attr {
640-
Ok(Some(obj_attr))
641-
} else if let Some((attr, descr_get)) = cls_attr {
642-
match descr_get {
643-
Some(descr_get) => {
644-
let cls = obj_cls.into_owned().into();
645-
descr_get(attr, Some(obj), Some(cls), self).map(Some)
646-
}
647-
None => Ok(Some(attr)),
648-
}
649-
} else if let Some(getter) = obj_cls.get_attr("__getattr__") {
650-
drop(obj_cls);
651-
self.invoke(&getter, (obj, name_str)).map(Some)
652-
} else {
653-
Ok(None)
654-
}
655-
}
656-
657596
pub fn is_callable(&self, obj: &PyObject) -> bool {
658597
obj.class()
659598
.mro_find_map(|cls| cls.slots.call.load())
@@ -741,6 +680,6 @@ impl VirtualMachine {
741680
attr_value: impl Into<PyObjectRef>,
742681
) -> PyResult<()> {
743682
let val = attr_value.into();
744-
object::generic_setattr(module, attr_name.into_pystr_ref(self), Some(val), self)
683+
module.generic_setattr(attr_name.into_pystr_ref(self), Some(val), self)
745684
}
746685
}

0 commit comments

Comments
 (0)