1#[cfg(feature = "static-detour")]
34#[cfg_attr(docsrs, doc(cfg(feature = "static-detour")))]
35#[macro_export]
36macro_rules! static_detour {
38 (@parse_attributes ($($input:tt)*) | #[$attribute:meta] $($rest:tt)*) => {
40 $crate::static_detour!(@parse_attributes ($($input)* $attribute) | $($rest)*);
41 };
42 (@parse_attributes ($($input:tt)*) | $($rest:tt)+) => {
43 $crate::static_detour!(@parse_access_modifier (($($input)*)) | $($rest)*);
44 };
45
46 (@parse_access_modifier ($($input:tt)*) | pub(in $vis:path) static $($rest:tt)*) => {
48 $crate::static_detour!(@parse_name ($($input)* (pub(in $vis))) | $($rest)*);
49 };
50 (@parse_access_modifier ($($input:tt)*) | pub($vis:tt) static $($rest:tt)*) => {
51 $crate::static_detour!(@parse_name ($($input)* (pub($vis))) | $($rest)*);
52 };
53 (@parse_access_modifier ($($input:tt)*) | pub static $($rest:tt)*) => {
54 $crate::static_detour!(@parse_name ($($input)* (pub)) | $($rest)*);
55 };
56 (@parse_access_modifier ($($input:tt)*) | static $($rest:tt)*) => {
57 $crate::static_detour!(@parse_name ($($input)* ()) | $($rest)*);
58 };
59
60 (@parse_name ($($input:tt)*) | $name:ident : $($rest:tt)*) => {
62 $crate::static_detour!(@parse_unsafe ($($input)* ($name)) | $($rest)*);
63 };
64
65 (@parse_unsafe ($($input:tt)*) | unsafe $($rest:tt)*) => {
67 $crate::static_detour!(@parse_calling_convention ($($input)*) (unsafe) | $($rest)*);
68 };
69 (@parse_unsafe ($($input:tt)*) | $($rest:tt)*) => {
70 $crate::static_detour!(@parse_calling_convention ($($input)*) () | $($rest)*);
71 };
72
73 (@parse_calling_convention
75 ($($input:tt)*) ($($modifier:tt)*) | extern $cc:tt fn $($rest:tt)*) => {
76 $crate::static_detour!(@parse_prototype ($($input)* ($($modifier)* extern $cc)) | $($rest)*);
77 };
78 (@parse_calling_convention
79 ($($input:tt)*) ($($modifier:tt)*) | extern fn $($rest:tt)*) => {
80 $crate::static_detour!(@parse_prototype ($($input)* ($($modifier)* extern)) | $($rest)*);
81 };
82 (@parse_calling_convention ($($input:tt)*) ($($modifier:tt)*) | fn $($rest:tt)*) => {
83 $crate::static_detour!(@parse_prototype ($($input)* ($($modifier)*)) | $($rest)*);
84 };
85
86 (@parse_prototype
88 ($($input:tt)*) | ($($argument_type:ty),*) -> $return_type:ty ; $($rest:tt)*) => {
89 $crate::static_detour!(
90 @parse_terminator ($($input)* ($($argument_type)*) ($return_type)) | ; $($rest)*);
91 };
92 (@parse_prototype ($($input:tt)*) | ($($argument_type:ty),*) $($rest:tt)*) => {
93 $crate::static_detour!(@parse_terminator ($($input)* ($($argument_type)*) (())) | $($rest)*);
94 };
95
96 (@parse_terminator ($($input:tt)*) | ; $($rest:tt)*) => {
98 $crate::static_detour!(@parse_entries ($($input)*) | $($rest)*);
99 };
100
101 (@parse_entries ($($input:tt)*) | $($rest:tt)+) => {
103 $crate::static_detour!(@aggregate $($input)*);
104 $crate::static_detour!($($rest)*);
105 };
106 (@parse_entries ($($input:tt)*) | ) => {
107 $crate::static_detour!(@aggregate $($input)*);
108 };
109
110 (@aggregate ($($attribute:meta)*) ($($visibility:tt)*) ($name:ident)
112 ($($modifier:tt)*) ($($argument_type:ty)*) ($return_type:ty)) => {
113 $crate::static_detour!(@argument_names (create_detour)(
114 ($($attribute)*) ($($visibility)*) ($name)
115 ($($modifier)*) ($($argument_type)*) ($return_type)
116 ($($modifier)* fn ($($argument_type),*) -> $return_type)
117 )($($argument_type)*));
118 };
119
120 (@create_detour ($($argument_name:ident)*) ($($attribute:meta)*) ($($visibility:tt)*)
122 ($name:ident) ($($modifier:tt)*) ($($argument_type:ty)*)
123 ($return_type:ty) ($fn_type:ty)) => {
124 $crate::static_detour!(@generate
125 #[allow(non_upper_case_globals)]
126 $(#[$attribute])*
127 $($visibility)* static $name: $crate::StaticDetour<$fn_type> = {
128 #[inline(never)]
129 #[allow(unused_unsafe)]
130 $($modifier) * fn __ffi_detour(
131 $($argument_name: $argument_type),*) -> $return_type {
132 #[allow(unused_unsafe)]
133 ($name.__detour())($($argument_name),*)
134 }
135
136 $crate::StaticDetour::__new(__ffi_detour)
137 };
138 );
139 };
140
141 (@argument_names ($label:ident) ($($input:tt)*) ($($token:tt)*)) => {
143 $crate::static_detour!(@argument_names ($label) ($($input)*)(
144 __arg_0 __arg_1 __arg_2 __arg_3 __arg_4 __arg_5 __arg_6
145 __arg_7 __arg_8 __arg_9 __arg_10 __arg_11 __arg_12 __arg_13
146 )($($token)*)());
147 };
148 (@argument_names
149 ($label:ident)
150 ($($input:tt)*)
151 ($hd_name:tt $($tl_name:tt)*)
152 ($hd:tt $($tl:tt)*) ($($acc:tt)*)) => {
153 $crate::static_detour!(
154 @argument_names ($label) ($($input)*) ($($tl_name)*) ($($tl)*) ($($acc)* $hd_name));
155 };
156 (@argument_names ($label:ident) ($($input:tt)*) ($($name:tt)*) () ($($acc:tt)*)) => {
157 $crate::static_detour!(@$label ($($acc)*) $($input)*);
158 };
159
160 (@generate $item:item) => { $item };
161
162 ($($t:tt)+) => {
164 $crate::static_detour!(@parse_attributes () | $($t)+);
165 };
166}
167
168macro_rules! impl_hookable {
169 (@recurse () ($($nm:ident : $ty:ident),*)) => {
170 impl_hookable!(@impl_all ($($nm : $ty),*));
171 };
172 (@recurse
173 ($hd_nm:ident : $hd_ty:ident $(, $tl_nm:ident : $tl_ty:ident)*)
174 ($($nm:ident : $ty:ident),*)) => {
175 impl_hookable!(@impl_all ($($nm : $ty),*));
176 impl_hookable!(@recurse ($($tl_nm : $tl_ty),*) ($($nm : $ty,)* $hd_nm : $hd_ty));
177 };
178
179 (@impl_all ($($nm:ident : $ty:ident),*)) => {
180 impl_hookable!(@impl_pair ($($nm : $ty),*) ( fn($($ty),*) -> Ret));
181 impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "cdecl" fn($($ty),*) -> Ret));
182 impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "stdcall" fn($($ty),*) -> Ret));
183 impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "fastcall" fn($($ty),*) -> Ret));
184 impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "win64" fn($($ty),*) -> Ret));
185 impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "C" fn($($ty),*) -> Ret));
186 impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "system" fn($($ty),*) -> Ret));
187
188 #[cfg(feature = "thiscall-abi")]
189 #[cfg_attr(docsrs, doc(cfg(feature = "thiscall-abi")))]
190 impl_hookable!(@impl_pair ($($nm : $ty),*) (extern "thiscall" fn($($ty),*) -> Ret));
191 };
192
193 (@impl_pair ($($nm:ident : $ty:ident),*) ($($fn_t:tt)*)) => {
194 impl_hookable!(@impl_fun ($($nm : $ty),*) ($($fn_t)*) (unsafe $($fn_t)*));
195 };
196
197 (@impl_fun ($($nm:ident : $ty:ident),*) ($safe_type:ty) ($unsafe_type:ty)) => {
198 impl_hookable!(@impl_core ($($nm : $ty),*) ($safe_type));
199 impl_hookable!(@impl_core ($($nm : $ty),*) ($unsafe_type));
200
201 impl_hookable!(@impl_unsafe ($($nm : $ty),*) ($unsafe_type) ($safe_type));
202 impl_hookable!(@impl_safe ($($nm : $ty),*) ($safe_type));
203 };
204
205 (@impl_unsafe ($($nm:ident : $ty:ident),*) ($target:ty) ($detour:ty)) => {
206 #[cfg(feature = "static-detour")]
207 impl<Ret: 'static, $($ty: 'static),*> $crate::StaticDetour<$target> {
208 #[doc(hidden)]
209 pub unsafe fn call(&self, $($nm : $ty),*) -> Ret {
210 let original: $target = ::std::mem::transmute(self.trampoline().expect("calling detour trampoline"));
211 original($($nm),*)
212 }
213 }
214
215 impl<Ret: 'static, $($ty: 'static),*> $crate::GenericDetour<$target> {
216 #[doc(hidden)]
217 pub unsafe fn call(&self, $($nm : $ty),*) -> Ret {
218 let original: $target = ::std::mem::transmute(self.trampoline());
219 original($($nm),*)
220 }
221 }
222 };
223
224 (@impl_safe ($($nm:ident : $ty:ident),*) ($fn_type:ty)) => {
225 #[cfg(feature = "static-detour")]
226 impl<Ret: 'static, $($ty: 'static),*> $crate::StaticDetour<$fn_type> {
227 #[doc(hidden)]
228 pub fn call(&self, $($nm : $ty),*) -> Ret {
229 unsafe {
230 let original: $fn_type = ::std::mem::transmute(self.trampoline().expect("calling detour trampoline"));
231 original($($nm),*)
232 }
233 }
234 }
235
236 impl<Ret: 'static, $($ty: 'static),*> $crate::GenericDetour<$fn_type> {
237 #[doc(hidden)]
238 pub fn call(&self, $($nm : $ty),*) -> Ret {
239 unsafe {
240 let original: $fn_type = ::std::mem::transmute(self.trampoline());
241 original($($nm),*)
242 }
243 }
244 }
245 };
246
247 (@impl_core ($($nm:ident : $ty:ident),*) ($fn_type:ty)) => {
248 unsafe impl<Ret: 'static, $($ty: 'static),*> Function for $fn_type {
249 type Arguments = ($($ty,)*);
250 type Output = Ret;
251
252 unsafe fn from_ptr(ptr: *const ()) -> Self {
253 ::std::mem::transmute(ptr)
254 }
255
256 fn to_ptr(&self) -> *const () {
257 *self as *const ()
258 }
259 }
260 };
261
262 ($($nm:ident : $ty:ident),*) => {
263 impl_hookable!(@recurse ($($nm : $ty),*) ());
264 };
265}