1use std::ffi::c_void;
23
24use crate::runtime::Sel;
25
26#[link(name = "objc")]
28unsafe extern "C" {
29 fn objc_msgSend();
30
31 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
32 fn objc_msgSend_fpret();
33
34 #[cfg(not(target_arch = "aarch64"))]
35 fn objc_msgSend_stret();
36}
37
38#[cfg(target_arch = "x86_64")]
46const STRET_THRESHOLD: usize = 16;
47
48#[cfg(target_arch = "x86")]
49const STRET_THRESHOLD: usize = 8;
50
51#[cfg(target_arch = "aarch64")]
52#[allow(dead_code)]
53const STRET_THRESHOLD: usize = usize::MAX; #[cfg(target_arch = "arm")]
56const STRET_THRESHOLD: usize = 4;
57
58#[inline]
60#[allow(dead_code)]
61#[allow(clippy::absurd_extreme_comparisons)]
62const fn requires_stret<T>() -> bool {
63 std::mem::size_of::<T>() > STRET_THRESHOLD
64}
65
66#[inline]
68#[allow(dead_code)]
69fn is_float<T: 'static>() -> bool {
70 use std::any::TypeId;
71 TypeId::of::<T>() == TypeId::of::<f32>() || TypeId::of::<T>() == TypeId::of::<f64>()
72}
73
74#[inline]
86pub unsafe fn msg_send_0<R>(obj: *const c_void, sel: Sel) -> R {
87 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
88 if is_float::<R>() {
89 let f: unsafe extern "C" fn(*const c_void, Sel) -> R =
90 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
91 return unsafe { f(obj, sel) };
92 }
93
94 #[cfg(not(target_arch = "aarch64"))]
95 if requires_stret::<R>() {
96 let mut result = std::mem::MaybeUninit::<R>::uninit();
97 let f: unsafe extern "C" fn(*mut R, *const c_void, Sel) =
98 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
99 unsafe { f(result.as_mut_ptr(), obj, sel) };
100 return unsafe { result.assume_init() };
101 }
102
103 let f: unsafe extern "C" fn(*const c_void, Sel) -> R =
104 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
105 unsafe { f(obj, sel) }
106}
107
108#[inline]
121pub unsafe fn msg_send_1<R, A>(obj: *const c_void, sel: Sel, a: A) -> R {
122 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
123 if is_float::<R>() {
124 let f: unsafe extern "C" fn(*const c_void, Sel, A) -> R =
125 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
126 return unsafe { f(obj, sel, a) };
127 }
128
129 #[cfg(not(target_arch = "aarch64"))]
130 if requires_stret::<R>() {
131 let mut result = std::mem::MaybeUninit::<R>::uninit();
132 let f: unsafe extern "C" fn(*mut R, *const c_void, Sel, A) =
133 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
134 unsafe { f(result.as_mut_ptr(), obj, sel, a) };
135 return unsafe { result.assume_init() };
136 }
137
138 let f: unsafe extern "C" fn(*const c_void, Sel, A) -> R =
139 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
140 unsafe { f(obj, sel, a) }
141}
142
143#[inline]
153pub unsafe fn msg_send_2<R, A, B>(obj: *const c_void, sel: Sel, a: A, b: B) -> R {
154 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
155 if is_float::<R>() {
156 let f: unsafe extern "C" fn(*const c_void, Sel, A, B) -> R =
157 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
158 return unsafe { f(obj, sel, a, b) };
159 }
160
161 #[cfg(not(target_arch = "aarch64"))]
162 if requires_stret::<R>() {
163 let mut result = std::mem::MaybeUninit::<R>::uninit();
164 let f: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B) =
165 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
166 unsafe { f(result.as_mut_ptr(), obj, sel, a, b) };
167 return unsafe { result.assume_init() };
168 }
169
170 let f: unsafe extern "C" fn(*const c_void, Sel, A, B) -> R =
171 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
172 unsafe { f(obj, sel, a, b) }
173}
174
175#[inline]
185pub unsafe fn msg_send_3<R, A, B, C>(obj: *const c_void, sel: Sel, a: A, b: B, c: C) -> R {
186 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
187 if is_float::<R>() {
188 let f: unsafe extern "C" fn(*const c_void, Sel, A, B, C) -> R =
189 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
190 return unsafe { f(obj, sel, a, b, c) };
191 }
192
193 #[cfg(not(target_arch = "aarch64"))]
194 if requires_stret::<R>() {
195 let mut result = std::mem::MaybeUninit::<R>::uninit();
196 let f: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B, C) =
197 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
198 unsafe { f(result.as_mut_ptr(), obj, sel, a, b, c) };
199 return unsafe { result.assume_init() };
200 }
201
202 let f: unsafe extern "C" fn(*const c_void, Sel, A, B, C) -> R =
203 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
204 unsafe { f(obj, sel, a, b, c) }
205}
206
207#[inline]
217pub unsafe fn msg_send_4<R, A, B, C, D>(obj: *const c_void, sel: Sel, a: A, b: B, c: C, d: D) -> R {
218 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
219 if is_float::<R>() {
220 let f: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D) -> R =
221 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
222 return unsafe { f(obj, sel, a, b, c, d) };
223 }
224
225 #[cfg(not(target_arch = "aarch64"))]
226 if requires_stret::<R>() {
227 let mut result = std::mem::MaybeUninit::<R>::uninit();
228 let f: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B, C, D) =
229 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
230 unsafe { f(result.as_mut_ptr(), obj, sel, a, b, c, d) };
231 return unsafe { result.assume_init() };
232 }
233
234 let f: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D) -> R =
235 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
236 unsafe { f(obj, sel, a, b, c, d) }
237}
238
239#[inline]
249pub unsafe fn msg_send_5<R, A, B, C, D, E>(
250 obj: *const c_void,
251 sel: Sel,
252 a: A,
253 b: B,
254 c: C,
255 d: D,
256 e: E,
257) -> R {
258 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
259 if is_float::<R>() {
260 let f: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E) -> R =
261 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
262 return unsafe { f(obj, sel, a, b, c, d, e) };
263 }
264
265 #[cfg(not(target_arch = "aarch64"))]
266 if requires_stret::<R>() {
267 let mut result = std::mem::MaybeUninit::<R>::uninit();
268 let f: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B, C, D, E) =
269 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
270 unsafe { f(result.as_mut_ptr(), obj, sel, a, b, c, d, e) };
271 return unsafe { result.assume_init() };
272 }
273
274 let f: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E) -> R =
275 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
276 unsafe { f(obj, sel, a, b, c, d, e) }
277}
278
279#[inline]
289#[allow(clippy::too_many_arguments)]
290pub unsafe fn msg_send_6<R, A, B, C, D, E, F>(
291 obj: *const c_void,
292 sel: Sel,
293 a: A,
294 b: B,
295 c: C,
296 d: D,
297 e: E,
298 f_arg: F,
299) -> R {
300 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
301 if is_float::<R>() {
302 let func: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E, F) -> R =
303 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
304 return unsafe { func(obj, sel, a, b, c, d, e, f_arg) };
305 }
306
307 #[cfg(not(target_arch = "aarch64"))]
308 if requires_stret::<R>() {
309 let mut result = std::mem::MaybeUninit::<R>::uninit();
310 let func: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B, C, D, E, F) =
311 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
312 unsafe { func(result.as_mut_ptr(), obj, sel, a, b, c, d, e, f_arg) };
313 return unsafe { result.assume_init() };
314 }
315
316 let func: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E, F) -> R =
317 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
318 unsafe { func(obj, sel, a, b, c, d, e, f_arg) }
319}
320
321#[inline]
331#[allow(clippy::too_many_arguments)]
332pub unsafe fn msg_send_7<R, A, B, C, D, E, F, G>(
333 obj: *const c_void,
334 sel: Sel,
335 a: A,
336 b: B,
337 c: C,
338 d: D,
339 e: E,
340 f_arg: F,
341 g: G,
342) -> R {
343 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
344 if is_float::<R>() {
345 let func: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E, F, G) -> R =
346 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
347 return unsafe { func(obj, sel, a, b, c, d, e, f_arg, g) };
348 }
349
350 #[cfg(not(target_arch = "aarch64"))]
351 if requires_stret::<R>() {
352 let mut result = std::mem::MaybeUninit::<R>::uninit();
353 let func: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B, C, D, E, F, G) =
354 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
355 unsafe { func(result.as_mut_ptr(), obj, sel, a, b, c, d, e, f_arg, g) };
356 return unsafe { result.assume_init() };
357 }
358
359 let func: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E, F, G) -> R =
360 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
361 unsafe { func(obj, sel, a, b, c, d, e, f_arg, g) }
362}
363
364#[inline]
374#[allow(clippy::too_many_arguments)]
375pub unsafe fn msg_send_8<R, A, B, C, D, E, F, G, H>(
376 obj: *const c_void,
377 sel: Sel,
378 a: A,
379 b: B,
380 c: C,
381 d: D,
382 e: E,
383 f_arg: F,
384 g: G,
385 h: H,
386) -> R {
387 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
388 if is_float::<R>() {
389 let func: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E, F, G, H) -> R =
390 unsafe { std::mem::transmute(objc_msgSend_fpret as *const c_void) };
391 return unsafe { func(obj, sel, a, b, c, d, e, f_arg, g, h) };
392 }
393
394 #[cfg(not(target_arch = "aarch64"))]
395 if requires_stret::<R>() {
396 let mut result = std::mem::MaybeUninit::<R>::uninit();
397 let func: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B, C, D, E, F, G, H) =
398 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
399 unsafe { func(result.as_mut_ptr(), obj, sel, a, b, c, d, e, f_arg, g, h) };
400 return unsafe { result.assume_init() };
401 }
402
403 let func: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E, F, G, H) -> R =
404 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
405 unsafe { func(obj, sel, a, b, c, d, e, f_arg, g, h) }
406}
407
408#[inline]
418#[allow(clippy::too_many_arguments)]
419pub unsafe fn msg_send_9<R, A, B, C, D, E, F, G, H, I>(
420 obj: *const c_void,
421 sel: Sel,
422 a: A,
423 b: B,
424 c: C,
425 d: D,
426 e: E,
427 f_arg: F,
428 g: G,
429 h: H,
430 i: I,
431) -> R {
432 #[cfg(not(target_arch = "aarch64"))]
434 if requires_stret::<R>() {
435 let mut result = std::mem::MaybeUninit::<R>::uninit();
436 let func: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B, C, D, E, F, G, H, I) =
437 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
438 unsafe { func(result.as_mut_ptr(), obj, sel, a, b, c, d, e, f_arg, g, h, i) };
439 return unsafe { result.assume_init() };
440 }
441
442 let func: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E, F, G, H, I) -> R =
443 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
444 unsafe { func(obj, sel, a, b, c, d, e, f_arg, g, h, i) }
445}
446
447#[inline]
457#[allow(clippy::too_many_arguments)]
458pub unsafe fn msg_send_10<R, A, B, C, D, E, F, G, H, I, J>(
459 obj: *const c_void,
460 sel: Sel,
461 a: A,
462 b: B,
463 c: C,
464 d: D,
465 e: E,
466 f_arg: F,
467 g: G,
468 h: H,
469 i: I,
470 j: J,
471) -> R {
472 #[cfg(not(target_arch = "aarch64"))]
473 if requires_stret::<R>() {
474 let mut result = std::mem::MaybeUninit::<R>::uninit();
475 let func: unsafe extern "C" fn(*mut R, *const c_void, Sel, A, B, C, D, E, F, G, H, I, J) =
476 unsafe { std::mem::transmute(objc_msgSend_stret as *const c_void) };
477 unsafe {
478 func(
479 result.as_mut_ptr(),
480 obj,
481 sel,
482 a,
483 b,
484 c,
485 d,
486 e,
487 f_arg,
488 g,
489 h,
490 i,
491 j,
492 )
493 };
494 return unsafe { result.assume_init() };
495 }
496
497 let func: unsafe extern "C" fn(*const c_void, Sel, A, B, C, D, E, F, G, H, I, J) -> R =
498 unsafe { std::mem::transmute(objc_msgSend as *const c_void) };
499 unsafe { func(obj, sel, a, b, c, d, e, f_arg, g, h, i, j) }
500}