unwinding/
panicking.rs

1use core::mem::ManuallyDrop;
2
3use crate::abi::*;
4
5pub unsafe trait Exception {
6    const CLASS: [u8; 8];
7
8    fn wrap(this: Self) -> *mut UnwindException;
9    unsafe fn unwrap(ex: *mut UnwindException) -> Self;
10}
11
12pub fn begin_panic<E: Exception>(exception: E) -> UnwindReasonCode {
13    unsafe extern "C" fn exception_cleanup<E: Exception>(
14        _unwind_code: UnwindReasonCode,
15        exception: *mut UnwindException,
16    ) {
17        unsafe { E::unwrap(exception) };
18    }
19
20    let ex = E::wrap(exception);
21    unsafe {
22        (*ex).exception_class = u64::from_ne_bytes(E::CLASS);
23        (*ex).exception_cleanup = Some(exception_cleanup::<E>);
24        _Unwind_RaiseException(ex)
25    }
26}
27
28pub fn catch_unwind<E: Exception, R, F: FnOnce() -> R>(f: F) -> Result<R, Option<E>> {
29    #[repr(C)]
30    union Data<F, R, E> {
31        f: ManuallyDrop<F>,
32        r: ManuallyDrop<R>,
33        p: ManuallyDrop<Option<E>>,
34    }
35
36    let mut data = Data {
37        f: ManuallyDrop::new(f),
38    };
39
40    let data_ptr = &mut data as *mut _ as *mut u8;
41    unsafe {
42        return if core::intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<E>) == 0 {
43            Ok(ManuallyDrop::into_inner(data.r))
44        } else {
45            Err(ManuallyDrop::into_inner(data.p))
46        };
47    }
48
49    #[inline]
50    fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
51        unsafe {
52            let data = &mut *(data as *mut Data<F, R, ()>);
53            let f = ManuallyDrop::take(&mut data.f);
54            data.r = ManuallyDrop::new(f());
55        }
56    }
57
58    #[cold]
59    fn do_catch<E: Exception>(data: *mut u8, exception: *mut u8) {
60        unsafe {
61            let data = &mut *(data as *mut ManuallyDrop<Option<E>>);
62            let exception = exception as *mut UnwindException;
63            if (*exception).exception_class != u64::from_ne_bytes(E::CLASS) {
64                _Unwind_DeleteException(exception);
65                *data = ManuallyDrop::new(None);
66                return;
67            }
68            *data = ManuallyDrop::new(Some(E::unwrap(exception)));
69        }
70    }
71}