use std::ffi::c_void;
mod sealed {
pub trait Sealed {}
}
pub unsafe trait SymbolType: Copy + sealed::Sealed {
#[doc(hidden)]
fn from_address(addr: *mut c_void) -> Option<Self>;
#[doc(hidden)]
fn to_address(self) -> *mut c_void;
#[doc(hidden)]
const IS_FN: bool;
}
pub trait FnPtr: SymbolType {
type Args;
type Output;
unsafe fn call(self, args: Self::Args) -> Self::Output;
}
unsafe impl<T> SymbolType for *const T {
fn from_address(addr: *mut c_void) -> Option<Self> {
Some(addr.cast_const().cast())
}
fn to_address(self) -> *mut c_void {
self.cast_mut().cast()
}
const IS_FN: bool = false;
}
impl<T> sealed::Sealed for *const T {}
unsafe impl<T> SymbolType for *mut T {
fn from_address(addr: *mut c_void) -> Option<Self> {
Some(addr.cast())
}
fn to_address(self) -> *mut c_void {
self.cast()
}
const IS_FN: bool = false;
}
impl<T> sealed::Sealed for *mut T {}
macro_rules! impl_symbol_type_fn {
($($generics:ident),* ; $fn_ty:ty) => {
impl<R, $($generics),*> sealed::Sealed for $fn_ty {}
unsafe impl<R, $($generics),*> SymbolType for $fn_ty {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn from_address(addr: *mut c_void) -> Option<Self> {
if addr.is_null() {
return None;
}
Some(unsafe { std::mem::transmute::<*mut c_void, Self>(addr) })
}
fn to_address(self) -> *mut c_void {
self as *mut c_void
}
const IS_FN: bool = true;
}
};
}
macro_rules! impl_fn {
($abi:literal; $($ty:ident $val:ident),*) => {
impl_symbol_type_fn!($($ty),* ; unsafe extern $abi fn($($ty),*) -> R);
impl<R, $($ty),*> FnPtr for unsafe extern $abi fn($($ty),*) -> R {
type Args = ($($ty,)*);
type Output = R;
unsafe fn call(self, ($($val,)*): Self::Args) -> R {
unsafe { self($($val),*) }
}
}
};
}
macro_rules! impl_fn_all_abis {
($($ty:ident $val:ident),*) => {
impl_fn!("C"; $($ty $val),*);
impl_fn!("C-unwind"; $($ty $val),*);
impl_fn!("system"; $($ty $val),*);
impl_fn!("system-unwind"; $($ty $val),*);
#[cfg(target_arch = "x86")]
impl_fn!("stdcall"; $($ty $val),*);
#[cfg(target_arch = "x86")]
impl_fn!("stdcall-unwind"; $($ty $val),*);
#[cfg(target_arch = "x86")]
impl_fn!("fastcall"; $($ty $val),*);
#[cfg(target_arch = "x86")]
impl_fn!("fastcall-unwind"; $($ty $val),*);
#[cfg(target_arch = "x86")]
impl_fn!("thiscall"; $($ty $val),*);
#[cfg(target_arch = "x86")]
impl_fn!("thiscall-unwind"; $($ty $val),*);
};
}
impl_fn_all_abis!();
impl_fn_all_abis!(A a);
impl_fn_all_abis!(A a, B b);
impl_fn_all_abis!(A a, B b, C c);
impl_fn_all_abis!(A a, B b, C c, D d);
impl_fn_all_abis!(A a, B b, C c, D d, E e);
impl_fn_all_abis!(A a, B b, C c, D d, E e, F f);
impl_fn_all_abis!(A a, B b, C c, D d, E e, F f, G g);
impl_fn_all_abis!(A a, B b, C c, D d, E e, F f, G g, H h);
impl_fn_all_abis!(A a, B b, C c, D d, E e, F f, G g, H h, I i);
impl_fn_all_abis!(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j);
impl_fn_all_abis!(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k);
impl_fn_all_abis!(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
macro_rules! impl_variadic {
($($ty:ident),+) => {
impl_symbol_type_fn!($($ty),+ ; unsafe extern "C" fn($($ty),+, ...) -> R);
impl_symbol_type_fn!($($ty),+ ; unsafe extern "C-unwind" fn($($ty),+, ...) -> R);
};
}
impl_variadic!(A);
impl_variadic!(A, B);
impl_variadic!(A, B, C);
impl_variadic!(A, B, C, D);
impl_variadic!(A, B, C, D, E);
impl_variadic!(A, B, C, D, E, F);