objc_sys/
exception.rs

1//! Defined in:
2//! Apple: `objc-exception.h`
3//! GNUStep: `eh_personality.c`, which is a bit brittle to rely on, but I
4//!   think it's fine...
5
6// A few things here are defined differently depending on the __OBJC2__
7// variable, which is set for all platforms except 32-bit macOS.
8
9#[cfg(any(
10    doc,
11    all(
12        target_vendor = "apple",
13        not(all(target_os = "macos", target_arch = "x86"))
14    )
15))]
16use crate::objc_class;
17use crate::objc_object;
18
19/// Remember that this is non-null!
20#[cfg(any(
21    doc,
22    all(
23        target_vendor = "apple",
24        not(all(target_os = "macos", target_arch = "x86"))
25    )
26))]
27pub type objc_exception_matcher = unsafe extern "C" fn(
28    catch_type: *mut objc_class,
29    exception: *mut objc_object,
30) -> std::os::raw::c_int;
31
32/// Remember that this is non-null!
33#[cfg(any(
34    doc,
35    all(
36        target_vendor = "apple",
37        not(all(target_os = "macos", target_arch = "x86"))
38    )
39))]
40pub type objc_exception_preprocessor =
41    unsafe extern "C" fn(exception: *mut objc_object) -> *mut objc_object;
42
43/// Remember that this is non-null!
44#[cfg(any(
45    doc,
46    all(
47        target_vendor = "apple",
48        not(all(target_os = "macos", target_arch = "x86"))
49    )
50))]
51pub type objc_uncaught_exception_handler = unsafe extern "C" fn(exception: *mut objc_object);
52
53#[cfg(feature = "unstable-objfw")]
54pub type objc_uncaught_exception_handler =
55    Option<unsafe extern "C" fn(exception: *mut objc_object)>;
56
57/// Remember that this is non-null!
58#[cfg(any(
59    doc,
60    all(target_vendor = "apple", target_os = "macos", not(target_arch = "x86"))
61))]
62pub type objc_exception_handler =
63    unsafe extern "C" fn(unused: *mut objc_object, context: *mut core::ffi::c_void);
64
65#[cfg(all(feature = "unstable-exception", not(feature = "unstable-c-unwind")))]
66type TryCatchClosure = extern "C" fn(*mut core::ffi::c_void);
67#[cfg(all(feature = "unstable-exception", feature = "unstable-c-unwind"))]
68type TryCatchClosure = extern "C-unwind" fn(*mut core::ffi::c_void);
69
70extern_c_unwind! {
71    /// See [`objc-exception.h`].
72    ///
73    /// [`objc-exception.h`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/objc-exception.h
74    #[cold]
75    pub fn objc_exception_throw(exception: *mut objc_object) -> !;
76
77    #[cfg(all(target_vendor = "apple", not(feature = "gnustep-1-7"), not(all(target_os = "macos", target_arch = "x86"))))]
78    #[cold]
79    pub fn objc_exception_rethrow() -> !;
80
81    #[cfg(feature = "gnustep-1-7")]
82    #[cold]
83    pub fn objc_exception_rethrow(exc_buf: *mut core::ffi::c_void) -> !;
84}
85
86extern_c! {
87    #[cfg(any(doc, feature = "gnustep-1-7", all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
88    pub fn objc_begin_catch(exc_buf: *mut core::ffi::c_void) -> *mut objc_object;
89    #[cfg(any(doc, feature = "gnustep-1-7", all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
90    pub fn objc_end_catch();
91
92    #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos", target_arch = "x86")))]
93    pub fn objc_exception_try_enter(exception_data: *const core::ffi::c_void);
94
95    #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos", target_arch = "x86")))]
96    pub fn objc_exception_try_exit(exception_data: *const core::ffi::c_void);
97
98    // objc_exception_extract
99    // objc_exception_match
100    // objc_exception_get_functions
101    // objc_exception_set_functions
102
103    #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
104    pub fn objc_setExceptionMatcher(f: objc_exception_matcher) -> objc_exception_matcher;
105    #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
106    pub fn objc_setExceptionPreprocessor(
107        f: objc_exception_preprocessor,
108    ) -> objc_exception_preprocessor;
109    #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86"))), feature = "unstable-objfw"))]
110    pub fn objc_setUncaughtExceptionHandler(
111        f: objc_uncaught_exception_handler,
112    ) -> objc_uncaught_exception_handler;
113
114    #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos", not(target_arch = "x86"))))]
115    pub fn objc_addExceptionHandler(f: objc_exception_handler, context: *mut core::ffi::c_void) -> usize;
116    #[cfg(any(doc, all(target_vendor = "apple", target_os = "macos", not(target_arch = "x86"))))]
117    pub fn objc_removeExceptionHandler(token: usize);
118
119    // Only available when ENABLE_OBJCXX is set, and a useable C++ runtime is
120    // present when building libobjc2.
121    //
122    // #[cfg(any(doc, feature = "gnustep-1-7"))]
123    // pub fn objc_set_apple_compatible_objcxx_exceptions(newValue: std::os::raw::c_int) -> std::os::raw::c_int;
124
125    #[cold]
126    #[cfg(any(doc, all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "x86")))))]
127    pub fn objc_terminate() -> !;
128}
129
130extern "C" {
131    /// Call the given function inside an Objective-C `@try/@catch` block.
132    ///
133    /// Defined in `extern/exception.m` and compiled in `build.rs`.
134    ///
135    /// Alternatively, we could manually write assembly for this function like
136    /// [`objrs` does][manual-asm] does, that would cut down on a build stage
137    /// (and would probably give us a bit better performance), but it gets
138    /// unwieldy _very_ quickly, so I chose the much more stable option.
139    ///
140    /// Another thing to remember: While Rust's and Objective-C's unwinding
141    /// mechanisms are similar now, Rust's is explicitly unspecified, and they
142    /// may diverge significantly in the future; so handling this in pure Rust
143    /// (using mechanisms like core::intrinsics::r#try) is not an option!
144    ///
145    /// [manual-asm]: https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/src/exception.rs
146    #[cfg(feature = "unstable-exception")]
147    #[link_name = "rust_objc_sys_0_3_try_catch_exception"]
148    pub fn try_catch(
149        f: TryCatchClosure,
150        context: *mut core::ffi::c_void,
151        error: *mut *mut objc_object,
152    ) -> std::os::raw::c_uchar;
153}
154
155#[cfg(all(test, feature = "unstable-exception"))]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_try_catch_linkable() {
161        let fptr: unsafe extern "C" fn(_, _, _) -> _ = try_catch;
162        std::println!("{fptr:p}");
163    }
164}