Skip to content

Commit 6f230ff

Browse files
committed
optimize descr_get
1 parent d59f9c3 commit 6f230ff

5 files changed

Lines changed: 83 additions & 51 deletions

File tree

derive/src/pyclass.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -342,20 +342,22 @@ where
342342
let into_func = quote_spanned! {ident.span() =>
343343
#transform(Self::#ident)
344344
};
345-
if slot_name == "call" {
346-
quote! {
347-
slots.#slot_ident.store(
348-
Some(
349-
|vm: &::rustpython_vm::VirtualMachine, args: ::rustpython_vm::function::PyFuncArgs| -> ::rustpython_vm::pyobject::PyResult {
350-
::rustpython_vm::function::IntoPyNativeFunc::call(&Self::#ident, vm, args)
351-
} as _
352-
)
353-
);
354-
}
355-
} else {
356-
quote! {
345+
match slot_name.as_str() {
346+
"call" => quote! {
347+
slots.#slot_ident.store(Some(
348+
|vm: &::rustpython_vm::VirtualMachine, args: ::rustpython_vm::function::PyFuncArgs| -> ::rustpython_vm::pyobject::PyResult {
349+
::rustpython_vm::function::IntoPyNativeFunc::call(&Self::#ident, vm, args)
350+
} as _
351+
));
352+
},
353+
"descr_get" => quote! {
354+
slots.#slot_ident.store(Some(
355+
Self::#ident as _
356+
))
357+
},
358+
_ => quote! {
357359
slots.#slot_ident = Some(#into_func);
358-
}
360+
},
359361
}
360362
};
361363

extra_tests/snippets/callables.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@ def __call__(self):
99
c = Callable()
1010
assert 1 == c()
1111
assert 2 == c()
12+
13+
class Inherited(Callable):
14+
pass
15+
16+
i = Inherited()
17+
18+
assert 1 == i()

