1 | use crate::{ |
2 | ffi, |
3 | types::{PyType, PyTypeMethods}, |
4 | Borrowed, Bound, |
5 | }; |
6 | use std::os::raw::c_int; |
7 | |
8 | impl Bound<'_, PyType> { |
9 | #[inline ] |
10 | pub(crate) fn get_slot<const S: c_int>(&self, slot: Slot<S>) -> <Slot<S> as GetSlotImpl>::Type |
11 | where |
12 | Slot<S>: GetSlotImpl, |
13 | { |
14 | // SAFETY: `self` is a valid type object. |
15 | unsafe { |
16 | slot.get_slot( |
17 | self.as_type_ptr(), |
18 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
19 | is_runtime_3_10(self.py()), |
20 | ) |
21 | } |
22 | } |
23 | } |
24 | |
25 | impl Borrowed<'_, '_, PyType> { |
26 | #[inline ] |
27 | pub(crate) fn get_slot<const S: c_int>(self, slot: Slot<S>) -> <Slot<S> as GetSlotImpl>::Type |
28 | where |
29 | Slot<S>: GetSlotImpl, |
30 | { |
31 | // SAFETY: `self` is a valid type object. |
32 | unsafe { |
33 | slot.get_slot( |
34 | self.as_type_ptr(), |
35 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
36 | is_runtime_3_10(self.py()), |
37 | ) |
38 | } |
39 | } |
40 | } |
41 | |
42 | /// Gets a slot from a raw FFI pointer. |
43 | /// |
44 | /// Safety: |
45 | /// - `ty` must be a valid non-null pointer to a `PyTypeObject`. |
46 | /// - The Python runtime must be initialized |
47 | pub(crate) unsafe fn get_slot<const S: c_int>( |
48 | ty: *mut ffi::PyTypeObject, |
49 | slot: Slot<S>, |
50 | ) -> <Slot<S> as GetSlotImpl>::Type |
51 | where |
52 | Slot<S>: GetSlotImpl, |
53 | { |
54 | unsafe { |
55 | slot.get_slot( |
56 | ty, |
57 | // SAFETY: the Python runtime is initialized |
58 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
59 | is_runtime_3_10(crate::Python::assume_gil_acquired()), |
60 | ) |
61 | } |
62 | } |
63 | |
64 | pub(crate) trait GetSlotImpl { |
65 | type Type; |
66 | |
67 | /// Gets the requested slot from a type object. |
68 | /// |
69 | /// Safety: |
70 | /// - `ty` must be a valid non-null pointer to a `PyTypeObject`. |
71 | /// - `is_runtime_3_10` must be `false` if the runtime is not Python 3.10 or later. |
72 | unsafe fn get_slot( |
73 | self, |
74 | ty: *mut ffi::PyTypeObject, |
75 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] is_runtime_3_10: bool, |
76 | ) -> Self::Type; |
77 | } |
78 | |
79 | #[derive (Copy, Clone)] |
80 | pub(crate) struct Slot<const S: c_int>; |
81 | |
82 | macro_rules! impl_slots { |
83 | ($($name:ident: ($slot:ident, $field:ident) -> $tp:ty),+ $(,)?) => { |
84 | $( |
85 | pub (crate) const $name: Slot<{ ffi::$slot }> = Slot; |
86 | |
87 | impl GetSlotImpl for Slot<{ ffi::$slot }> { |
88 | type Type = $tp; |
89 | |
90 | #[inline] |
91 | unsafe fn get_slot( |
92 | self, |
93 | ty: *mut ffi::PyTypeObject, |
94 | #[cfg(all(Py_LIMITED_API, not(Py_3_10)))] is_runtime_3_10: bool |
95 | ) -> Self::Type { |
96 | #[cfg(not(Py_LIMITED_API))] |
97 | { |
98 | unsafe {(*ty).$field } |
99 | } |
100 | |
101 | #[cfg(Py_LIMITED_API)] |
102 | { |
103 | #[cfg(not(Py_3_10))] |
104 | { |
105 | // Calling PyType_GetSlot on static types is not valid before Python 3.10 |
106 | // ... so the workaround is to first do a runtime check for these versions |
107 | // (3.7, 3.8, 3.9) and then look in the type object anyway. This is only ok |
108 | // because we know that the interpreter is not going to change the size |
109 | // of the type objects for these historical versions. |
110 | if !is_runtime_3_10 && unsafe {ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE)} == 0 |
111 | { |
112 | return unsafe {(*ty.cast::<PyTypeObject39Snapshot>()).$field}; |
113 | } |
114 | } |
115 | |
116 | // SAFETY: slot type is set carefully to be valid |
117 | unsafe {std::mem::transmute(ffi::PyType_GetSlot(ty, ffi::$slot))} |
118 | } |
119 | } |
120 | } |
121 | )* |
122 | }; |
123 | } |
124 | |
125 | // Slots are implemented on-demand as needed.) |
126 | impl_slots! { |
127 | TP_ALLOC: (Py_tp_alloc, tp_alloc) -> Option<ffi::allocfunc>, |
128 | TP_BASE: (Py_tp_base, tp_base) -> *mut ffi::PyTypeObject, |
129 | TP_CLEAR: (Py_tp_clear, tp_clear) -> Option<ffi::inquiry>, |
130 | TP_DESCR_GET: (Py_tp_descr_get, tp_descr_get) -> Option<ffi::descrgetfunc>, |
131 | TP_FREE: (Py_tp_free, tp_free) -> Option<ffi::freefunc>, |
132 | TP_TRAVERSE: (Py_tp_traverse, tp_traverse) -> Option<ffi::traverseproc>, |
133 | } |
134 | |
135 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
136 | fn is_runtime_3_10(py: crate::Python<'_>) -> bool { |
137 | use crate::sync::GILOnceCell; |
138 | |
139 | static IS_RUNTIME_3_10: GILOnceCell<bool> = GILOnceCell::new(); |
140 | *IS_RUNTIME_3_10.get_or_init(py, || py.version_info() >= (3, 10)) |
141 | } |
142 | |
143 | #[repr (C)] |
144 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
145 | pub struct PyNumberMethods39Snapshot { |
146 | pub nb_add: Option<ffi::binaryfunc>, |
147 | pub nb_subtract: Option<ffi::binaryfunc>, |
148 | pub nb_multiply: Option<ffi::binaryfunc>, |
149 | pub nb_remainder: Option<ffi::binaryfunc>, |
150 | pub nb_divmod: Option<ffi::binaryfunc>, |
151 | pub nb_power: Option<ffi::ternaryfunc>, |
152 | pub nb_negative: Option<ffi::unaryfunc>, |
153 | pub nb_positive: Option<ffi::unaryfunc>, |
154 | pub nb_absolute: Option<ffi::unaryfunc>, |
155 | pub nb_bool: Option<ffi::inquiry>, |
156 | pub nb_invert: Option<ffi::unaryfunc>, |
157 | pub nb_lshift: Option<ffi::binaryfunc>, |
158 | pub nb_rshift: Option<ffi::binaryfunc>, |
159 | pub nb_and: Option<ffi::binaryfunc>, |
160 | pub nb_xor: Option<ffi::binaryfunc>, |
161 | pub nb_or: Option<ffi::binaryfunc>, |
162 | pub nb_int: Option<ffi::unaryfunc>, |
163 | pub nb_reserved: *mut std::os::raw::c_void, |
164 | pub nb_float: Option<ffi::unaryfunc>, |
165 | pub nb_inplace_add: Option<ffi::binaryfunc>, |
166 | pub nb_inplace_subtract: Option<ffi::binaryfunc>, |
167 | pub nb_inplace_multiply: Option<ffi::binaryfunc>, |
168 | pub nb_inplace_remainder: Option<ffi::binaryfunc>, |
169 | pub nb_inplace_power: Option<ffi::ternaryfunc>, |
170 | pub nb_inplace_lshift: Option<ffi::binaryfunc>, |
171 | pub nb_inplace_rshift: Option<ffi::binaryfunc>, |
172 | pub nb_inplace_and: Option<ffi::binaryfunc>, |
173 | pub nb_inplace_xor: Option<ffi::binaryfunc>, |
174 | pub nb_inplace_or: Option<ffi::binaryfunc>, |
175 | pub nb_floor_divide: Option<ffi::binaryfunc>, |
176 | pub nb_true_divide: Option<ffi::binaryfunc>, |
177 | pub nb_inplace_floor_divide: Option<ffi::binaryfunc>, |
178 | pub nb_inplace_true_divide: Option<ffi::binaryfunc>, |
179 | pub nb_index: Option<ffi::unaryfunc>, |
180 | pub nb_matrix_multiply: Option<ffi::binaryfunc>, |
181 | pub nb_inplace_matrix_multiply: Option<ffi::binaryfunc>, |
182 | } |
183 | |
184 | #[repr (C)] |
185 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
186 | pub struct PySequenceMethods39Snapshot { |
187 | pub sq_length: Option<ffi::lenfunc>, |
188 | pub sq_concat: Option<ffi::binaryfunc>, |
189 | pub sq_repeat: Option<ffi::ssizeargfunc>, |
190 | pub sq_item: Option<ffi::ssizeargfunc>, |
191 | pub was_sq_slice: *mut std::os::raw::c_void, |
192 | pub sq_ass_item: Option<ffi::ssizeobjargproc>, |
193 | pub was_sq_ass_slice: *mut std::os::raw::c_void, |
194 | pub sq_contains: Option<ffi::objobjproc>, |
195 | pub sq_inplace_concat: Option<ffi::binaryfunc>, |
196 | pub sq_inplace_repeat: Option<ffi::ssizeargfunc>, |
197 | } |
198 | |
199 | #[repr (C)] |
200 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
201 | pub struct PyMappingMethods39Snapshot { |
202 | pub mp_length: Option<ffi::lenfunc>, |
203 | pub mp_subscript: Option<ffi::binaryfunc>, |
204 | pub mp_ass_subscript: Option<ffi::objobjargproc>, |
205 | } |
206 | |
207 | #[repr (C)] |
208 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
209 | pub struct PyAsyncMethods39Snapshot { |
210 | pub am_await: Option<ffi::unaryfunc>, |
211 | pub am_aiter: Option<ffi::unaryfunc>, |
212 | pub am_anext: Option<ffi::unaryfunc>, |
213 | } |
214 | |
215 | #[repr (C)] |
216 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
217 | pub struct PyBufferProcs39Snapshot { |
218 | // not available in limited api, but structure needs to have the right size |
219 | pub bf_getbuffer: *mut std::os::raw::c_void, |
220 | pub bf_releasebuffer: *mut std::os::raw::c_void, |
221 | } |
222 | |
223 | /// Snapshot of the structure of PyTypeObject for Python 3.7 through 3.9. |
224 | /// |
225 | /// This is used as a fallback for static types in abi3 when the Python version is less than 3.10; |
226 | /// this is a bit of a hack but there's no better option and the structure of the type object is |
227 | /// not going to change for those historical versions. |
228 | #[repr (C)] |
229 | #[cfg (all(Py_LIMITED_API, not(Py_3_10)))] |
230 | struct PyTypeObject39Snapshot { |
231 | pub ob_base: ffi::PyVarObject, |
232 | pub tp_name: *const std::os::raw::c_char, |
233 | pub tp_basicsize: ffi::Py_ssize_t, |
234 | pub tp_itemsize: ffi::Py_ssize_t, |
235 | pub tp_dealloc: Option<ffi::destructor>, |
236 | #[cfg (not(Py_3_8))] |
237 | pub tp_print: *mut std::os::raw::c_void, // stubbed out, not available in limited API |
238 | #[cfg (Py_3_8)] |
239 | pub tp_vectorcall_offset: ffi::Py_ssize_t, |
240 | pub tp_getattr: Option<ffi::getattrfunc>, |
241 | pub tp_setattr: Option<ffi::setattrfunc>, |
242 | pub tp_as_async: *mut PyAsyncMethods39Snapshot, |
243 | pub tp_repr: Option<ffi::reprfunc>, |
244 | pub tp_as_number: *mut PyNumberMethods39Snapshot, |
245 | pub tp_as_sequence: *mut PySequenceMethods39Snapshot, |
246 | pub tp_as_mapping: *mut PyMappingMethods39Snapshot, |
247 | pub tp_hash: Option<ffi::hashfunc>, |
248 | pub tp_call: Option<ffi::ternaryfunc>, |
249 | pub tp_str: Option<ffi::reprfunc>, |
250 | pub tp_getattro: Option<ffi::getattrofunc>, |
251 | pub tp_setattro: Option<ffi::setattrofunc>, |
252 | pub tp_as_buffer: *mut PyBufferProcs39Snapshot, |
253 | pub tp_flags: std::os::raw::c_ulong, |
254 | pub tp_doc: *const std::os::raw::c_char, |
255 | pub tp_traverse: Option<ffi::traverseproc>, |
256 | pub tp_clear: Option<ffi::inquiry>, |
257 | pub tp_richcompare: Option<ffi::richcmpfunc>, |
258 | pub tp_weaklistoffset: ffi::Py_ssize_t, |
259 | pub tp_iter: Option<ffi::getiterfunc>, |
260 | pub tp_iternext: Option<ffi::iternextfunc>, |
261 | pub tp_methods: *mut ffi::PyMethodDef, |
262 | pub tp_members: *mut ffi::PyMemberDef, |
263 | pub tp_getset: *mut ffi::PyGetSetDef, |
264 | pub tp_base: *mut ffi::PyTypeObject, |
265 | pub tp_dict: *mut ffi::PyObject, |
266 | pub tp_descr_get: Option<ffi::descrgetfunc>, |
267 | pub tp_descr_set: Option<ffi::descrsetfunc>, |
268 | pub tp_dictoffset: ffi::Py_ssize_t, |
269 | pub tp_init: Option<ffi::initproc>, |
270 | pub tp_alloc: Option<ffi::allocfunc>, |
271 | pub tp_new: Option<ffi::newfunc>, |
272 | pub tp_free: Option<ffi::freefunc>, |
273 | pub tp_is_gc: Option<ffi::inquiry>, |
274 | pub tp_bases: *mut ffi::PyObject, |
275 | pub tp_mro: *mut ffi::PyObject, |
276 | pub tp_cache: *mut ffi::PyObject, |
277 | pub tp_subclasses: *mut ffi::PyObject, |
278 | pub tp_weaklist: *mut ffi::PyObject, |
279 | pub tp_del: Option<ffi::destructor>, |
280 | pub tp_version_tag: std::os::raw::c_uint, |
281 | pub tp_finalize: Option<ffi::destructor>, |
282 | #[cfg (Py_3_8)] |
283 | pub tp_vectorcall: Option<ffi::vectorcallfunc>, |
284 | } |
285 | |