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