objc2_exception_helper/lib.rs
1//! External helper methods for catching Objective-C exceptions.
2//!
3//! This exists as a separate crate to avoid having to compile a `build.rs`
4//! script in `objc2` in most cases, and to properly version the compiled
5//! binary with [the `links` Cargo manifest key][cargo-links].
6//!
7//! You should not need to use this crate directly.
8//!
9//! [cargo-links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
10#![no_std]
11#![doc(html_root_url = "https://docs.rs/objc2-exception-helper/0.1.1")]
12
13// Forwards-compatibility
14#[cfg(feature = "alloc")]
15extern crate alloc;
16#[cfg(feature = "std")]
17extern crate std;
18
19use core::ffi::c_void;
20
21type TryCatchClosure = extern "C-unwind" fn(*mut c_void);
22
23// `try_catch` is `extern "C-unwind"`, since it does not use `@catch (...)`,
24// but instead let unhandled exceptions pass through.
25extern "C-unwind" {
26 /// Call the given function inside an Objective-C `@try/@catch` block.
27 ///
28 /// Defined in `src/try_catch.m` and compiled in `build.rs`.
29 ///
30 /// Alternatively, we could manually write assembly for this function like
31 /// [`objrs` does][manual-asm] does, that would cut down on a build stage
32 /// (and would probably give us a bit better performance), but it gets
33 /// unwieldy _very_ quickly, so I chose the much more stable option.
34 ///
35 /// Another thing to remember: While Rust's and Objective-C's unwinding
36 /// mechanisms are similar now, Rust's is explicitly unspecified, and they
37 /// may diverge significantly in the future; so handling this in pure Rust
38 /// (using mechanisms like core::intrinsics::r#try) is not an option!
39 ///
40 /// [manual-asm]: https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/src/exception.rs
41 ///
42 ///
43 /// # Panics
44 ///
45 /// This panics / continues unwinding if the unwind is not triggered by an
46 /// Objective-C exception (i.e. it was triggered by Rust/C++/...).
47 #[link_name = "objc2_exception_helper_0_1_try_catch"]
48 pub fn try_catch(f: TryCatchClosure, context: *mut c_void, error: *mut *mut c_void) -> u8;
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use core::ptr;
55
56 #[link(name = "objc", kind = "dylib")]
57 extern "C" {}
58
59 #[test]
60 fn context_is_passed_through_correctly() {
61 struct SyncPtr(*mut c_void);
62 unsafe impl Sync for SyncPtr {}
63
64 static VALUE: SyncPtr = SyncPtr(&VALUE.0 as *const *mut c_void as *mut c_void);
65
66 extern "C-unwind" fn check_value(value: *mut c_void) {
67 assert_eq!(VALUE.0, value);
68 }
69
70 let mut error: *mut c_void = ptr::null_mut();
71 let res = unsafe { try_catch(check_value, VALUE.0, &mut error) };
72 assert_eq!(res, 0);
73 assert!(error.is_null());
74 }
75}