1 | //! Trampolines for various pyfunction and pymethod implementations. |
2 | //! |
3 | //! They exist to monomorphise std::panic::catch_unwind once into PyO3, rather than inline in every |
4 | //! function, thus saving a huge amount of compile-time complexity. |
5 | |
6 | use std::{ |
7 | any::Any, |
8 | os::raw::c_int, |
9 | panic::{self, UnwindSafe}, |
10 | }; |
11 | |
12 | use crate::{ |
13 | callback::PyCallbackOutput, ffi, impl_::panic::PanicTrap, methods::IPowModulo, |
14 | panic::PanicException, types::PyModule, GILPool, Py, PyResult, Python, |
15 | }; |
16 | |
17 | #[inline ] |
18 | pub unsafe fn module_init( |
19 | f: for<'py> unsafe fn(Python<'py>) -> PyResult<Py<PyModule>>, |
20 | ) -> *mut ffi::PyObject { |
21 | trampoline(|py: Python<'_>| f(py).map(|module: Py| module.into_ptr())) |
22 | } |
23 | |
24 | #[inline ] |
25 | pub unsafe fn noargs( |
26 | slf: *mut ffi::PyObject, |
27 | args: *mut ffi::PyObject, |
28 | f: for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject) -> PyResult<*mut ffi::PyObject>, |
29 | ) -> *mut ffi::PyObject { |
30 | debug_assert!(args.is_null()); |
31 | trampoline(|py: Python<'_>| f(py, slf)) |
32 | } |
33 | |
34 | macro_rules! trampoline { |
35 | (pub fn $name:ident($($arg_names:ident: $arg_types:ty),* $(,)?) -> $ret:ty;) => { |
36 | #[inline] |
37 | pub unsafe fn $name( |
38 | $($arg_names: $arg_types,)* |
39 | f: for<'py> unsafe fn (Python<'py>, $($arg_types),*) -> PyResult<$ret>, |
40 | ) -> $ret { |
41 | trampoline(|py| f(py, $($arg_names,)*)) |
42 | } |
43 | } |
44 | } |
45 | |
46 | macro_rules! trampolines { |
47 | ($(pub fn $name:ident($($arg_names:ident: $arg_types:ty),* $(,)?) -> $ret:ty);* ;) => { |
48 | $(trampoline!(pub fn $name($($arg_names: $arg_types),*) -> $ret;));*; |
49 | } |
50 | } |
51 | |
52 | trampolines!( |
53 | pub fn fastcall_with_keywords( |
54 | slf: *mut ffi::PyObject, |
55 | args: *const *mut ffi::PyObject, |
56 | nargs: ffi::Py_ssize_t, |
57 | kwnames: *mut ffi::PyObject, |
58 | ) -> *mut ffi::PyObject; |
59 | |
60 | pub fn cfunction_with_keywords( |
61 | slf: *mut ffi::PyObject, |
62 | args: *mut ffi::PyObject, |
63 | kwargs: *mut ffi::PyObject, |
64 | ) -> *mut ffi::PyObject; |
65 | ); |
66 | |
67 | // Trampolines used by slot methods |
68 | trampolines!( |
69 | pub fn getattrofunc(slf: *mut ffi::PyObject, attr: *mut ffi::PyObject) -> *mut ffi::PyObject; |
70 | |
71 | pub fn setattrofunc( |
72 | slf: *mut ffi::PyObject, |
73 | attr: *mut ffi::PyObject, |
74 | value: *mut ffi::PyObject, |
75 | ) -> c_int; |
76 | |
77 | pub fn binaryfunc(slf: *mut ffi::PyObject, arg1: *mut ffi::PyObject) -> *mut ffi::PyObject; |
78 | |
79 | pub fn descrgetfunc( |
80 | slf: *mut ffi::PyObject, |
81 | arg1: *mut ffi::PyObject, |
82 | arg2: *mut ffi::PyObject, |
83 | ) -> *mut ffi::PyObject; |
84 | |
85 | pub fn getiterfunc(slf: *mut ffi::PyObject) -> *mut ffi::PyObject; |
86 | |
87 | pub fn hashfunc(slf: *mut ffi::PyObject) -> ffi::Py_hash_t; |
88 | |
89 | pub fn inquiry(slf: *mut ffi::PyObject) -> c_int; |
90 | |
91 | pub fn iternextfunc(slf: *mut ffi::PyObject) -> *mut ffi::PyObject; |
92 | |
93 | pub fn lenfunc(slf: *mut ffi::PyObject) -> ffi::Py_ssize_t; |
94 | |
95 | pub fn newfunc( |
96 | subtype: *mut ffi::PyTypeObject, |
97 | args: *mut ffi::PyObject, |
98 | kwargs: *mut ffi::PyObject, |
99 | ) -> *mut ffi::PyObject; |
100 | |
101 | pub fn objobjproc(slf: *mut ffi::PyObject, arg1: *mut ffi::PyObject) -> c_int; |
102 | |
103 | pub fn reprfunc(slf: *mut ffi::PyObject) -> *mut ffi::PyObject; |
104 | |
105 | pub fn richcmpfunc( |
106 | slf: *mut ffi::PyObject, |
107 | other: *mut ffi::PyObject, |
108 | op: c_int, |
109 | ) -> *mut ffi::PyObject; |
110 | |
111 | pub fn ssizeargfunc(arg1: *mut ffi::PyObject, arg2: ffi::Py_ssize_t) -> *mut ffi::PyObject; |
112 | |
113 | pub fn ternaryfunc( |
114 | slf: *mut ffi::PyObject, |
115 | arg1: *mut ffi::PyObject, |
116 | arg2: *mut ffi::PyObject, |
117 | ) -> *mut ffi::PyObject; |
118 | |
119 | pub fn unaryfunc(slf: *mut ffi::PyObject) -> *mut ffi::PyObject; |
120 | ); |
121 | |
122 | #[cfg (any(not(Py_LIMITED_API), Py_3_11))] |
123 | trampoline! { |
124 | pub fn getbufferproc(slf: *mut ffi::PyObject, buf: *mut ffi::Py_buffer, flags: c_int) -> c_int; |
125 | } |
126 | |
127 | #[cfg (any(not(Py_LIMITED_API), Py_3_11))] |
128 | #[inline ] |
129 | pub unsafe fn releasebufferproc( |
130 | slf: *mut ffi::PyObject, |
131 | buf: *mut ffi::Py_buffer, |
132 | f: for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject, *mut ffi::Py_buffer) -> PyResult<()>, |
133 | ) { |
134 | trampoline_unraisable(|py| f(py, slf, buf), ctx:slf) |
135 | } |
136 | |
137 | #[inline ] |
138 | pub(crate) unsafe fn dealloc( |
139 | slf: *mut ffi::PyObject, |
140 | f: for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject) -> (), |
141 | ) { |
142 | // After calling tp_dealloc the object is no longer valid, |
143 | // so pass null_mut() to the context. |
144 | // |
145 | // (Note that we don't allow the implementation `f` to fail.) |
146 | trampoline_unraisable( |
147 | |py| { |
148 | f(py, slf); |
149 | Ok(()) |
150 | }, |
151 | ctx:std::ptr::null_mut(), |
152 | ) |
153 | } |
154 | |
155 | // Ipowfunc is a unique case where PyO3 has its own type |
156 | // to workaround a problem on 3.7 (see IPowModulo type definition). |
157 | // Once 3.7 support dropped can just remove this. |
158 | trampoline!( |
159 | pub fn ipowfunc( |
160 | arg1: *mut ffi::PyObject, |
161 | arg2: *mut ffi::PyObject, |
162 | arg3: IPowModulo, |
163 | ) -> *mut ffi::PyObject; |
164 | ); |
165 | |
166 | /// Implementation of trampoline functions, which sets up a GILPool and calls F. |
167 | /// |
168 | /// Panics during execution are trapped so that they don't propagate through any |
169 | /// outer FFI boundary. |
170 | #[inline ] |
171 | pub(crate) fn trampoline<F, R>(body: F) -> R |
172 | where |
173 | F: for<'py> FnOnce(Python<'py>) -> PyResult<R> + UnwindSafe, |
174 | R: PyCallbackOutput, |
175 | { |
176 | let trap: PanicTrap = PanicTrap::new(msg:"uncaught panic at ffi boundary" ); |
177 | let pool: GILPool = unsafe { GILPool::new() }; |
178 | let py: Python<'_> = pool.python(); |
179 | let out: R = panic_result_into_callback_output( |
180 | py, |
181 | panic_result:panic::catch_unwind(move || -> PyResult<_> { body(py) }), |
182 | ); |
183 | trap.disarm(); |
184 | out |
185 | } |
186 | |
187 | /// Converts the output of std::panic::catch_unwind into a Python function output, either by raising a Python |
188 | /// exception or by unwrapping the contained success output. |
189 | #[inline ] |
190 | fn panic_result_into_callback_output<R>( |
191 | py: Python<'_>, |
192 | panic_result: Result<PyResult<R>, Box<dyn Any + Send + 'static>>, |
193 | ) -> R |
194 | where |
195 | R: PyCallbackOutput, |
196 | { |
197 | let py_err: PyErr = match panic_result { |
198 | Ok(Ok(value: R)) => return value, |
199 | Ok(Err(py_err: PyErr)) => py_err, |
200 | Err(payload: Box) => PanicException::from_panic_payload(payload), |
201 | }; |
202 | py_err.restore(py); |
203 | R::ERR_VALUE |
204 | } |
205 | |
206 | /// Implementation of trampoline for functions which can't return an error. |
207 | /// |
208 | /// Panics during execution are trapped so that they don't propagate through any |
209 | /// outer FFI boundary. |
210 | /// |
211 | /// Exceptions produced are sent to `sys.unraisablehook`. |
212 | /// |
213 | /// # Safety |
214 | /// |
215 | /// ctx must be either a valid ffi::PyObject or NULL |
216 | #[inline ] |
217 | unsafe fn trampoline_unraisable<F>(body: F, ctx: *mut ffi::PyObject) |
218 | where |
219 | F: for<'py> FnOnce(Python<'py>) -> PyResult<()> + UnwindSafe, |
220 | { |
221 | let trap: PanicTrap = PanicTrap::new(msg:"uncaught panic at ffi boundary" ); |
222 | let pool: GILPool = GILPool::new(); |
223 | let py: Python<'_> = pool.python(); |
224 | if let Err(py_err: PyErr) = panic::catch_unwind(move || body(py)) |
225 | .unwrap_or_else(|payload: Box| Err(PanicException::from_panic_payload(payload))) |
226 | { |
227 | py_err.write_unraisable(py, obj:py.from_borrowed_ptr_or_opt(ptr:ctx)); |
228 | } |
229 | trap.disarm(); |
230 | } |
231 | |