| use core::mem::ManuallyDrop; |
| |
| use crate::abi::*; |
| |
| pub unsafe trait Exception { |
| const CLASS: [u8; 8]; |
| |
| fn wrap(this: Self) -> *mut UnwindException; |
| unsafe fn unwrap(ex: *mut UnwindException) -> Self; |
| } |
| |
| pub fn begin_panic<E: Exception>(exception: E) -> UnwindReasonCode { |
| unsafe extern "C" fn exception_cleanup<E: Exception>( |
| _unwind_code: UnwindReasonCode, |
| exception: *mut UnwindException, |
| ) { |
| unsafe { E::unwrap(exception) }; |
| } |
| |
| let ex = E::wrap(exception); |
| unsafe { |
| (*ex).exception_class = u64::from_be_bytes(E::CLASS); |
| (*ex).exception_cleanup = Some(exception_cleanup::<E>); |
| _Unwind_RaiseException(ex) |
| } |
| } |
| |
| pub fn catch_unwind<E: Exception, R, F: FnOnce() -> R>(f: F) -> Result<R, Option<E>> { |
| #[repr(C)] |
| union Data<F, R, E> { |
| f: ManuallyDrop<F>, |
| r: ManuallyDrop<R>, |
| p: ManuallyDrop<Option<E>>, |
| } |
| |
| let mut data = Data { |
| f: ManuallyDrop::new(f), |
| }; |
| |
| let data_ptr = &mut data as *mut _ as *mut u8; |
| unsafe { |
| return if core::intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<E>) == 0 { |
| Ok(ManuallyDrop::into_inner(data.r)) |
| } else { |
| Err(ManuallyDrop::into_inner(data.p)) |
| }; |
| } |
| |
| #[inline] |
| fn do_call<F: FnOnce() -> R, R>(data: *mut u8) { |
| unsafe { |
| let data = &mut *(data as *mut Data<F, R, ()>); |
| let f = ManuallyDrop::take(&mut data.f); |
| data.r = ManuallyDrop::new(f()); |
| } |
| } |
| |
| #[cold] |
| fn do_catch<E: Exception>(data: *mut u8, exception: *mut u8) { |
| unsafe { |
| let data = &mut *(data as *mut ManuallyDrop<Option<E>>); |
| let exception = exception as *mut UnwindException; |
| if (*exception).exception_class != u64::from_be_bytes(E::CLASS) { |
| _Unwind_DeleteException(exception); |
| *data = ManuallyDrop::new(None); |
| return; |
| } |
| *data = ManuallyDrop::new(Some(E::unwrap(exception))); |
| } |
| } |
| } |