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(exception: E) -> UnwindReasonCode { unsafe extern "C" fn exception_cleanup( _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::); _Unwind_RaiseException(ex) } } pub fn catch_unwind R>(f: F) -> Result> { #[repr(C)] union Data { f: ManuallyDrop, r: ManuallyDrop, p: ManuallyDrop>, } let mut data = Data { f: ManuallyDrop::new(f), }; let data_ptr = &mut data as *mut _ as *mut u8; unsafe { return if core::intrinsics::r#try(do_call::, data_ptr, do_catch::) == 0 { Ok(ManuallyDrop::into_inner(data.r)) } else { Err(ManuallyDrop::into_inner(data.p)) }; } #[inline] fn do_call R, R>(data: *mut u8) { unsafe { let data = &mut *(data as *mut Data); let f = ManuallyDrop::take(&mut data.f); data.r = ManuallyDrop::new(f()); } } #[cold] fn do_catch(data: *mut u8, exception: *mut u8) { unsafe { let data = &mut *(data as *mut ManuallyDrop>); 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))); } } }