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}