1use std::{
7 any::Any,
8 os::raw::c_int,
9 panic::{self, UnwindSafe},
10};
11
12use crate::internal::state::AttachGuard;
13use crate::{
14 ffi, ffi_ptr_ext::FfiPtrExt, impl_::callback::PyCallbackOutput, impl_::panic::PanicTrap,
15 impl_::pymethods::IPowModulo, panic::PanicException, types::PyModule, Bound, PyResult, Python,
16};
17
18#[inline]
19pub unsafe fn module_exec(
20 module: *mut ffi::PyObject,
21 f: for<'a, 'py> fn(&'a Bound<'py, PyModule>) -> PyResult<()>,
22) -> c_int {
23 unsafe {
24 trampoline(|py| {
25 let module = module.assume_borrowed_or_err(py)?.cast::<PyModule>()?;
26 f(&module)?;
27 Ok(0)
28 })
29 }
30}
31
32pub trait MethodDef<T> {
35 const METH: T;
36}
37
38#[macro_export]
46#[doc(hidden)]
47macro_rules! get_trampoline_function {
48 ($trampoline:ident, $f:path) => {{
49 struct Def;
50 impl $crate::impl_::trampoline::MethodDef<$crate::impl_::trampoline::$trampoline::Func> for Def {
51 const METH: $crate::impl_::trampoline::$trampoline::Func = $f;
52 }
53 $crate::impl_::trampoline::$trampoline::<Def>
54 }};
55}
56
57pub use get_trampoline_function;
58
59macro_rules! trampoline {
65 (pub fn $name:ident($($arg_names:ident: $arg_types:ty),* $(,)?) -> $ret:ty;) => {
66 pub unsafe extern "C" fn $name<Meth: MethodDef<$name::Func>>(
70 $($arg_names: $arg_types,)*
71 ) -> $ret {
72 unsafe { $name::inner($($arg_names),*, Meth::METH) }
73 }
74
75 pub mod $name {
77 use super::*;
78
79 #[inline]
81 pub(crate) unsafe fn inner($($arg_names: $arg_types),*, f: $name::Func) -> $ret {
82 unsafe { trampoline(|py| f(py, $($arg_names,)*)) }
83 }
84
85 pub type Func = for<'py> unsafe fn (Python<'py>, $($arg_types),*) -> PyResult<$ret>;
87 }
88 }
89}
90
91pub unsafe extern "C" fn noargs<Meth: MethodDef<noargs::Func>>(
93 slf: *mut ffi::PyObject,
94 _args: *mut ffi::PyObject, ) -> *mut ffi::PyObject {
96 unsafe { noargs::inner(slf, Meth::METH) }
97}
98
99pub mod noargs {
100 use super::*;
101
102 #[inline]
103 pub(crate) unsafe fn inner(slf: *mut ffi::PyObject, f: Func) -> *mut ffi::PyObject {
104 unsafe { trampoline(|py| f(py, slf)) }
105 }
106
107 pub type Func = unsafe fn(Python<'_>, *mut ffi::PyObject) -> PyResult<*mut ffi::PyObject>;
108}
109
110macro_rules! trampolines {
111 ($(pub fn $name:ident($($arg_names:ident: $arg_types:ty),* $(,)?) -> $ret:ty);* ;) => {
112 $(trampoline!(pub fn $name($($arg_names: $arg_types),*) -> $ret;));*;
113 }
114}
115
116trampolines!(
117 pub fn fastcall_cfunction_with_keywords(
118 slf: *mut ffi::PyObject,
119 args: *const *mut ffi::PyObject,
120 nargs: ffi::Py_ssize_t,
121 kwnames: *mut ffi::PyObject,
122 ) -> *mut ffi::PyObject;
123
124 pub fn cfunction_with_keywords(
125 slf: *mut ffi::PyObject,
126 args: *mut ffi::PyObject,
127 kwargs: *mut ffi::PyObject,
128 ) -> *mut ffi::PyObject;
129);
130
131trampolines!(
133 pub fn getattrofunc(slf: *mut ffi::PyObject, attr: *mut ffi::PyObject) -> *mut ffi::PyObject;
134
135 pub fn setattrofunc(
136 slf: *mut ffi::PyObject,
137 attr: *mut ffi::PyObject,
138 value: *mut ffi::PyObject,
139 ) -> c_int;
140
141 pub fn binaryfunc(slf: *mut ffi::PyObject, arg1: *mut ffi::PyObject) -> *mut ffi::PyObject;
142
143 pub fn descrgetfunc(
144 slf: *mut ffi::PyObject,
145 arg1: *mut ffi::PyObject,
146 arg2: *mut ffi::PyObject,
147 ) -> *mut ffi::PyObject;
148
149 pub fn getiterfunc(slf: *mut ffi::PyObject) -> *mut ffi::PyObject;
150
151 pub fn hashfunc(slf: *mut ffi::PyObject) -> ffi::Py_hash_t;
152
153 pub fn inquiry(slf: *mut ffi::PyObject) -> c_int;
154
155 pub fn iternextfunc(slf: *mut ffi::PyObject) -> *mut ffi::PyObject;
156
157 pub fn lenfunc(slf: *mut ffi::PyObject) -> ffi::Py_ssize_t;
158
159 pub fn newfunc(
160 subtype: *mut ffi::PyTypeObject,
161 args: *mut ffi::PyObject,
162 kwargs: *mut ffi::PyObject,
163 ) -> *mut ffi::PyObject;
164
165 pub fn initproc(
166 slf: *mut ffi::PyObject,
167 args: *mut ffi::PyObject,
168 kwargs: *mut ffi::PyObject,
169 ) -> c_int;
170
171 pub fn objobjproc(slf: *mut ffi::PyObject, arg1: *mut ffi::PyObject) -> c_int;
172
173 pub fn reprfunc(slf: *mut ffi::PyObject) -> *mut ffi::PyObject;
174
175 pub fn richcmpfunc(
176 slf: *mut ffi::PyObject,
177 other: *mut ffi::PyObject,
178 op: c_int,
179 ) -> *mut ffi::PyObject;
180
181 pub fn ssizeargfunc(arg1: *mut ffi::PyObject, arg2: ffi::Py_ssize_t) -> *mut ffi::PyObject;
182
183 pub fn ternaryfunc(
184 slf: *mut ffi::PyObject,
185 arg1: *mut ffi::PyObject,
186 arg2: *mut ffi::PyObject,
187 ) -> *mut ffi::PyObject;
188
189 pub fn unaryfunc(slf: *mut ffi::PyObject) -> *mut ffi::PyObject;
190);
191
192#[cfg(any(not(Py_LIMITED_API), Py_3_11))]
193trampoline! {
194 pub fn getbufferproc(slf: *mut ffi::PyObject, buf: *mut ffi::Py_buffer, flags: c_int) -> c_int;
195}
196
197#[cfg(any(not(Py_LIMITED_API), Py_3_11))]
200pub unsafe extern "C" fn releasebufferproc<Meth: MethodDef<releasebufferproc::Func>>(
201 slf: *mut ffi::PyObject,
202 buf: *mut ffi::Py_buffer,
203) {
204 unsafe { releasebufferproc::inner(slf, buf, Meth::METH) }
205}
206
207#[cfg(any(not(Py_LIMITED_API), Py_3_11))]
208pub mod releasebufferproc {
209 use super::*;
210
211 #[inline]
212 pub(crate) unsafe fn inner(slf: *mut ffi::PyObject, buf: *mut ffi::Py_buffer, f: Func) {
213 unsafe { trampoline_unraisable(|py| f(py, slf, buf), slf) }
214 }
215
216 pub type Func = unsafe fn(Python<'_>, *mut ffi::PyObject, *mut ffi::Py_buffer) -> PyResult<()>;
217}
218
219#[inline]
220pub(crate) unsafe fn dealloc(
221 slf: *mut ffi::PyObject,
222 f: for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject) -> (),
223) {
224 unsafe {
229 trampoline_unraisable(
230 |py| {
231 f(py, slf);
232 Ok(())
233 },
234 std::ptr::null_mut(),
235 )
236 }
237}
238
239trampoline!(
243 pub fn ipowfunc(
244 arg1: *mut ffi::PyObject,
245 arg2: *mut ffi::PyObject,
246 arg3: IPowModulo,
247 ) -> *mut ffi::PyObject;
248);
249
250#[inline]
257pub(crate) unsafe fn trampoline<F, R>(body: F) -> R
258where
259 F: for<'py> FnOnce(Python<'py>) -> PyResult<R> + UnwindSafe,
260 R: PyCallbackOutput,
261{
262 let trap = PanicTrap::new("uncaught panic at ffi boundary");
263
264 let guard = unsafe { AttachGuard::assume() };
266 let py = guard.python();
267 let out = panic_result_into_callback_output(
268 py,
269 panic::catch_unwind(move || -> PyResult<_> { body(py) }),
270 );
271 trap.disarm();
272 out
273}
274
275#[inline]
278fn panic_result_into_callback_output<R>(
279 py: Python<'_>,
280 panic_result: Result<PyResult<R>, Box<dyn Any + Send + 'static>>,
281) -> R
282where
283 R: PyCallbackOutput,
284{
285 let py_err = match panic_result {
286 Ok(Ok(value)) => return value,
287 Ok(Err(py_err)) => py_err,
288 Err(payload) => PanicException::from_panic_payload(payload),
289 };
290 py_err.restore(py);
291 R::ERR_VALUE
292}
293
294#[inline]
306unsafe fn trampoline_unraisable<F>(body: F, ctx: *mut ffi::PyObject)
307where
308 F: for<'py> FnOnce(Python<'py>) -> PyResult<()> + UnwindSafe,
309{
310 let trap = PanicTrap::new("uncaught panic at ffi boundary");
311
312 let guard = unsafe { AttachGuard::assume() };
314 let py = guard.python();
315
316 if let Err(py_err) = panic::catch_unwind(move || body(py))
317 .unwrap_or_else(|payload| Err(PanicException::from_panic_payload(payload)))
318 {
319 py_err.write_unraisable(py, unsafe { ctx.assume_borrowed_or_opt(py) }.as_deref());
320 }
321 trap.disarm();
322}