#[cfg(feature = "static-detour")]
#[cfg_attr(docsrs, doc(cfg(feature = "static-detour")))]
#[macro_export]
macro_rules! static_detour {
(@parse_attributes ($($input:tt)*) | #[$attribute:meta] $($rest:tt)*) => {
$crate::static_detour!(@parse_attributes ($($input)* $attribute) | $($rest)*);
};
(@parse_attributes ($($input:tt)*) | $($rest:tt)+) => {
$crate::static_detour!(@parse_access_modifier (($($input)*)) | $($rest)*);
};
(@parse_access_modifier ($($input:tt)*) | pub(in $vis:path) static $($rest:tt)*) => {
$crate::static_detour!(@parse_name ($($input)* (pub(in $vis))) | $($rest)*);
};
(@parse_access_modifier ($($input:tt)*) | pub($vis:tt) static $($rest:tt)*) => {
$crate::static_detour!(@parse_name ($($input)* (pub($vis))) | $($rest)*);
};
(@parse_access_modifier ($($input:tt)*) | pub static $($rest:tt)*) => {
$crate::static_detour!(@parse_name ($($input)* (pub)) | $($rest)*);
};
(@parse_access_modifier ($($input:tt)*) | static $($rest:tt)*) => {
$crate::static_detour!(@parse_name ($($input)* ()) | $($rest)*);
};
(@parse_name ($($input:tt)*) | $name:ident : $($rest:tt)*) => {
$crate::static_detour!(@parse_unsafe ($($input)* ($name)) | $($rest)*);
};
(@parse_unsafe ($($input:tt)*) | unsafe $($rest:tt)*) => {
$crate::static_detour!(@parse_calling_convention ($($input)*) (unsafe) | $($rest)*);
};
(@parse_unsafe ($($input:tt)*) | $($rest:tt)*) => {
$crate::static_detour!(@parse_calling_convention ($($input)*) () | $($rest)*);
};
(@parse_calling_convention
($($input:tt)*) ($($modifier:tt)*) | extern $cc:tt fn $($rest:tt)*) => {
$crate::static_detour!(@parse_prototype ($($input)* ($($modifier)* extern $cc)) | $($rest)*);
};
(@parse_calling_convention
($($input:tt)*) ($($modifier:tt)*) | extern fn $($rest:tt)*) => {
$crate::static_detour!(@parse_prototype ($($input)* ($($modifier)* extern)) | $($rest)*);
};
(@parse_calling_convention ($($input:tt)*) ($($modifier:tt)*) | fn $($rest:tt)*) => {
$crate::static_detour!(@parse_prototype ($($input)* ($($modifier)*)) | $($rest)*);
};
(@parse_prototype
($($input:tt)*) | ($($argument_type:ty),*) -> $return_type:ty ; $($rest:tt)*) => {
$crate::static_detour!(
@parse_terminator ($($input)* ($($argument_type)*) ($return_type)) | ; $($rest)*);
};
(@parse_prototype ($($input:tt)*) | ($($argument_type:ty),*) $($rest:tt)*) => {
$crate::static_detour!(@parse_terminator ($($input)* ($($argument_type)*) (())) | $($rest)*);
};
(@parse_terminator ($($input:tt)*) | ; $($rest:tt)*) => {
$crate::static_detour!(@parse_entries ($($input)*) | $($rest)*);
};
(@parse_entries ($($input:tt)*) | $($rest:tt)+) => {
$crate::static_detour!(@aggregate $($input)*);
$crate::static_detour!($($rest)*);
};
(@parse_entries ($($input:tt)*) | ) => {
$crate::static_detour!(@aggregate $($input)*);
};
(@aggregate ($($attribute:meta)*) ($($visibility:tt)*) ($name:ident)
($($modifier:tt)*) ($($argument_type:ty)*) ($return_type:ty)) => {
$crate::static_detour!(@argument_names (create_detour)(
($($attribute)*) ($($visibility)*) ($name)
($($modifier)*) ($($argument_type)*) ($return_type)
($($modifier)* fn ($($argument_type),*) -> $return_type)
)($($argument_type)*));
};
(@create_detour ($($argument_name:ident)*) ($($attribute:meta)*) ($($visibility:tt)*)
($name:ident) ($($modifier:tt)*) ($($argument_type:ty)*)
($return_type:ty) ($fn_type:ty)) => {
$crate::static_detour!(@generate
#[allow(non_upper_case_globals)]
$(#[$attribute])*
$($visibility)* static $name: $crate::StaticDetour<$fn_type> = {
#[inline(never)]
#[allow(unused_unsafe)]
$($modifier) * fn __ffi_detour(
$($argument_name: $argument_type),*) -> $return_type {
#[allow(unused_unsafe)]
($name.__detour())($($argument_name),*)
}
$crate::StaticDetour::__new(__ffi_detour)
};
);
};
(@argument_names ($label:ident) ($($input:tt)*) ($($token:tt)*)) => {
$crate::static_detour!(@argument_names ($label) ($($input)*)(
__arg_0 __arg_1 __arg_2 __arg_3 __arg_4 __arg_5 __arg_6
__arg_7 __arg_8 __arg_9 __arg_10 __arg_11 __arg_12 __arg_13
)($($token)*)());
};
(@argument_names
($label:ident)
($($input:tt)*)
($hd_name:tt $($tl_name:tt)*)
($hd:tt $($tl:tt)*) ($($acc:tt)*)) => {
$crate::static_detour!(
@argument_names ($label) ($($input)*) ($($tl_name)*) ($($tl)*) ($($acc)* $hd_name));
};
(@argument_names ($label:ident) ($($input:tt)*) ($($name:tt)*) () ($($acc:tt)*)) => {
$crate::static_detour!(@$label ($($acc)*) $($input)*);
};
(@generate $item:item) => { $item };
($($t:tt)+) => {
$crate::static_detour!(@parse_attributes () | $($t)+);
};
}
macro_rules! impl_hookable {
(@recurse () ($($nm:ident : $ty:ident),*)) => {
impl_hookable!(@impl_all ($($nm : $ty),*));
};
(@recurse
($hd_nm:ident : $hd_ty:ident $(, $tl_nm:ident : $tl_ty:ident)*)
($($nm:ident : $ty:ident),*)) => {
impl_hookable!(@impl_all ($($nm : $ty),*));
impl_hookable!(@recurse ($($tl_nm : $tl_ty),*) ($($nm : $ty,)* $hd_nm : $hd_ty));
};
(@impl_all ($($nm:ident : $ty:ident),*)) => {
impl_hookable!(@impl_pair ($($nm : $ty),*) ( fn($($ty),*) -> Ret));
impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "cdecl" fn($($ty),*) -> Ret));
impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "stdcall" fn($($ty),*) -> Ret));
impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "fastcall" fn($($ty),*) -> Ret));
impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "win64" fn($($ty),*) -> Ret));
impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "C" fn($($ty),*) -> Ret));
impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "system" fn($($ty),*) -> Ret));
#[cfg(feature = "thiscall-abi")]
#[cfg_attr(docsrs, doc(cfg(feature = "thiscall-abi")))]
impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "thiscall" fn($($ty),*) -> Ret));
};
(@impl_pair ($($nm:ident : $ty:ident),*) ($($fn_t:tt)*)) => {
impl_hookable!(@impl_fun ($($nm : $ty),*) ($($fn_t)*) (unsafe $($fn_t)*));
};
(@impl_fun ($($nm:ident : $ty:ident),*) ($safe_type:ty) ($unsafe_type:ty)) => {
impl_hookable!(@impl_core ($($nm : $ty),*) ($safe_type));
impl_hookable!(@impl_core ($($nm : $ty),*) ($unsafe_type));
impl_hookable!(@impl_unsafe ($($nm : $ty),*) ($unsafe_type) ($safe_type));
impl_hookable!(@impl_safe ($($nm : $ty),*) ($safe_type));
};
(@impl_unsafe ($($nm:ident : $ty:ident),*) ($target:ty) ($detour:ty)) => {
#[cfg(feature = "static-detour")]
impl<Ret: 'static, $($ty: 'static),*> $crate::StaticDetour<$target> {
#[doc(hidden)]
pub unsafe fn call(&self, $($nm : $ty),*) -> Ret {
let original: $target = ::std::mem::transmute(self.trampoline().expect("calling detour trampoline"));
original($($nm),*)
}
}
impl<Ret: 'static, $($ty: 'static),*> $crate::GenericDetour<$target> {
#[doc(hidden)]
pub unsafe fn call(&self, $($nm : $ty),*) -> Ret {
let original: $target = ::std::mem::transmute(self.trampoline());
original($($nm),*)
}
}
};
(@impl_safe ($($nm:ident : $ty:ident),*) ($fn_type:ty)) => {
#[cfg(feature = "static-detour")]
impl<Ret: 'static, $($ty: 'static),*> $crate::StaticDetour<$fn_type> {
#[doc(hidden)]
pub fn call(&self, $($nm : $ty),*) -> Ret {
unsafe {
let original: $fn_type = ::std::mem::transmute(self.trampoline().expect("calling detour trampoline"));
original($($nm),*)
}
}
}
impl<Ret: 'static, $($ty: 'static),*> $crate::GenericDetour<$fn_type> {
#[doc(hidden)]
pub fn call(&self, $($nm : $ty),*) -> Ret {
unsafe {
let original: $fn_type = ::std::mem::transmute(self.trampoline());
original($($nm),*)
}
}
}
};
(@impl_core ($($nm:ident : $ty:ident),*) ($fn_type:ty)) => {
unsafe impl<Ret: 'static, $($ty: 'static),*> Function for $fn_type {
type Arguments = ($($ty,)*);
type Output = Ret;
unsafe fn from_ptr(ptr: *const ()) -> Self {
::std::mem::transmute(ptr)
}
fn to_ptr(&self) -> *const () {
*self as *const ()
}
}
};
($($nm:ident : $ty:ident),*) => {
impl_hookable!(@recurse ($($nm : $ty),*) ());
};
}