use super::{
super::{abort, intrinsic::intercept},
ThrowByPointer,
};
pub(crate) struct ActiveBackend;
unsafe impl ThrowByPointer for ActiveBackend {
type ExceptionHeader = Header;
fn new_header() -> Header {
Header {
reference_count: 0,
exception_type: core::ptr::null(),
exception_destructor: None,
caught: false,
rethrown: false,
adjusted_ptr: core::ptr::null_mut(),
padding: core::ptr::null(),
}
}
#[inline]
unsafe fn throw(ex: *mut Header) -> ! {
let end_of_header = unsafe { ex.add(1) }.cast();
unsafe {
__cxa_throw(end_of_header, &raw const TYPE_INFO, cleanup);
}
}
#[inline(always)]
fn intercept<Func: FnOnce() -> R, R>(func: Func) -> Result<R, *mut Header> {
let ptr = match intercept(func, |ex| {
#[expect(
clippy::cast_ptr_alignment,
reason = "guaranteed to be aligned by rustc"
)]
unsafe {
(*ex.cast::<CatchData>()).ptr
}
}) {
Ok(result) => return Ok(result),
Err(ptr) => ptr,
};
let adjusted_ptr = unsafe { __cxa_begin_catch(ptr) };
let ex: *mut Header = unsafe { adjusted_ptr.cast::<Header>().sub(1) };
if unsafe { (*ex).exception_type } != &raw const TYPE_INFO {
__cxa_rethrow();
}
unsafe {
(*ex).reference_count = 2;
}
__cxa_end_catch();
Err(ex)
}
}
#[repr(C)]
pub(crate) struct Header {
reference_count: usize,
exception_type: *const TypeInfo,
exception_destructor: Option<unsafe fn(*mut ()) -> *mut ()>,
caught: bool,
rethrown: bool,
adjusted_ptr: *mut (),
padding: *const (),
}
#[repr(C)]
struct TypeInfo {
vtable: *const usize,
name: *const i8,
}
unsafe impl Sync for TypeInfo {}
#[repr(C)]
struct CatchData {
ptr: *mut (),
is_rust_panic: bool,
}
unsafe extern "C" {
#[link_name = "\x01_ZTVN10__cxxabiv117__class_type_infoE"]
static CLASS_TYPE_INFO_VTABLE: [u8; 0];
}
static TYPE_INFO: TypeInfo = TypeInfo {
vtable: (&raw const CLASS_TYPE_INFO_VTABLE)
.cast::<usize>()
.wrapping_add(2),
name: c"lithium_exception".as_ptr(),
};
unsafe extern "C-unwind" {
fn __cxa_begin_catch(thrown_exception: *mut ()) -> *mut ();
safe fn __cxa_rethrow() -> !;
safe fn __cxa_end_catch();
fn __cxa_throw(
thrown_object: *mut (),
tinfo: *const TypeInfo,
destructor: unsafe extern "C" fn(*mut ()) -> *mut (),
) -> !;
}
unsafe extern "C" fn cleanup(_ex: *mut ()) -> *mut () {
abort("A Lithium exception was caught by a non-Lithium catch mechanism. This is undefined behavior. The process will now terminate.\n");
}