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, trace: Option, trace_argument: *mut core::ffi::c_void, ) -> 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_ne_bytes(E::CLASS); (*ex).exception_cleanup = Some(exception_cleanup::); _Unwind_RaiseException(ex, trace, trace_argument) } } 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::catch_unwind(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_ne_bytes(E::CLASS) { _Unwind_DeleteException(exception); *data = ManuallyDrop::new(None); return; } *data = ManuallyDrop::new(Some(E::unwrap(exception))); } } }