1use core::ptr::NonNull;
2
3use crate::encode::{EncodeArguments, EncodeReturn, RefEncode};
4use crate::runtime::{AnyClass, AnyObject, Sel};
5use crate::Message;
6
7#[cfg(not(feature = "catch-all"))]
14macro_rules! conditional_try {
15 (|| $expr:expr) => {
16 $expr
17 };
18}
19
20#[cfg(feature = "catch-all")]
21macro_rules! conditional_try {
22 (|| $expr:expr) => {{
23 let f = core::panic::AssertUnwindSafe(|| $expr);
24 match crate::exception::catch(f) {
25 Ok(r) => r,
26 Err(exception) => {
27 if let Some(exception) = exception {
28 panic!("uncaught {exception:?}\n{}", exception.stack_trace())
29 } else {
30 panic!("uncaught exception nil")
31 }
32 }
33 }
34 }};
35}
36
37#[cfg(all(target_vendor = "apple", not(feature = "gnustep-1-7")))]
42mod msg_send_primitive {
43 #[allow(unused_imports)]
44 use core::mem;
45
46 #[allow(unused_imports)]
47 use crate::encode::Encoding;
48 use crate::encode::{EncodeArguments, EncodeReturn};
49 use crate::ffi;
50 use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
51
52 #[allow(clippy::missing_safety_doc)]
55 unsafe trait MsgSendFn: EncodeReturn {
56 const MSG_SEND: Imp;
57 const MSG_SEND_SUPER: Imp;
58 }
59
60 #[cfg(target_arch = "aarch64")]
61 unsafe impl<T: EncodeReturn> MsgSendFn for T {
65 const MSG_SEND: Imp = ffi::objc_msgSend;
66 const MSG_SEND_SUPER: Imp = ffi::objc_msgSendSuper;
67 }
68
69 #[cfg(target_arch = "arm")]
70 unsafe impl<T: EncodeReturn> MsgSendFn for T {
77 const MSG_SEND: Imp = {
78 if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN
79 {
80 ffi::objc_msgSend
81 } else if mem::size_of::<T>() <= 4 {
82 ffi::objc_msgSend
83 } else {
84 ffi::objc_msgSend_stret
85 }
86 };
87 const MSG_SEND_SUPER: Imp = {
88 if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN
89 {
90 ffi::objc_msgSendSuper
91 } else if mem::size_of::<T>() <= 4 {
92 ffi::objc_msgSendSuper
93 } else {
94 ffi::objc_msgSendSuper_stret
95 }
96 };
97 }
98
99 #[cfg(target_arch = "x86")]
100 unsafe impl<T: EncodeReturn> MsgSendFn for T {
107 const MSG_SEND: Imp = {
108 if let Encoding::Float | Encoding::Double | Encoding::LongDouble = T::ENCODING_RETURN {
110 ffi::objc_msgSend_fpret
111 } else if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
112 ffi::objc_msgSend
113 } else {
114 ffi::objc_msgSend_stret
115 }
116 };
117 const MSG_SEND_SUPER: Imp = {
118 if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
119 ffi::objc_msgSendSuper
120 } else {
121 ffi::objc_msgSendSuper_stret
122 }
123 };
124 }
125
126 #[cfg(target_arch = "x86_64")]
127 unsafe impl<T: EncodeReturn> MsgSendFn for T {
134 const MSG_SEND: Imp = {
135 if let Encoding::LongDouble = T::ENCODING_RETURN {
137 ffi::objc_msgSend_fpret
138 } else if let Encoding::LongDoubleComplex = T::ENCODING_RETURN {
139 ffi::objc_msgSend_fp2ret
140 } else if mem::size_of::<T>() <= 16 {
141 ffi::objc_msgSend
142 } else {
143 ffi::objc_msgSend_stret
144 }
145 };
146 const MSG_SEND_SUPER: Imp = {
147 if mem::size_of::<T>() <= 16 {
148 ffi::objc_msgSendSuper
149 } else {
150 ffi::objc_msgSendSuper_stret
151 }
152 };
153 }
154
155 #[inline]
156 #[track_caller]
157 pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
158 receiver: *mut AnyObject,
159 sel: Sel,
160 args: A,
161 ) -> R {
162 let msg_send_fn = R::MSG_SEND;
163 unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
173 }
174
175 #[inline]
176 #[track_caller]
177 pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
178 receiver: *mut AnyObject,
179 super_class: &AnyClass,
180 sel: Sel,
181 args: A,
182 ) -> R {
183 let mut sup = ffi::objc_super {
184 receiver,
185 super_class,
186 };
187 let receiver: *mut ffi::objc_super = &mut sup;
188 let receiver = receiver.cast();
189
190 let msg_send_fn = R::MSG_SEND_SUPER;
191 unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
192 }
193}
194
195#[cfg(feature = "gnustep-1-7")]
196mod msg_send_primitive {
197 use core::mem;
198
199 use crate::encode::{EncodeArguments, EncodeReturn};
200 use crate::ffi;
201 use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
202
203 #[inline]
204 fn unwrap_msg_send_fn(msg_send_fn: Option<Imp>) -> Imp {
205 match msg_send_fn {
206 Some(msg_send_fn) => msg_send_fn,
207 None => {
208 unsafe { core::hint::unreachable_unchecked() }
213 }
214 }
215 }
216
217 #[track_caller]
218 pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
219 receiver: *mut AnyObject,
220 sel: Sel,
221 args: A,
222 ) -> R {
223 if receiver.is_null() {
235 return unsafe { mem::zeroed() };
239 }
240
241 let msg_send_fn = unsafe { ffi::objc_msg_lookup(receiver, sel) };
242 let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
243 unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
244 }
245
246 #[track_caller]
247 pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
248 receiver: *mut AnyObject,
249 super_class: &AnyClass,
250 sel: Sel,
251 args: A,
252 ) -> R {
253 if receiver.is_null() {
254 return unsafe { mem::zeroed() };
256 }
257
258 let sup = ffi::objc_super {
259 receiver,
260 super_class,
261 };
262 let msg_send_fn = unsafe { ffi::objc_msg_lookup_super(&sup, sel) };
263 let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
264 unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
265 }
266}
267
268#[cfg(all(not(target_vendor = "apple"), not(feature = "gnustep-1-7")))]
269mod msg_send_primitive {
270 use crate::encode::{EncodeArguments, EncodeReturn};
271 use crate::runtime::{AnyClass, AnyObject, Sel};
272
273 #[track_caller]
274 pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
275 _receiver: *mut AnyObject,
276 _sel: Sel,
277 _args: A,
278 ) -> R {
279 unimplemented!("no runtime chosen")
280 }
281
282 #[track_caller]
283 pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
284 _receiver: *mut AnyObject,
285 _superclass: &AnyClass,
286 _sel: Sel,
287 _args: A,
288 ) -> R {
289 unimplemented!("no runtime chosen")
290 }
291}
292
293#[cfg(debug_assertions)]
295#[track_caller]
296fn msg_send_check(
297 obj: Option<&AnyObject>,
298 sel: Sel,
299 args: &[crate::encode::Encoding],
300 ret: &crate::encode::Encoding,
301) {
302 let cls = if let Some(obj) = obj {
303 obj.class()
304 } else {
305 panic_null(sel)
306 };
307
308 msg_send_check_class(cls, sel, args, ret);
309}
310
311#[cfg(debug_assertions)]
312#[track_caller]
313fn msg_send_check_class(
314 cls: &AnyClass,
315 sel: Sel,
316 args: &[crate::encode::Encoding],
317 ret: &crate::encode::Encoding,
318) {
319 if cfg!(feature = "disable-encoding-assertions") {
320 return;
322 }
323
324 use crate::verify::{verify_method_signature, Inner, VerificationError};
325
326 let err = if let Some(method) = cls.instance_method(sel) {
327 if let Err(err) = verify_method_signature(method, args, ret) {
328 err
329 } else {
330 return;
331 }
332 } else {
333 VerificationError::from(Inner::MethodNotFound)
334 };
335
336 panic_verify(cls, sel, &err);
337}
338
339#[cfg(debug_assertions)]
340#[track_caller]
341fn panic_null(sel: Sel) -> ! {
342 panic!("messsaging {sel} to nil")
343}
344
345#[cfg(debug_assertions)]
346#[track_caller]
347fn panic_verify(cls: &AnyClass, sel: Sel, err: &crate::runtime::VerificationError) -> ! {
348 panic!(
349 "invalid message send to {}[{cls} {sel}]: {err}",
350 if cls.is_metaclass() { "+" } else { "-" },
351 )
352}
353
354mod private {
355 pub trait Sealed {}
356}
357
358pub unsafe trait MessageReceiver: private::Sealed + Sized {
368 #[doc(hidden)]
369 type __Inner: ?Sized + RefEncode;
370
371 #[doc(hidden)]
372 fn __as_raw_receiver(self) -> *mut AnyObject;
373
374 #[inline]
417 #[track_caller]
418 #[doc(alias = "performSelector")]
419 #[doc(alias = "performSelector:")]
420 #[doc(alias = "performSelector:withObject:")]
421 #[doc(alias = "performSelector:withObject:withObject:")]
422 unsafe fn send_message<A: EncodeArguments, R: EncodeReturn>(self, sel: Sel, args: A) -> R {
423 let receiver = self.__as_raw_receiver();
424 #[cfg(debug_assertions)]
425 {
426 let obj = unsafe { receiver.as_ref() };
428 msg_send_check(obj, sel, A::ENCODINGS, &R::ENCODING_RETURN);
429 }
430
431 conditional_try!(|| unsafe { msg_send_primitive::send(receiver, sel, args) })
433 }
434
435 #[inline]
458 #[track_caller]
459 unsafe fn send_super_message<A: EncodeArguments, R: EncodeReturn>(
460 self,
461 superclass: &AnyClass,
462 sel: Sel,
463 args: A,
464 ) -> R {
465 let receiver = self.__as_raw_receiver();
466 #[cfg(debug_assertions)]
467 {
468 if receiver.is_null() {
469 panic_null(sel);
470 }
471 msg_send_check_class(superclass, sel, A::ENCODINGS, &R::ENCODING_RETURN);
472 }
473
474 conditional_try!(|| unsafe {
476 msg_send_primitive::send_super(receiver, superclass, sel, args)
477 })
478 }
479}
480
481impl<T: ?Sized + Message> private::Sealed for *const T {}
485unsafe impl<T: ?Sized + Message> MessageReceiver for *const T {
486 type __Inner = T;
487
488 #[inline]
489 fn __as_raw_receiver(self) -> *mut AnyObject {
490 (self as *mut T).cast()
491 }
492}
493
494impl<T: ?Sized + Message> private::Sealed for *mut T {}
495unsafe impl<T: ?Sized + Message> MessageReceiver for *mut T {
496 type __Inner = T;
497
498 #[inline]
499 fn __as_raw_receiver(self) -> *mut AnyObject {
500 self.cast()
501 }
502}
503
504impl<T: ?Sized + Message> private::Sealed for NonNull<T> {}
505unsafe impl<T: ?Sized + Message> MessageReceiver for NonNull<T> {
506 type __Inner = T;
507
508 #[inline]
509 fn __as_raw_receiver(self) -> *mut AnyObject {
510 self.as_ptr().cast()
511 }
512}
513
514impl<T: ?Sized + Message> private::Sealed for &T {}
515unsafe impl<T: ?Sized + Message> MessageReceiver for &T {
516 type __Inner = T;
517
518 #[inline]
519 fn __as_raw_receiver(self) -> *mut AnyObject {
520 let ptr: *const T = self;
521 (ptr as *mut T).cast()
522 }
523}
524
525impl private::Sealed for &mut AnyObject {}
526unsafe impl MessageReceiver for &mut AnyObject {
532 type __Inner = AnyObject;
533
534 #[inline]
535 fn __as_raw_receiver(self) -> *mut AnyObject {
536 self
537 }
538}
539
540#[cfg(test)]
541mod tests {
542 use core::ptr;
543
544 use super::*;
545 use crate::msg_send;
546 use crate::rc::{Allocated, Retained};
547 use crate::runtime::NSObject;
548 use crate::test_utils;
549
550 #[allow(unused)]
551 fn test_different_receivers(obj: &mut AnyObject) {
552 unsafe {
553 let x = &mut *obj;
554 let _: () = msg_send![x, mutable1];
555 let _: () = msg_send![&mut *obj, mutable1];
560 let _: () = msg_send![&mut *obj, mutable2];
561
562 let obj = NonNull::from(obj);
564 let _: () = msg_send![obj, mutable1];
565 let _: () = msg_send![obj, mutable2];
566
567 let obj: *mut AnyObject = obj.as_ptr();
569 let _: () = msg_send![obj, mutable1];
570 let _: () = msg_send![obj, mutable2];
571 }
572 }
573
574 #[test]
575 fn test_send_message() {
576 let obj = test_utils::custom_object();
577 let _: () = unsafe { msg_send![&obj, setFoo: 4u32] };
578 let result: u32 = unsafe { msg_send![&obj, foo] };
579 assert_eq!(result, 4);
580 }
581
582 #[test]
583 fn test_send_message_stret() {
584 let obj = test_utils::custom_object();
585 let result: test_utils::CustomStruct = unsafe { msg_send![&obj, customStruct] };
586 let expected = test_utils::CustomStruct {
587 a: 1,
588 b: 2,
589 c: 3,
590 d: 4,
591 };
592 assert_eq!(result, expected);
593 }
594
595 #[test]
596 #[cfg_attr(debug_assertions, should_panic = "messsaging description to nil")]
597 fn test_send_message_nil() {
598 let nil: *mut NSObject = ::core::ptr::null_mut();
599
600 let result: Option<Retained<NSObject>> = unsafe { msg_send![nil, description] };
602 assert!(result.is_none());
603
604 let result: usize = unsafe { msg_send![nil, hash] };
606 assert_eq!(result, 0);
607
608 #[cfg(target_pointer_width = "16")]
610 let result: f32 = 0.0;
611 #[cfg(target_pointer_width = "32")]
612 let result: f32 = unsafe { msg_send![nil, floatValue] };
613 #[cfg(target_pointer_width = "64")]
614 let result: f64 = unsafe { msg_send![nil, doubleValue] };
615 assert_eq!(result, 0.0);
616
617 let result: Option<Retained<NSObject>> =
619 unsafe { msg_send![nil, multiple: 1u32, arguments: 2i8] };
620 assert!(result.is_none());
621
622 let obj = unsafe { Allocated::new(ptr::null_mut()) };
624 let result: Option<Retained<NSObject>> = unsafe { msg_send![obj, init] };
625 assert!(result.is_none());
626 }
627
628 #[test]
629 fn test_send_message_super() {
630 let obj = test_utils::custom_subclass_object();
631 let superclass = test_utils::custom_class();
632 unsafe {
633 let _: () = msg_send![&obj, setFoo: 4u32];
634 let foo: u32 = msg_send![super(&obj, superclass), foo];
635 assert_eq!(foo, 4);
636
637 let foo: u32 = msg_send![&obj, foo];
639 assert_eq!(foo, 6);
640 }
641 }
642
643 #[test]
644 #[cfg_attr(
645 feature = "gnustep-1-7",
646 ignore = "GNUStep deadlocks here for some reason"
647 )]
648 fn test_send_message_class_super() {
649 let cls = test_utils::custom_subclass();
650 let superclass = test_utils::custom_class();
651 unsafe {
652 let foo: u32 = msg_send![super(cls, superclass.metaclass()), classFoo];
653 assert_eq!(foo, 7);
654
655 let foo: u32 = msg_send![cls, classFoo];
657 assert_eq!(foo, 9);
658 }
659 }
660}