1#![allow(non_snake_case)]
2
3use objc2::encode::Encode;
4use objc2::encode::EncodeArguments;
5use objc2::encode::EncodeReturn;
6use objc2::ffi::class_addMethod;
7use objc2::runtime::AnyClass;
8use objc2::runtime::AnyObject;
9use objc2::runtime::MethodImplementation;
10use objc2::runtime::Sel;
11use objc2::Encoding;
12use objc2::Message;
13
14pub use objc2;
15pub use ctor;
16
17pub fn exchange_instance_method(
19 originalClass: &AnyClass,
20 originalSelector: Sel,
21 swizzledClass: &AnyClass,
22 swizzledSelector: Sel,
23) {
24 let original_method = originalClass.instance_method(originalSelector);
25 let swizzled_method = swizzledClass.instance_method(swizzledSelector);
26 if original_method.is_some() && swizzled_method.is_some() {
27 let ori = original_method.unwrap();
28 let swi = swizzled_method.unwrap();
29 unsafe { ori.exchange_implementation(swi) };
30 }
31}
32
33pub fn exchange_class_method(
34 originalClass: &AnyClass,
35 originalSelector: Sel,
36 swizzledClass: &AnyClass,
37 swizzledSelector: Sel,
38) {
39 let original_method = originalClass.class_method(originalSelector);
40 let swizzled_method = swizzledClass.class_method(swizzledSelector);
41 if original_method.is_some() && swizzled_method.is_some() {
42 let ori = original_method.unwrap();
43 let swi = swizzled_method.unwrap();
44 unsafe { ori.exchange_implementation(swi) };
45 }
46}
47
48pub fn hook_instance_method<T, F>(cls: &AnyClass, sel: Sel, func: F)
49where
50 T: Message + ?Sized,
51 F: MethodImplementation<Callee = T>,
52{
53 if cls.instance_method(sel).is_none() {
54 return;
55 }
56
57 let s = format!("hook_{}", sel.name());
58 let hook_sel = Sel::register(s.as_str());
59 insert_method(cls, hook_sel, func);
60
61 exchange_instance_method(cls, sel, cls, hook_sel);
62}
63
64pub fn hook_class_method<T, F>(cls: &AnyClass, sel: Sel, func: F)
65where
66 T: Message + ?Sized,
67 F: MethodImplementation<Callee = T>,
68{
69 let s = format!("hook_{}", sel.name());
70 let hook_sel = Sel::register(s.as_str());
71 insert_method(cls.metaclass(), hook_sel, func);
72
73 exchange_class_method(cls, sel, cls, hook_sel);
74}
75
76pub fn insert_method<T, F>(cls: &AnyClass, sel: Sel, func: F) -> bool
77where
78 T: Message + ?Sized,
79 F: MethodImplementation<Callee = T>,
80{
81 fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> std::ffi::CString {
82 let mut types = format!("{ret}{}{}", <*mut AnyObject>::ENCODING, Sel::ENCODING);
84 for enc in args {
85 use core::fmt::Write;
86 write!(&mut types, "{enc}").unwrap();
87 }
88 std::ffi::CString::new(types).unwrap()
89 }
90
91 let enc_args = F::Arguments::ENCODINGS;
92 let enc_ret = &F::Return::ENCODING_RETURN;
93
94 let sel_args = sel.name().as_bytes().iter().filter(|&&b| b == b':').count();
95
96 assert_eq!(
97 sel_args,
98 enc_args.len(),
99 "selector {sel} accepts {sel_args} arguments, but function accepts {}",
100 enc_args.len(),
101 );
102
103 let types = method_type_encoding(enc_ret, enc_args);
104
105 unsafe {
106 let success = class_addMethod(
107 cls as *const AnyClass as _,
108 sel.as_ptr(),
109 Some(func.__imp()),
110 types.as_ptr(),
111 );
112 success as i8 != false as i8
114 }
115}
116
117#[macro_export]
118macro_rules! dummy_args {
119 (
120 $arg:ident $(,$tail:ident)* $(,)?
121 ) => {
122 $crate::dummy_args! {
123 [_]
124 $($tail)*
125 }
126 };
127 (
128 unsafe $arg:ident $(,$tail:ident)* $(,)?
129 ) => {
130 $crate::dummy_args! {
131 unsafe
132 [_]
133 $($tail)*
134 }
135 };
136
137 (
138 $($unsafe:ident)?
139 [$($t:tt)*]
140 $arg:ident $($tail:ident)*
141 ) => {
142 $crate::dummy_args! {
143 $($unsafe)?
144 [$($t)*, _]
145 $($tail)*
146 }
147 };
148
149 (
150 $($unsafe:ident)?
151 [$($t:tt)*]
152 ) => {
153 $($unsafe)? extern "C" fn ($($t)*) -> _
154 };
155}
156
157#[macro_export]
158macro_rules! arg_count {
159 (
161 $arg:ident $(,$tail:ident)* $(,)?
162 ) => {
163 $crate::arg_count! {
164 [1] $($tail)*
165 }
166 };
167
168 (
169 [$($t:tt)*]
170 $arg:ident $($tail:ident)*
171 ) => {
172 $crate::arg_count! {
173 [$($t)* + 1] $($tail)*
174 }
175 };
176
177 () => {0};
178
179 (
180 [$($t:tt)*]
181 ) => {
182 $($t)*
183 };
184}
185
186#[macro_export]
187macro_rules! sel_count {
188 (
190 $arg:ident
191 ) => {
192 $crate::sel_count! {
193 [0]
194 }
195 };
196
197 (
198 $arg:ident : $($tail:ident :)*
199 ) => {
200 $crate::sel_count! {
201 [1]
202 $($tail :)*
203 }
204 };
205
206 (
207 [$($t:tt)*]
208 $arg:ident : $($tail:ident :)*
209 ) => {
210 $crate::sel_count! {
211 [$($t)* + 1] $($tail :)*
212 }
213 };
214
215 (
216 [$($t:tt)*]
217 ) => {
218 $($t)*
219 };
220}
221
222#[macro_export]
223macro_rules! param_count_match {
224 (
225 [$($arg:ident),*]
226 []
227 ) => {};
228
229 (
230 [$($arg:ident),*]
231 [$($sel:tt)*]
232 ) => {
233 $crate::param_count_match!{
234 [$($arg),*]
235 [$($sel)*]
236 [$($sel)*]
237 }
238 };
239
240 (
241 [$arg1:ident $(,$tail1:ident)* $(,)?]
242 [$sel:ident : $($tail2:ident :)*]
243 [$($t:tt)*]
244 ) => {
245 $crate::param_count_match! {
246 [$($tail1),*]
247 [$($tail2 :)*]
248 [$($t)*]
249 }
250 };
251
252 (
253 [$arg1:ident, $arg2:ident]
254 [$($sel:ident)?]
255 [$($t:tt)*]
256 ) => {
257 };
259
260 (
261 [$($arg:tt)*]
262 [$($sel:tt)*]
263 [$($t:tt)*]
264 ) => {
265 compile_error!(stringify!(the param count of selector($($t)*) and function does not equal));
266 };
267}
268
269#[macro_export]
289macro_rules! hook_helper {
290 (
291 $(
292 $(#[$meta: meta])*
293 $(-($($r:tt)*)[$class1: ident $($sel1:tt)*])?
294 $(+($($r2:tt)*)[$class2: ident $($sel2:tt)*])?
295 unsafe extern "C" fn $name: ident ($($arg:ident: $ty: ty),* $(,)?) $(->$ret:ty)? $body: block
296 )*
297 ) => {
298 $(
299 #[allow(non_snake_case)]
300 $(#[$meta])*
301 unsafe extern "C" fn $name($($arg: $ty),*) $(->$ret)? $body
302
303 #[allow(unused_imports)]
304 #[allow(non_snake_case)]
305 mod $name {
306 use $crate::objc2::class;
307 use $crate::objc2::sel;
308 use super::$name;
309
310 #[$crate::ctor::ctor]
311 fn __dymmy() {
312 $crate::param_count_match!{
313 [$($arg),*]
314 [$($($sel1)*)?]
315 };
316
317 $crate::param_count_match!{
318 [$($arg),*]
319 [$($($sel2)*)?]
320 };
321
322 #[allow(unused_macros)]
323 macro_rules! func {
324 () => {
325 $crate::dummy_args!(unsafe $($arg),*)
326 };
327 }
328 $(
329 $crate::hook_instance_method(class!($class1), sel!($($sel1)*), $name as func!());
330 )?
331 $(
332 $crate::hook_class_method(class!($class2), sel!($($sel2)*), $name as func!());
333 )?
334 }
335 }
336 )*
337 };
338}
339
340#[macro_export]
341macro_rules! new_selector {
342 (
343 $(
344 $(#[$meta: meta])*
345 $(-($($r:tt)*)[$class1: ident $($sel1:tt)*])?
346 $(+($($r2:tt)*)[$class2: ident $($sel2:tt)*])?
347 unsafe extern "C" fn $name: ident ($($arg:ident: $ty: ty),* $(,)?) $(->$ret:ty)? $body: block
348 )*
349 ) => {
350 $(
351 #[allow(non_snake_case)]
352 $(#[$meta])*
353 unsafe extern "C" fn $name($($arg: $ty),*) $(->$ret)? $body
354
355 #[allow(unused_imports)]
356 #[allow(non_snake_case)]
357 mod $name {
358 use $crate::objc2::class;
359 use $crate::objc2::sel;
360 use super::$name;
361
362 #[$crate::ctor::ctor]
363 fn __dymmy() {
364 $crate::param_count_match!{
365 [$($arg),*]
366 [$($($sel1)*)?]
367 };
368
369 $crate::param_count_match!{
370 [$($arg),*]
371 [$($($sel2)*)?]
372 };
373
374 #[allow(unused_macros)]
375 macro_rules! func {
376 () => {
377 $crate::dummy_args!(unsafe $($arg),*)
378 };
379 }
380 $(
381 $crate::insert_method(class!($class1), sel!($($sel1)*), $name as func!());
382 )?
383 $(
384 $crate::insert_method(class!($class2).metaclass(), sel!($($sel2)*), $name as func!());
385 )?
386 }
387 }
388 )*
389 };
390}