use core::ptr::NonNull; use crate::encode::{EncodeArguments, EncodeReturn, RefEncode}; use crate::runtime::{AnyClass, AnyObject, Sel}; use crate::Message; /// Wrap the given closure in `exception::catch` if the `catch-all` feature is /// enabled. /// /// This is a macro to help with monomorphization when the feature is /// disabled, as well as improving the final stack trace (`#[track_caller]` /// doesn't really work on closures). #[cfg(not(feature = "catch-all"))] macro_rules! conditional_try { (|| $expr:expr) => { $expr }; } #[cfg(feature = "catch-all")] macro_rules! conditional_try { (|| $expr:expr) => {{ let f = core::panic::AssertUnwindSafe(|| $expr); match crate::exception::catch(f) { Ok(r) => r, Err(exception) => { if let Some(exception) = exception { panic!("uncaught {exception:?}\n{}", exception.stack_trace()) } else { panic!("uncaught exception nil") } } } }}; } // More information on how objc_msgSend works: // // // #[cfg(all(target_vendor = "apple", not(feature = "gnustep-1-7")))] mod msg_send_primitive { #[allow(unused_imports)] use core::mem; #[allow(unused_imports)] use crate::encode::Encoding; use crate::encode::{EncodeArguments, EncodeReturn}; use crate::ffi; use crate::runtime::{AnyClass, AnyObject, Imp, Sel}; /// On the below architectures we can statically find the correct method to /// call from the return type, by looking at its `EncodeReturn` impl. #[allow(clippy::missing_safety_doc)] unsafe trait MsgSendFn: EncodeReturn { const MSG_SEND: Imp; const MSG_SEND_SUPER: Imp; } #[cfg(target_arch = "aarch64")] /// `objc_msgSend_stret` is not even available in arm64. /// /// unsafe impl MsgSendFn for T { const MSG_SEND: Imp = ffi::objc_msgSend; const MSG_SEND_SUPER: Imp = ffi::objc_msgSendSuper; } #[cfg(target_arch = "arm")] /// Double-word sized fundamental data types don't use stret, but any /// composite type larger than 4 bytes does. /// /// /// /// unsafe impl MsgSendFn for T { const MSG_SEND: Imp = { if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN { ffi::objc_msgSend } else if mem::size_of::() <= 4 { ffi::objc_msgSend } else { ffi::objc_msgSend_stret } }; const MSG_SEND_SUPER: Imp = { if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN { ffi::objc_msgSendSuper } else if mem::size_of::() <= 4 { ffi::objc_msgSendSuper } else { ffi::objc_msgSendSuper_stret } }; } #[cfg(target_arch = "x86")] /// Structures 1 or 2 bytes in size are placed in EAX. /// Structures 4 or 8 bytes in size are placed in: EAX and EDX. /// Structures of other sizes are placed at the address supplied by the caller. /// /// /// unsafe impl MsgSendFn for T { const MSG_SEND: Imp = { // See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172 if let Encoding::Float | Encoding::Double | Encoding::LongDouble = T::ENCODING_RETURN { ffi::objc_msgSend_fpret } else if let 0 | 1 | 2 | 4 | 8 = mem::size_of::() { ffi::objc_msgSend } else { ffi::objc_msgSend_stret } }; const MSG_SEND_SUPER: Imp = { if let 0 | 1 | 2 | 4 | 8 = mem::size_of::() { ffi::objc_msgSendSuper } else { ffi::objc_msgSendSuper_stret } }; } #[cfg(target_arch = "x86_64")] /// If the size of an object is larger than two eightbytes, it has class /// MEMORY. If the type has class MEMORY, then the caller provides space for /// the return value and passes the address of this storage. /// /// /// unsafe impl MsgSendFn for T { const MSG_SEND: Imp = { // See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172 if let Encoding::LongDouble = T::ENCODING_RETURN { ffi::objc_msgSend_fpret } else if let Encoding::LongDoubleComplex = T::ENCODING_RETURN { ffi::objc_msgSend_fp2ret } else if mem::size_of::() <= 16 { ffi::objc_msgSend } else { ffi::objc_msgSend_stret } }; const MSG_SEND_SUPER: Imp = { if mem::size_of::() <= 16 { ffi::objc_msgSendSuper } else { ffi::objc_msgSendSuper_stret } }; } #[inline] #[track_caller] pub(crate) unsafe fn send( receiver: *mut AnyObject, sel: Sel, args: A, ) -> R { let msg_send_fn = R::MSG_SEND; // Note: Modern Objective-C compilers have a workaround to ensure that // messages to `nil` with a struct return produces `mem::zeroed()`, // see: // // // We _could_ technically do something similar, but since we're // disallowing messages to `nil` with `debug_assertions` enabled // anyhow, and since Rust has a much stronger type-system that // disallows NULL/nil in most cases, we won't bother supporting it. unsafe { A::__invoke(msg_send_fn, receiver, sel, args) } } #[inline] #[track_caller] pub(crate) unsafe fn send_super( receiver: *mut AnyObject, super_class: &AnyClass, sel: Sel, args: A, ) -> R { let mut sup = ffi::objc_super { receiver, super_class, }; let receiver: *mut ffi::objc_super = &mut sup; let receiver = receiver.cast(); let msg_send_fn = R::MSG_SEND_SUPER; unsafe { A::__invoke(msg_send_fn, receiver, sel, args) } } } #[cfg(feature = "gnustep-1-7")] mod msg_send_primitive { use core::mem; use crate::encode::{EncodeArguments, EncodeReturn}; use crate::ffi; use crate::runtime::{AnyClass, AnyObject, Imp, Sel}; #[inline] fn unwrap_msg_send_fn(msg_send_fn: Option) -> Imp { match msg_send_fn { Some(msg_send_fn) => msg_send_fn, None => { // SAFETY: This will never be NULL, even if the selector is not // found a callable function pointer will still be returned! // // `clang` doesn't insert a NULL check here either. unsafe { core::hint::unreachable_unchecked() } } } } #[track_caller] pub(crate) unsafe fn send( receiver: *mut AnyObject, sel: Sel, args: A, ) -> R { // If `receiver` is NULL, objc_msg_lookup will return a standard // C-method taking two arguments, the receiver and the selector. // // Transmuting and calling such a function with multiple parameters is // safe as long as the return value is a primitive (and e.g. not a big // struct or array). // // However, when the return value is a floating point value, the float // will end up as some undefined value, usually NaN, which is // incompatible with Apple's platforms. As such, we insert this extra // NULL check here. if receiver.is_null() { // SAFETY: Caller guarantees that messages to NULL-receivers only // return pointers or primitive values, and a mem::zeroed pointer // / primitive is just a NULL-pointer or a zeroed primitive. return unsafe { mem::zeroed() }; } let msg_send_fn = unsafe { ffi::objc_msg_lookup(receiver, sel) }; let msg_send_fn = unwrap_msg_send_fn(msg_send_fn); unsafe { A::__invoke(msg_send_fn, receiver, sel, args) } } #[track_caller] pub(crate) unsafe fn send_super( receiver: *mut AnyObject, super_class: &AnyClass, sel: Sel, args: A, ) -> R { if receiver.is_null() { // SAFETY: Same as in `send`. return unsafe { mem::zeroed() }; } let sup = ffi::objc_super { receiver, super_class, }; let msg_send_fn = unsafe { ffi::objc_msg_lookup_super(&sup, sel) }; let msg_send_fn = unwrap_msg_send_fn(msg_send_fn); unsafe { A::__invoke(msg_send_fn, receiver, sel, args) } } } #[cfg(all(not(target_vendor = "apple"), not(feature = "gnustep-1-7")))] mod msg_send_primitive { use crate::encode::{EncodeArguments, EncodeReturn}; use crate::runtime::{AnyClass, AnyObject, Sel}; #[track_caller] pub(crate) unsafe fn send( _receiver: *mut AnyObject, _sel: Sel, _args: A, ) -> R { unimplemented!("no runtime chosen") } #[track_caller] pub(crate) unsafe fn send_super( _receiver: *mut AnyObject, _superclass: &AnyClass, _sel: Sel, _args: A, ) -> R { unimplemented!("no runtime chosen") } } /// Help with monomorphizing in framework crates #[cfg(debug_assertions)] #[track_caller] fn msg_send_check( obj: Option<&AnyObject>, sel: Sel, args: &[crate::encode::Encoding], ret: &crate::encode::Encoding, ) { let cls = if let Some(obj) = obj { obj.class() } else { panic_null(sel) }; msg_send_check_class(cls, sel, args, ret); } #[cfg(debug_assertions)] #[track_caller] fn msg_send_check_class( cls: &AnyClass, sel: Sel, args: &[crate::encode::Encoding], ret: &crate::encode::Encoding, ) { if cfg!(feature = "disable-encoding-assertions") { // These checks are disabled. return; } use crate::verify::{verify_method_signature, Inner, VerificationError}; let err = if let Some(method) = cls.instance_method(sel) { if let Err(err) = verify_method_signature(method, args, ret) { err } else { return; } } else { VerificationError::from(Inner::MethodNotFound) }; panic_verify(cls, sel, &err); } #[cfg(debug_assertions)] #[track_caller] fn panic_null(sel: Sel) -> ! { panic!("messsaging {sel} to nil") } #[cfg(debug_assertions)] #[track_caller] fn panic_verify(cls: &AnyClass, sel: Sel, err: &crate::runtime::VerificationError) -> ! { panic!( "invalid message send to {}[{cls} {sel}]: {err}", if cls.is_metaclass() { "+" } else { "-" }, ) } mod private { pub trait Sealed {} } /// Types that can directly be used as the receiver of Objective-C messages. /// /// Examples include objects pointers, class pointers, and block pointers. /// /// /// # Safety /// /// This is a sealed trait, and should not need to be implemented. Open an /// issue if you know a use-case where this restrition should be lifted! pub unsafe trait MessageReceiver: private::Sealed + Sized { #[doc(hidden)] type __Inner: ?Sized + RefEncode; #[doc(hidden)] fn __as_raw_receiver(self) -> *mut AnyObject; /// Sends a message to the receiver with the given selector and arguments. /// /// This should be used instead of the [`performSelector:`] family of /// methods, as this is both more performant and flexible than that. /// /// The correct version of `objc_msgSend` will be chosen based on the /// return type. For more information, see [the Messaging section in /// Apple's Objective-C Runtime Programming Guide][guide-messaging]. /// /// If the selector is known at compile-time, it is recommended to use the /// [`msg_send!`] macro rather than this method. /// /// [`performSelector:`]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418867-performselector?language=objc /// [guide-messaging]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html /// /// /// # Safety /// /// This shares the same safety requirements as [`msg_send!`]. /// /// The added invariant is that the selector must take the same number of /// arguments as is given. /// /// [`msg_send!`]: crate::msg_send /// /// /// # Example /// /// Call the `copy` method, but using a dynamic selector instead. /// /// ```no_run /// use objc2::rc::Retained; /// use objc2::runtime::MessageReceiver; /// use objc2::sel; /// # use objc2::runtime::NSObject as MyObject; /// /// let obj = MyObject::new(); /// // SAFETY: The `copy` method takes no arguments, and returns an object /// let copy: *mut MyObject = unsafe { obj.send_message(sel!(copy), ()) }; /// // SAFETY: The `copy` method returns an object with +1 retain count /// let copy = unsafe { Retained::from_raw(copy) }.unwrap(); /// ``` #[inline] #[track_caller] #[doc(alias = "performSelector")] #[doc(alias = "performSelector:")] #[doc(alias = "performSelector:withObject:")] #[doc(alias = "performSelector:withObject:withObject:")] unsafe fn send_message(self, sel: Sel, args: A) -> R { let receiver = self.__as_raw_receiver(); #[cfg(debug_assertions)] { // SAFETY: Caller ensures only valid or NULL pointers. let obj = unsafe { receiver.as_ref() }; msg_send_check(obj, sel, A::ENCODINGS, &R::ENCODING_RETURN); } // SAFETY: Upheld by caller conditional_try!(|| unsafe { msg_send_primitive::send(receiver, sel, args) }) } /// Sends a message to a specific superclass with the given selector and /// arguments. /// /// The correct version of `objc_msgSend_super` will be chosen based on the /// return type. For more information, see the section on "Sending /// Messages" in Apple's [documentation][runtime]. /// /// If the selector is known at compile-time, it is recommended to use the /// [`msg_send!(super(...), ...)`] macro rather than this method. /// /// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc /// /// /// # Safety /// /// This shares the same safety requirements as /// [`msg_send!(super(...), ...)`]. /// /// The added invariant is that the selector must take the same number of /// arguments as is given. /// /// [`msg_send!(super(...), ...)`]: crate::msg_send #[inline] #[track_caller] unsafe fn send_super_message( self, superclass: &AnyClass, sel: Sel, args: A, ) -> R { let receiver = self.__as_raw_receiver(); #[cfg(debug_assertions)] { if receiver.is_null() { panic_null(sel); } msg_send_check_class(superclass, sel, A::ENCODINGS, &R::ENCODING_RETURN); } // SAFETY: Upheld by caller conditional_try!(|| unsafe { msg_send_primitive::send_super(receiver, superclass, sel, args) }) } } // Note that we implement MessageReceiver for unsized types as well, this is // to support `extern type`s in the future, not because we want to allow DSTs. impl private::Sealed for *const T {} unsafe impl MessageReceiver for *const T { type __Inner = T; #[inline] fn __as_raw_receiver(self) -> *mut AnyObject { (self as *mut T).cast() } } impl private::Sealed for *mut T {} unsafe impl MessageReceiver for *mut T { type __Inner = T; #[inline] fn __as_raw_receiver(self) -> *mut AnyObject { self.cast() } } impl private::Sealed for NonNull {} unsafe impl MessageReceiver for NonNull { type __Inner = T; #[inline] fn __as_raw_receiver(self) -> *mut AnyObject { self.as_ptr().cast() } } impl private::Sealed for &T {} unsafe impl MessageReceiver for &T { type __Inner = T; #[inline] fn __as_raw_receiver(self) -> *mut AnyObject { let ptr: *const T = self; (ptr as *mut T).cast() } } impl private::Sealed for &mut AnyObject {} /// `&mut AnyObject` is allowed as mutable, for easier transition from `objc`, /// even though it's basically always incorrect to hold `&mut AnyObject`. /// /// Use `*mut AnyObject` instead if you know for certain you need mutability, /// and cannot make do with interior mutability. unsafe impl MessageReceiver for &mut AnyObject { type __Inner = AnyObject; #[inline] fn __as_raw_receiver(self) -> *mut AnyObject { self } } #[cfg(test)] mod tests { use core::ptr; use super::*; use crate::msg_send; use crate::rc::{Allocated, Retained}; use crate::runtime::NSObject; use crate::test_utils; #[allow(unused)] fn test_different_receivers(obj: &mut AnyObject) { unsafe { let x = &mut *obj; let _: () = msg_send![x, mutable1]; // `x` is consumed by the above, so this won't work: // let _: () = msg_send![x, mutable2]; // It is only possible if we reborrow: let _: () = msg_send![&mut *obj, mutable1]; let _: () = msg_send![&mut *obj, mutable2]; // Test NonNull let obj = NonNull::from(obj); let _: () = msg_send![obj, mutable1]; let _: () = msg_send![obj, mutable2]; // And test raw pointers let obj: *mut AnyObject = obj.as_ptr(); let _: () = msg_send![obj, mutable1]; let _: () = msg_send![obj, mutable2]; } } #[test] fn test_send_message() { let obj = test_utils::custom_object(); let _: () = unsafe { msg_send![&obj, setFoo: 4u32] }; let result: u32 = unsafe { msg_send![&obj, foo] }; assert_eq!(result, 4); } #[test] fn test_send_message_stret() { let obj = test_utils::custom_object(); let result: test_utils::CustomStruct = unsafe { msg_send![&obj, customStruct] }; let expected = test_utils::CustomStruct { a: 1, b: 2, c: 3, d: 4, }; assert_eq!(result, expected); } #[test] #[cfg_attr(debug_assertions, should_panic = "messsaging description to nil")] fn test_send_message_nil() { let nil: *mut NSObject = ::core::ptr::null_mut(); // This result should not be relied on let result: Option> = unsafe { msg_send![nil, description] }; assert!(result.is_none()); // This result should not be relied on let result: usize = unsafe { msg_send![nil, hash] }; assert_eq!(result, 0); // This result should not be relied on #[cfg(target_pointer_width = "16")] let result: f32 = 0.0; #[cfg(target_pointer_width = "32")] let result: f32 = unsafe { msg_send![nil, floatValue] }; #[cfg(target_pointer_width = "64")] let result: f64 = unsafe { msg_send![nil, doubleValue] }; assert_eq!(result, 0.0); // This result should not be relied on let result: Option> = unsafe { msg_send![nil, multiple: 1u32, arguments: 2i8] }; assert!(result.is_none()); // This result should not be relied on let obj = unsafe { Allocated::new(ptr::null_mut()) }; let result: Option> = unsafe { msg_send![obj, init] }; assert!(result.is_none()); } #[test] fn test_send_message_super() { let obj = test_utils::custom_subclass_object(); let superclass = test_utils::custom_class(); unsafe { let _: () = msg_send![&obj, setFoo: 4u32]; let foo: u32 = msg_send![super(&obj, superclass), foo]; assert_eq!(foo, 4); // The subclass is overridden to return foo + 2 let foo: u32 = msg_send![&obj, foo]; assert_eq!(foo, 6); } } #[test] #[cfg_attr( feature = "gnustep-1-7", ignore = "GNUStep deadlocks here for some reason" )] fn test_send_message_class_super() { let cls = test_utils::custom_subclass(); let superclass = test_utils::custom_class(); unsafe { let foo: u32 = msg_send![super(cls, superclass.metaclass()), classFoo]; assert_eq!(foo, 7); // The subclass is overridden to return + 2 let foo: u32 = msg_send![cls, classFoo]; assert_eq!(foo, 9); } } }