Reduce size of compiled code for class initialization
This commit is contained in:
parent
95636f1ba7
commit
c3b935f06c
|
@ -757,7 +757,7 @@ impl pyo3::class::impl_::PyClassImpl for MyClass {
|
|||
type BaseType = PyAny;
|
||||
type ThreadChecker = pyo3::class::impl_::ThreadCheckerStub<MyClass>;
|
||||
|
||||
fn for_each_method_def(visitor: impl FnMut(&pyo3::class::PyMethodDefType)) {
|
||||
fn for_each_method_def(visitor: &mut dyn FnMut(&pyo3::class::PyMethodDefType)) {
|
||||
use pyo3::class::impl_::*;
|
||||
let collector = PyClassImplCollector::<MyClass>::new();
|
||||
collector.py_methods().iter()
|
||||
|
|
|
@ -436,7 +436,7 @@ fn impl_class(
|
|||
type BaseType = #base;
|
||||
type ThreadChecker = #thread_checker;
|
||||
|
||||
fn for_each_method_def(visitor: impl FnMut(&pyo3::class::PyMethodDefType)) {
|
||||
fn for_each_method_def(visitor: &mut dyn FnMut(&pyo3::class::PyMethodDefType)) {
|
||||
use pyo3::class::impl_::*;
|
||||
let collector = PyClassImplCollector::<Self>::new();
|
||||
#iter_py_methods
|
||||
|
|
|
@ -65,7 +65,7 @@ pub trait PyClassImpl: Sized {
|
|||
/// can be accessed by multiple threads by `threading` module.
|
||||
type ThreadChecker: PyClassThreadChecker<Self>;
|
||||
|
||||
fn for_each_method_def(_visitor: impl FnMut(&PyMethodDefType)) {}
|
||||
fn for_each_method_def(_visitor: &mut dyn FnMut(&PyMethodDefType)) {}
|
||||
fn get_new() -> Option<ffi::newfunc> {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -206,13 +206,13 @@ where
|
|||
}
|
||||
|
||||
// normal methods
|
||||
let methods = py_class_method_defs::<T>();
|
||||
let methods = py_class_method_defs(&T::for_each_method_def);
|
||||
if !methods.is_empty() {
|
||||
slots.push(ffi::Py_tp_methods, into_raw(methods));
|
||||
}
|
||||
|
||||
// properties
|
||||
let props = py_class_properties::<T>();
|
||||
let props = py_class_properties(T::Dict::IS_DUMMY, &T::for_each_method_def);
|
||||
if !props.is_empty() {
|
||||
slots.push(ffi::Py_tp_getset, into_raw(props));
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ where
|
|||
name: get_type_name::<T>(module_name)?,
|
||||
basicsize: std::mem::size_of::<T::Layout>() as c_int,
|
||||
itemsize: 0,
|
||||
flags: py_class_flags::<T>(has_gc_methods),
|
||||
flags: py_class_flags(has_gc_methods, T::IS_GC, T::IS_BASETYPE),
|
||||
slots: slots.0.as_mut_ptr(),
|
||||
};
|
||||
|
||||
|
@ -299,22 +299,24 @@ fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {
|
|||
#[cfg(any(Py_LIMITED_API, Py_3_10))]
|
||||
fn tp_init_additional<T: PyClass>(_type_object: *mut ffi::PyTypeObject) {}
|
||||
|
||||
fn py_class_flags<T: PyClass + PyTypeInfo>(has_gc_methods: bool) -> c_uint {
|
||||
let mut flags = if has_gc_methods || T::IS_GC {
|
||||
fn py_class_flags(has_gc_methods: bool, is_gc: bool, is_basetype: bool) -> c_uint {
|
||||
let mut flags = if has_gc_methods || is_gc {
|
||||
ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC
|
||||
} else {
|
||||
ffi::Py_TPFLAGS_DEFAULT
|
||||
};
|
||||
if T::IS_BASETYPE {
|
||||
if is_basetype {
|
||||
flags |= ffi::Py_TPFLAGS_BASETYPE;
|
||||
}
|
||||
flags.try_into().unwrap()
|
||||
}
|
||||
|
||||
fn py_class_method_defs<T: PyClassImpl>() -> Vec<ffi::PyMethodDef> {
|
||||
fn py_class_method_defs(
|
||||
for_each_method_def: &dyn Fn(&mut dyn FnMut(&PyMethodDefType)),
|
||||
) -> Vec<ffi::PyMethodDef> {
|
||||
let mut defs = Vec::new();
|
||||
|
||||
T::for_each_method_def(|def| match def {
|
||||
for_each_method_def(&mut |def| match def {
|
||||
PyMethodDefType::Method(def)
|
||||
| PyMethodDefType::Class(def)
|
||||
| PyMethodDefType::Static(def) => {
|
||||
|
@ -381,10 +383,13 @@ const PY_GET_SET_DEF_INIT: ffi::PyGetSetDef = ffi::PyGetSetDef {
|
|||
};
|
||||
|
||||
#[allow(clippy::clippy::collapsible_if)] // for if cfg!
|
||||
fn py_class_properties<T: PyClass>() -> Vec<ffi::PyGetSetDef> {
|
||||
fn py_class_properties(
|
||||
is_dummy: bool,
|
||||
for_each_method_def: &dyn Fn(&mut dyn FnMut(&PyMethodDefType)),
|
||||
) -> Vec<ffi::PyGetSetDef> {
|
||||
let mut defs = std::collections::HashMap::new();
|
||||
|
||||
T::for_each_method_def(|def| match def {
|
||||
for_each_method_def(&mut |def| match def {
|
||||
PyMethodDefType::Getter(getter) => {
|
||||
getter.copy_to(defs.entry(getter.name).or_insert(PY_GET_SET_DEF_INIT));
|
||||
}
|
||||
|
@ -398,7 +403,7 @@ fn py_class_properties<T: PyClass>() -> Vec<ffi::PyGetSetDef> {
|
|||
|
||||
// PyPy doesn't automatically adds __dict__ getter / setter.
|
||||
// PyObject_GenericGetDict not in the limited API until Python 3.10.
|
||||
push_dict_getset::<T>(&mut props);
|
||||
push_dict_getset(&mut props, is_dummy);
|
||||
|
||||
if !props.is_empty() {
|
||||
props.push(unsafe { std::mem::zeroed() });
|
||||
|
@ -407,8 +412,8 @@ fn py_class_properties<T: PyClass>() -> Vec<ffi::PyGetSetDef> {
|
|||
}
|
||||
|
||||
#[cfg(not(any(PyPy, all(Py_LIMITED_API, not(Py_3_10)))))]
|
||||
fn push_dict_getset<T: PyClass>(props: &mut Vec<ffi::PyGetSetDef>) {
|
||||
if !T::Dict::IS_DUMMY {
|
||||
fn push_dict_getset(props: &mut Vec<ffi::PyGetSetDef>, is_dummy: bool) {
|
||||
if !is_dummy {
|
||||
props.push(ffi::PyGetSetDef {
|
||||
name: "__dict__\0".as_ptr() as *mut c_char,
|
||||
get: Some(ffi::PyObject_GenericGetDict),
|
||||
|
@ -420,4 +425,4 @@ fn push_dict_getset<T: PyClass>(props: &mut Vec<ffi::PyGetSetDef>) {
|
|||
}
|
||||
|
||||
#[cfg(any(PyPy, all(Py_LIMITED_API, not(Py_3_10))))]
|
||||
fn push_dict_getset<T: PyClass>(_: &mut Vec<ffi::PyGetSetDef>) {}
|
||||
fn push_dict_getset(_: &mut Vec<ffi::PyGetSetDef>, _is_dummy: bool) {}
|
||||
|
|
|
@ -104,6 +104,17 @@ impl LazyStaticType {
|
|||
})
|
||||
});
|
||||
|
||||
self.ensure_init(py, type_object, T::NAME, &T::for_each_method_def);
|
||||
type_object
|
||||
}
|
||||
|
||||
fn ensure_init(
|
||||
&self,
|
||||
py: Python,
|
||||
type_object: *mut ffi::PyTypeObject,
|
||||
name: &str,
|
||||
for_each_method_def: &dyn Fn(&mut dyn FnMut(&PyMethodDefType)),
|
||||
) {
|
||||
// We might want to fill the `tp_dict` with python instances of `T`
|
||||
// itself. In order to do so, we must first initialize the type object
|
||||
// with an empty `tp_dict`: now we can create instances of `T`.
|
||||
|
@ -117,7 +128,7 @@ impl LazyStaticType {
|
|||
|
||||
if self.tp_dict_filled.get(py).is_some() {
|
||||
// `tp_dict` is already filled: ok.
|
||||
return type_object;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -126,7 +137,7 @@ impl LazyStaticType {
|
|||
if threads.contains(&thread_id) {
|
||||
// Reentrant call: just return the type object, even if the
|
||||
// `tp_dict` is not filled yet.
|
||||
return type_object;
|
||||
return;
|
||||
}
|
||||
threads.push(thread_id);
|
||||
}
|
||||
|
@ -136,7 +147,7 @@ impl LazyStaticType {
|
|||
// means that another thread can continue the initialization in the
|
||||
// meantime: at worst, we'll just make a useless computation.
|
||||
let mut items = vec![];
|
||||
T::for_each_method_def(|def| {
|
||||
for_each_method_def(&mut |def| {
|
||||
if let PyMethodDefType::ClassAttribute(attr) = def {
|
||||
items.push((
|
||||
extract_cstr_or_leak_cstring(
|
||||
|
@ -162,10 +173,8 @@ impl LazyStaticType {
|
|||
|
||||
if let Err(err) = result {
|
||||
err.clone_ref(py).print(py);
|
||||
panic!("An error occured while initializing `{}.__dict__`", T::NAME);
|
||||
panic!("An error occured while initializing `{}.__dict__`", name);
|
||||
}
|
||||
|
||||
type_object
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue