pub trait FnPtr: Sized + Copy {
type Args;
type Return;
const CALL_CONV: CallConv;
#[track_caller]
fn to_addr(self) -> *const ();
#[track_caller]
unsafe fn call_unchecked(self, args: Self::Args) -> Self::Return;
}
pub unsafe trait SafeFnPtr: FnPtr {
#[track_caller]
#[inline]
fn call(self, args: Self::Args) -> Self::Return {
unsafe { self.call_unchecked(args) }
}
}
macro_rules! define_calling_conv {
($dol:tt =>
$(#[$attr:meta])*
pub enum $name:ident {
$(
$(#[$conv_attr:meta])*
$conv:ident($conv_name:tt)
),+
$(,)?
}
) => {
$(#[$attr])*
pub enum $name {
$(
$(#[$conv_attr])*
$conv,
)*
}
impl $name {
#[inline]
#[must_use]
pub const fn name(self) -> &'static str {
match self {
$( Self::$conv => $conv_name, )*
}
}
}
#[macro_export]
macro_rules! call_conv {
$(
($conv_name) => { $dol crate::func::CallConv::$conv };
)*
}
};
}
define_calling_conv!($ =>
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[non_exhaustive]
pub enum CallConv {
#[default]
Rust("Rust"),
C("C"),
CUnwind("C-unwind"),
System("system"),
SystemUnwind("system-unwind"),
Cdecl("cdecl"),
CdeclUnwind("cdecl-unwind"),
Stdcall("stdcall"),
StdcallUnwind("stdcall-unwind"),
Win64("win64"),
Win64Unwind("win64-unwind"),
Sysv64("sysv64"),
Sysv64Unwind("sysv64-unwind"),
Aapcs("aapcs"),
AapcsUnwind("aapcs-unwind"),
Fastcall("fastcall"),
FastcallUnwind("fastcall-unwind"),
Thiscall("thiscall"),
ThiscallUnwind("thiscall-unwind"),
}
);
impl CallConv {
#[inline]
#[must_use]
pub const fn can_unwind(self) -> bool {
match self {
CallConv::Rust
| CallConv::CUnwind
| CallConv::SystemUnwind
| CallConv::CdeclUnwind
| CallConv::StdcallUnwind
| CallConv::Win64Unwind
| CallConv::Sysv64Unwind
| CallConv::AapcsUnwind
| CallConv::FastcallUnwind
| CallConv::ThiscallUnwind => true,
CallConv::C
| CallConv::System
| CallConv::Cdecl
| CallConv::Stdcall
| CallConv::Win64
| CallConv::Sysv64
| CallConv::Aapcs
| CallConv::Fastcall
| CallConv::Thiscall => false,
}
}
}
macro_rules! create_fn_ptr {
($dol:tt => (
$(
(
$($name:ident),* $(,)?
)
),* $(,)?
)) => {
macro_rules! fn_ptr {
(
$dol ($dol call_conv:tt),*
) => {
$dol(
$(
// Handle safe functions.
impl<Ret $(, $name )*> FnPtr for extern $dol call_conv fn($(_: $name),*) -> Ret {
type Args = ($($name,)*);
type Return = Ret;
const CALL_CONV: CallConv = call_conv!($dol call_conv);
#[inline]
fn to_addr(self) -> *const () {
self as _
}
#[inline]
unsafe fn call_unchecked(self, args: Self::Args) -> Self::Return
{
#[allow(non_snake_case)]
let ($($name,)*) = args;
self($($name),*)
}
}
unsafe impl<Ret $(, $name )*> SafeFnPtr for extern $dol call_conv fn($(_: $name),*) -> Ret {}
impl<Ret $(, $name)*> FnPtr for unsafe extern $dol call_conv fn($(_: $name),*) -> Ret {
type Args = ($($name,)*);
type Return = Ret;
const CALL_CONV: CallConv = call_conv!($dol call_conv);
#[inline]
fn to_addr(self) -> *const () {
self as _
}
#[inline]
unsafe fn call_unchecked(self, args: Self::Args) -> Self::Return {
#[allow(non_snake_case)]
let ($($name,)*) = args;
unsafe { self($($name),*) }
}
}
)*)*
};
}
};
}
create_fn_ptr!($ => (
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9),
(T0, T1, T2, T3, T4, T5, T6, T7, T8),
(T0, T1, T2, T3, T4, T5, T6, T7),
(T0, T1, T2, T3, T4, T5, T6),
(T0, T1, T2, T3, T4, T5),
(T0, T1, T2, T3, T4),
(T0, T1, T2, T3),
(T0, T1, T2),
(T0, T1),
(T0),
(),
));
fn_ptr!("Rust", "C", "C-unwind", "system", "system-unwind");
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn_ptr!("cdecl", "cdecl-unwind");
#[cfg(target_arch = "x86_64")]
fn_ptr!("sysv64", "sysv64-unwind");
#[cfg(target_arch = "arm")]
fn_ptr!("aapcs", "aapcs-unwind");