vm/src/obj/objtype.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,24 +210,25 @@ impl PyClass {
210210
if let Some(attr) = mcl.get_attr(&name) {
211211
let attr_class = attr.lease_class();
212212
if attr_class.has_attr("__set__") {
213-
if let Some(ref descriptor) = attr_class.get_attr("__get__") {
214-
drop(attr_class);
213+
if let Some(ref descr_get) =
214+
PyLease::into_pyref(attr_class).first_in_mro(|cls| cls.slots.descr_get.load())
215+
{
215216
let mcl = PyLease::into_pyref(mcl).into_object();
216-
return vm.invoke(descriptor, vec![attr, zelf.into_object(), mcl]);
217+
return descr_get(
218+
vm,
219+
attr,
220+
Some(zelf.into_object()),
221+
OptionalArg::Present(mcl),
222+
);
217223
}
218224
}
219225
}
220226

221227
if let Some(attr) = zelf.get_attr(&name) {
222228
let attr_class = attr.class();
223-
let slots = &attr_class.slots;
224-
if let Some(ref descr_get) = slots.descr_get {
229+
if let Some(ref descr_get) = attr_class.first_in_mro(|cls| cls.slots.descr_get.load()) {
225230
drop(mcl);
226231
return descr_get(vm, attr, None, OptionalArg::Present(zelf.into_object()));
227-
} else if let Some(ref descriptor) = attr_class.get_attr("__get__") {
228-
drop(mcl);
229-
// TODO: is this nessessary?
230-
return vm.invoke(descriptor, vec![attr, vm.ctx.none(), zelf.into_object()]);
231232
}
232233
}
233234

@@ -706,11 +707,12 @@ pub fn new(
706707
if base.slots.flags.has_feature(PyTpFlags::HAS_DICT) {
707708
slots.flags |= PyTpFlags::HAS_DICT
708709
}
709-
for slot_name in ["__call__"].iter() {
710+
for slot_name in ["__call__", "__get__"].iter() {
710711
if attrs.contains_key(*slot_name) {
711712
slots.update_slot_func(*slot_name);
712713
}
713714
}
715+
714716
let new_type = PyRef::new_ref(
715717
PyClass {
716718
name: String::from(name),

vm/src/slots.rs

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::common::hash::PyHash;
55
use crate::function::{IntoPyNativeFunc, OptionalArg, PyFuncArgs, PyNativeFunc};
66
use crate::pyobject::{
77
IdProtocol, PyComparisonValue, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
8+
TypeProtocol,
89
};
910
use crate::VirtualMachine;
1011
use crossbeam_utils::atomic::AtomicCell;
@@ -40,18 +41,22 @@ impl Default for PyTpFlags {
4041
}
4142
}
4243

44+
type GenericFunc = fn(&VirtualMachine, PyFuncArgs) -> PyResult;
45+
type DescrGetFunc =
46+
fn(&VirtualMachine, PyObjectRef, Option<PyObjectRef>, OptionalArg<PyObjectRef>) -> PyResult;
47+
type HashFunc = Box<py_dyn_fn!(dyn Fn(PyObjectRef, &VirtualMachine) -> PyResult<PyHash>)>;
48+
4349
#[derive(Default)]
4450
pub struct PyClassSlots {
4551
pub flags: PyTpFlags,
4652
pub name: PyRwLock<Option<String>>, // tp_name, not class name
4753
pub new: Option<PyNativeFunc>,
48-
pub call: AtomicCell<Option<fn(&VirtualMachine, PyFuncArgs) -> PyResult>>,
49-
pub descr_get: Option<PyDescrGetFunc>,
54+
pub call: AtomicCell<Option<GenericFunc>>,
55+
pub descr_get: AtomicCell<Option<DescrGetFunc>>,
5056
pub hash: Option<HashFunc>,
5157
pub cmp: Option<CmpFunc>,
5258
}
5359

54-
type HashFunc = Box<py_dyn_fn!(dyn Fn(PyObjectRef, &VirtualMachine) -> PyResult<PyHash>)>;
5560
type CmpFunc = Box<
5661
py_dyn_fn!(
5762
dyn Fn(
@@ -75,10 +80,14 @@ impl PyClassSlots {
7580
pub(crate) fn update_slot_func(&self, name: &str) {
7681
match name {
7782
"__call__" => {
78-
self.call
79-
.store(Some(|vm: &VirtualMachine, args: PyFuncArgs| -> PyResult {
80-
IntoPyNativeFunc::call(&call_magic_call, vm, args)
81-
} as _))
83+
let func: GenericFunc =
84+
|vm, args| { IntoPyNativeFunc::call(&call_magic_call, vm, args) } as _;
85+
self.call.store(Some(func))
86+
}
87+
"__get__" => {
88+
let func: DescrGetFunc =
89+
|vm, zelf, obj, cls| { call_magic_descr_get(vm, zelf, obj, cls) } as _;
90+
self.descr_get.store(Some(func))
8291
}
8392
_ => (),
8493
}
@@ -98,17 +107,37 @@ pub trait SlotCall: PyValue {
98107
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult;
99108
}
100109

101-
fn call_magic_call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
102-
use crate::obj::objstr::PyString;
103-
use crate::pyobject::IntoPyRef;
110+
#[inline]
111+
fn get_class_magic(zelf: &PyObjectRef, name: &str) -> PyObjectRef {
112+
zelf.get_class_attr(name).unwrap()
113+
// let cls = zelf.lease_class();
114+
// let attrs = cls.attributes.read();
115+
// let attr = attrs.get(name);
116+
// attr.unwrap().clone()
117+
}
104118

105-
let magic_call = vm.generic_getattribute(zelf, PyString::from("__call__").into_pyref(vm))?;
106-
vm.invoke(&magic_call, args)
119+
fn call_magic_call(zelf: PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
120+
let magic = get_class_magic(&zelf, "__call__");
121+
let magic = vm.call_if_get_descriptor(magic, zelf.clone())?;
122+
args.insert(zelf);
123+
vm.invoke(&magic, args)
124+
}
107125

108-
// use crate::pyobject::TypeProtocol;
109-
// let magic_call = zelf.get_class_attr("__call__").unwrap();
110-
// args.insert( zelf);
111-
// vm.invoke(&magic_call, args)
126+
fn call_magic_descr_get(
127+
vm: &VirtualMachine,
128+
zelf: PyObjectRef,
129+
obj: Option<PyObjectRef>,
130+
cls: OptionalArg<PyObjectRef>,
131+
) -> PyResult {
132+
let magic = get_class_magic(&zelf, "__get__");
133+
vm.invoke(
134+
&magic,
135+
vec![
136+
zelf,
137+
vm.unwrap_or_none(obj),
138+
vm.unwrap_or_none(cls.into_option()),
139+
],
140+
)
112141
}
113142

114143
pub type PyDescrGetFunc = Box<

vm/src/vm.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -841,18 +841,10 @@ impl VirtualMachine {
841841
obj: Option<PyObjectRef>,
842842
cls: Option<PyObjectRef>,
843843
) -> Option<PyResult> {
844-
let descr_class = descr.class();
845-
let slots = &descr_class.slots;
846-
if let Some(descr_get) = slots.descr_get.as_ref() {
847-
Some(descr_get(self, descr, obj, OptionalArg::from_option(cls)))
848-
} else if let Some(ref descriptor) = descr_class.get_attr("__get__") {
849-
Some(self.invoke(
850-
descriptor,
851-
vec![descr, self.unwrap_or_none(obj), self.unwrap_or_none(cls)],
852-
))
853-
} else {
854-
None
855-
}
844+
descr
845+
.class()
846+
.first_in_mro(|cls| cls.slots.descr_get.load())
847+
.map(|descr_get| descr_get(self, descr, obj, OptionalArg::from_option(cls)))
856848
}
857849

858850
pub fn call_get_descriptor(&self, descr: PyObjectRef, obj: PyObjectRef) -> Option<PyResult> {

0 commit comments

Comments
 (0)