From fabf442c7d494c79b8353ebcafea15024a9f7cbd Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 1 Oct 2021 15:36:29 +0200 Subject: [PATCH 1/9] Add public `Message::send_super_message` method for dynamic selectors Also cuts down on the hoops we have to jump through from msg_send to reach the actual message sending --- objc2/src/lib.rs | 2 - objc2/src/macros.rs | 8 +-- objc2/src/message/mod.rs | 148 +++++++++++++++++++-------------------- 3 files changed, 77 insertions(+), 81 deletions(-) diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index 02df9997d..36100f645 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -85,8 +85,6 @@ pub use crate::message::{Message, MessageArguments, MessageError}; pub use crate::cache::CachedClass as __CachedClass; pub use crate::cache::CachedSel as __CachedSel; -pub use crate::message::send_message as __send_message; -pub use crate::message::send_super_message as __send_super_message; #[macro_use] mod macros; diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index fb9214d44..23ea42c2d 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -102,7 +102,7 @@ macro_rules! msg_send { (super($obj:expr, $superclass:expr), $name:ident) => ({ let sel = $crate::sel!($name); let result; - match $crate::__send_super_message(&*$obj, $superclass, sel, ()) { + match $crate::Message::send_super_message(&*$obj, $superclass, sel, ()) { Err(s) => panic!("{}", s), Ok(r) => result = r, } @@ -111,7 +111,7 @@ macro_rules! msg_send { (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({ let sel = $crate::sel!($($name:)+); let result; - match $crate::__send_super_message(&*$obj, $superclass, sel, ($($arg,)*)) { + match $crate::Message::send_super_message(&*$obj, $superclass, sel, ($($arg,)*)) { Err(s) => panic!("{}", s), Ok(r) => result = r, } @@ -120,7 +120,7 @@ macro_rules! msg_send { ($obj:expr, $name:ident) => ({ let sel = $crate::sel!($name); let result; - match $crate::__send_message(&*$obj, sel, ()) { + match $crate::Message::send_message(&*$obj, sel, ()) { Err(s) => panic!("{}", s), Ok(r) => result = r, } @@ -129,7 +129,7 @@ macro_rules! msg_send { ($obj:expr, $($name:ident : $arg:expr)+) => ({ let sel = $crate::sel!($($name:)+); let result; - match $crate::__send_message(&*$obj, sel, ($($arg,)*)) { + match $crate::Message::send_message(&*$obj, sel, ($($arg,)*)) { Err(s) => panic!("{}", s), Ok(r) => result = r, } diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index a36c78b80..6508e1f3b 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -56,46 +56,88 @@ use self::verify::{verify_message_signature, VerificationError}; /// /// [`objc_msgSend`]: https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend pub unsafe trait Message: RefEncode { - /** - Sends a message to self with the given selector and arguments. + /// Sends a message to self with the given selector and arguments. + /// + /// The correct version of `objc_msgSend` 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!`][`crate::msg_send`] macro rather than this method. + /// + /// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc + #[cfg_attr(feature = "verify_message", inline(always))] + unsafe fn send_message(this: *const Self, sel: Sel, args: A) -> Result + where + Self: Sized, + A: MessageArguments + EncodeArguments, + R: Encode, + { + #[cfg(feature = "verify_message")] + { + let cls = if this.is_null() { + return Err(VerificationError::NilReceiver(sel).into()); + } else { + (*(this as *const Object)).class() + }; - The correct version of `objc_msgSend` will be chosen based on the - return type. For more information, see Apple's documentation: - + verify_message_signature::(cls, sel)?; + } + send_unverified(this, sel, args) + } - If the selector is known at compile-time, it is recommended to use the - `msg_send!` macro rather than this method. - */ - unsafe fn send_message(&self, sel: Sel, args: A) -> Result + /// Sends a message to self's 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)`][`crate::msg_send`] macro rather than this method. + /// + /// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc + #[cfg_attr(feature = "verify_message", inline(always))] + unsafe fn send_super_message( + this: *const Self, + superclass: &Class, + sel: Sel, + args: A, + ) -> Result where Self: Sized, A: MessageArguments + EncodeArguments, R: Encode, { - send_message(self, sel, args) + #[cfg(feature = "verify_message")] + { + if this.is_null() { + return Err(VerificationError::NilReceiver(sel).into()); + } + verify_message_signature::(superclass, sel)?; + } + send_super_unverified(this, superclass, sel, args) } - /** - Verifies that the argument and return types match the encoding of the - method for the given selector. - - This will look up the encoding of the method for the given selector, `sel`, - and return a [`MessageError`] if any encodings differ for the arguments `A` - and return type `R`. - - # Example - ``` no_run - # use objc2::{class, msg_send, sel}; - # use objc2::runtime::{BOOL, Class, Object}; - # use objc2::Message; - let obj: &Object; - # obj = unsafe { msg_send![class!(NSObject), new] }; - let sel = sel!(isKindOfClass:); - // Verify isKindOfClass: takes one Class and returns a BOOL - let result = obj.verify_message::<(&Class,), BOOL>(sel); - assert!(result.is_ok()); - ``` - */ + /// Verify that the argument and return types match the encoding of the + /// method for the given selector. + /// + /// This will look up the encoding of the method for the given selector, + /// `sel`, and return a [`MessageError`] if any encodings differ for the + /// arguments `A` and return type `R`. + /// + /// # Example + /// ``` no_run + /// # use objc2::{class, msg_send, sel}; + /// # use objc2::runtime::{BOOL, Class, Object}; + /// # use objc2::Message; + /// let obj: &Object; + /// # obj = unsafe { msg_send![class!(NSObject), new] }; + /// let sel = sel!(isKindOfClass:); + /// // Verify isKindOfClass: takes one Class and returns a BOOL + /// let result = obj.verify_message::<(&Class,), BOOL>(sel); + /// assert!(result.is_ok()); + /// ``` fn verify_message(&self, sel: Sel) -> Result<(), MessageError> where Self: Sized, @@ -202,50 +244,6 @@ impl<'a> From> for MessageError { } } -#[doc(hidden)] -#[cfg_attr(feature = "verify_message", inline(always))] -pub unsafe fn send_message(obj: *const T, sel: Sel, args: A) -> Result -where - T: Message, - A: MessageArguments + EncodeArguments, - R: Encode, -{ - #[cfg(feature = "verify_message")] - { - let cls = if obj.is_null() { - return Err(VerificationError::NilReceiver(sel).into()); - } else { - (*(obj as *const Object)).class() - }; - - verify_message_signature::(cls, sel)?; - } - send_unverified(obj, sel, args) -} - -#[doc(hidden)] -#[cfg_attr(feature = "verify_message", inline(always))] -pub unsafe fn send_super_message( - obj: *const T, - superclass: &Class, - sel: Sel, - args: A, -) -> Result -where - T: Message, - A: MessageArguments + EncodeArguments, - R: Encode, -{ - #[cfg(feature = "verify_message")] - { - if obj.is_null() { - return Err(VerificationError::NilReceiver(sel).into()); - } - verify_message_signature::(superclass, sel)?; - } - send_super_unverified(obj, superclass, sel, args) -} - #[cfg(test)] mod tests { use super::*; From 663f32fa3722307a906279ec54da5532b4558036 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 1 Oct 2021 15:36:52 +0200 Subject: [PATCH 2/9] Add helper trait `MessageReceiver` In Objective-C, having a null pointer receiver is valid, but with `msg_send!` it would be converted into a reference, which is undefined behavior. The reference was converted into a pointer immediately after, but it is still UB. There might also be a mutable reference somewhere else in the program, which would now be aliased. With this, we can now stop de-referencing the receiver of msg_send!, and still have Id work ergonomically. However, null pointer receivers are still discouraged because of all the other issues they cause! This change additionally allows sending messages to NonNull, Option<&[mut] T> and Option>. --- objc2/src/lib.rs | 2 +- objc2/src/macros.rs | 8 +- objc2/src/message/apple/mod.rs | 17 ++-- objc2/src/message/gnustep.rs | 18 ++-- objc2/src/message/mod.rs | 158 +++++++++++++++++++++++++++++---- objc2/src/test_utils.rs | 12 ++- 6 files changed, 176 insertions(+), 39 deletions(-) diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index 36100f645..53c3940f3 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -81,7 +81,7 @@ extern "C" {} pub use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode}; -pub use crate::message::{Message, MessageArguments, MessageError}; +pub use crate::message::{Message, MessageArguments, MessageError, MessageReceiver}; pub use crate::cache::CachedClass as __CachedClass; pub use crate::cache::CachedSel as __CachedSel; diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index 23ea42c2d..238ddc191 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -102,7 +102,7 @@ macro_rules! msg_send { (super($obj:expr, $superclass:expr), $name:ident) => ({ let sel = $crate::sel!($name); let result; - match $crate::Message::send_super_message(&*$obj, $superclass, sel, ()) { + match $crate::MessageReceiver::send_super_message(&$obj, $superclass, sel, ()) { Err(s) => panic!("{}", s), Ok(r) => result = r, } @@ -111,7 +111,7 @@ macro_rules! msg_send { (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({ let sel = $crate::sel!($($name:)+); let result; - match $crate::Message::send_super_message(&*$obj, $superclass, sel, ($($arg,)*)) { + match $crate::MessageReceiver::send_super_message(&$obj, $superclass, sel, ($($arg,)*)) { Err(s) => panic!("{}", s), Ok(r) => result = r, } @@ -120,7 +120,7 @@ macro_rules! msg_send { ($obj:expr, $name:ident) => ({ let sel = $crate::sel!($name); let result; - match $crate::Message::send_message(&*$obj, sel, ()) { + match $crate::MessageReceiver::send_message(&$obj, sel, ()) { Err(s) => panic!("{}", s), Ok(r) => result = r, } @@ -129,7 +129,7 @@ macro_rules! msg_send { ($obj:expr, $($name:ident : $arg:expr)+) => ({ let sel = $crate::sel!($($name:)+); let result; - match $crate::Message::send_message(&*$obj, sel, ($($arg,)*)) { + match $crate::MessageReceiver::send_message(&$obj, sel, ($($arg,)*)) { Err(s) => panic!("{}", s), Ok(r) => result = r, } diff --git a/objc2/src/message/apple/mod.rs b/objc2/src/message/apple/mod.rs index e62dca5c2..d8bed3946 100644 --- a/objc2/src/message/apple/mod.rs +++ b/objc2/src/message/apple/mod.rs @@ -1,6 +1,6 @@ use objc2_sys::objc_super; -use super::{Encode, Message, MessageArguments, MessageError}; +use super::{Encode, MessageArguments, MessageError}; use crate::runtime::{Class, Imp, Object, Sel}; #[cfg(target_arch = "x86")] @@ -23,30 +23,31 @@ trait MsgSendFn: Encode { const MSG_SEND_SUPER: Imp; } -pub unsafe fn send_unverified(obj: *const T, sel: Sel, args: A) -> Result +pub unsafe fn send_unverified( + receiver: *mut Object, + sel: Sel, + args: A, +) -> Result where - T: Message, A: MessageArguments, R: Encode, { - let receiver = obj as *mut T as *mut Object; let msg_send_fn = R::MSG_SEND; objc_try!({ A::invoke(msg_send_fn, receiver, sel, args) }) } -pub unsafe fn send_super_unverified( - obj: *const T, +pub unsafe fn send_super_unverified( + receiver: *mut Object, superclass: &Class, sel: Sel, args: A, ) -> Result where - T: Message, A: MessageArguments, R: Encode, { let sup = objc_super { - receiver: obj as *mut T as *mut Object as *mut _, + receiver: receiver as *mut _, super_class: superclass as *const Class as *const _, }; let receiver = &sup as *const objc_super as *mut Object; diff --git a/objc2/src/message/gnustep.rs b/objc2/src/message/gnustep.rs index 19fbe4df0..ab50fd96a 100644 --- a/objc2/src/message/gnustep.rs +++ b/objc2/src/message/gnustep.rs @@ -1,36 +1,36 @@ use core::mem; use objc2_sys::{objc_msg_lookup, objc_msg_lookup_super, objc_super}; -use super::{Encode, Message, MessageArguments, MessageError}; +use super::{Encode, MessageArguments, MessageError}; use crate::runtime::{Class, Object, Sel}; -pub unsafe fn send_unverified(obj: *const T, sel: Sel, args: A) -> Result +pub unsafe fn send_unverified( + receiver: *mut Object, + sel: Sel, + args: A, +) -> Result where - T: Message, A: MessageArguments, R: Encode, { - if obj.is_null() { + if receiver.is_null() { return mem::zeroed(); } - let receiver = obj as *mut T as *mut Object; let msg_send_fn = objc_msg_lookup(receiver as *mut _, sel.as_ptr() as *const _); objc_try!({ A::invoke(msg_send_fn.expect("Null IMP"), receiver, sel, args) }) } -pub unsafe fn send_super_unverified( - obj: *const T, +pub unsafe fn send_super_unverified( + receiver: *mut Object, superclass: &Class, sel: Sel, args: A, ) -> Result where - T: Message, A: MessageArguments, R: Encode, { - let receiver = obj as *mut T as *mut Object; let sup = objc_super { receiver: receiver as *mut _, super_class: superclass as *const Class as *const _, diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index 6508e1f3b..7b7832966 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -1,8 +1,11 @@ use alloc::string::{String, ToString}; use core::fmt; use core::mem; +use core::ptr; +use core::ptr::NonNull; use std::error::Error; +use crate::rc::{Id, Ownership}; use crate::runtime::{Class, Imp, Object, Sel}; use crate::{Encode, EncodeArguments, RefEncode}; @@ -39,23 +42,65 @@ mod platform; use self::platform::{send_super_unverified, send_unverified}; use self::verify::{verify_message_signature, VerificationError}; -/// This trait marks types that can be sent Objective-C messages. +/// Types that can be sent Objective-C messages. /// /// Examples include objects, classes, and blocks. /// -/// Implementing this allows using pointers and references to the type as the +/// Implementing this provides [`MessageReceiver`] implementations for common +/// pointer types and references to the type, which allows using them as the /// receiver (first argument) in the [`msg_send!`][`crate::msg_send`] macro. /// /// # Safety /// -/// The type must implement [`RefEncode`] and adhere to the safety guidelines -/// therein. -/// /// A pointer to the type must be able to be the receiver of an Objective-C /// message sent with [`objc_msgSend`] or similar. /// +/// Additionally, the type must implement [`RefEncode`] and adhere to the +/// safety requirements therein. +/// /// [`objc_msgSend`]: https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend -pub unsafe trait Message: RefEncode { +pub unsafe trait Message: RefEncode {} + +unsafe impl Message for Object {} + +unsafe impl Message for Class {} + +// TODO: Make this fully private +pub(crate) mod private { + use super::{Id, Message, NonNull, Ownership}; + + pub trait Sealed {} + + impl Sealed for *const T {} + impl Sealed for *mut T {} + + impl<'a, T: Message + ?Sized> Sealed for &'a T {} + impl<'a, T: Message + ?Sized> Sealed for &'a mut T {} + impl Sealed for NonNull {} + impl Sealed for Id {} + + impl<'a, T: Message + ?Sized> Sealed for Option<&'a T> {} + impl<'a, T: Message + ?Sized> Sealed for Option<&'a mut T> {} + impl Sealed for Option> {} + impl Sealed for Option> {} +} + +/// Types that can directly be used as the receiver of Objective-C messages. +/// +/// This is a sealed trait (for now) that is automatically implemented for +/// pointers to types implementing [`Message`], so that code can be generic +/// over the message receiver. +/// +/// This is mostly an implementation detail; you'll want to implement +/// [`Message`] for your type instead. +/// +/// # Safety +/// +/// [`Self::as_raw_receiver`] must be implemented correctly. +pub unsafe trait MessageReceiver: private::Sealed { + /// Get a raw pointer to the receiver of the message. + fn as_raw_receiver(&self) -> *mut Object; + /// Sends a message to self with the given selector and arguments. /// /// The correct version of `objc_msgSend` will be chosen based on the @@ -67,12 +112,12 @@ pub unsafe trait Message: RefEncode { /// /// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc #[cfg_attr(feature = "verify_message", inline(always))] - unsafe fn send_message(this: *const Self, sel: Sel, args: A) -> Result + unsafe fn send_message(&self, sel: Sel, args: A) -> Result where - Self: Sized, A: MessageArguments + EncodeArguments, R: Encode, { + let this = self.as_raw_receiver(); #[cfg(feature = "verify_message")] { let cls = if this.is_null() { @@ -99,16 +144,16 @@ pub unsafe trait Message: RefEncode { /// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc #[cfg_attr(feature = "verify_message", inline(always))] unsafe fn send_super_message( - this: *const Self, + &self, superclass: &Class, sel: Sel, args: A, ) -> Result where - Self: Sized, A: MessageArguments + EncodeArguments, R: Encode, { + let this = self.as_raw_receiver(); #[cfg(feature = "verify_message")] { if this.is_null() { @@ -130,7 +175,7 @@ pub unsafe trait Message: RefEncode { /// ``` no_run /// # use objc2::{class, msg_send, sel}; /// # use objc2::runtime::{BOOL, Class, Object}; - /// # use objc2::Message; + /// # use objc2::MessageReceiver; /// let obj: &Object; /// # obj = unsafe { msg_send![class!(NSObject), new] }; /// let sel = sel!(isKindOfClass:); @@ -140,18 +185,99 @@ pub unsafe trait Message: RefEncode { /// ``` fn verify_message(&self, sel: Sel) -> Result<(), MessageError> where - Self: Sized, A: EncodeArguments, R: Encode, { - let obj = unsafe { &*(self as *const _ as *const Object) }; + let obj = unsafe { &*self.as_raw_receiver() }; verify_message_signature::(obj.class(), sel).map_err(MessageError::from) } } -unsafe impl Message for Object {} +// 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. -unsafe impl Message for Class {} +unsafe impl MessageReceiver for *const T { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + *self as *mut T as *mut Object + } +} + +unsafe impl MessageReceiver for *mut T { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + *self as *mut Object + } +} + +unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a T { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + *self as *const T as *mut T as *mut Object + } +} + +unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a mut T { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + *self as *const T as *mut T as *mut Object + } +} + +unsafe impl MessageReceiver for NonNull { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + self.as_ptr() as *mut Object + } +} + +unsafe impl MessageReceiver for Id { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + // TODO: Maybe don't dereference here, just to be safe? + (&**self).as_raw_receiver() + } +} + +unsafe impl<'a, T: Message + ?Sized> MessageReceiver for Option<&'a T> { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + match self { + None => ptr::null_mut(), + Some(obj) => obj.as_raw_receiver(), + } + } +} + +unsafe impl<'a, T: Message + ?Sized> MessageReceiver for Option<&'a mut T> { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + match self { + None => ptr::null_mut(), + Some(obj) => obj.as_raw_receiver(), + } + } +} + +unsafe impl MessageReceiver for Option> { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + match self { + None => ptr::null_mut(), + Some(obj) => obj.as_raw_receiver(), + } + } +} + +unsafe impl MessageReceiver for Option> { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + match self { + None => ptr::null_mut(), + Some(id) => id.as_raw_receiver(), + } + } +} /// Types that may be used as the arguments of an Objective-C message. pub trait MessageArguments: Sized { @@ -159,7 +285,7 @@ pub trait MessageArguments: Sized { /// /// This method is the primitive used when sending messages and should not /// be called directly; instead, use the `msg_send!` macro or, in cases - /// with a dynamic selector, the [`Message::send_message`] method. + /// with a dynamic selector, the [`MessageReceiver::send_message`] method. unsafe fn invoke(imp: Imp, obj: *mut Object, sel: Sel, args: Self) -> R; } diff --git a/objc2/src/test_utils.rs b/objc2/src/test_utils.rs index b7be60377..3d33b6794 100644 --- a/objc2/src/test_utils.rs +++ b/objc2/src/test_utils.rs @@ -4,7 +4,7 @@ use std::sync::Once; use crate::declare::{ClassDecl, ProtocolDecl}; use crate::runtime::{self, Class, Object, Protocol, Sel}; -use crate::{Encode, Encoding}; +use crate::{Encode, Encoding, MessageReceiver}; pub struct CustomObject { obj: *mut Object, @@ -18,6 +18,16 @@ impl CustomObject { } } +// TODO: Remove the need for this hack +impl crate::message::private::Sealed for CustomObject {} + +unsafe impl MessageReceiver for CustomObject { + #[inline] + fn as_raw_receiver(&self) -> *mut Object { + self.obj + } +} + impl Deref for CustomObject { type Target = Object; From a94fd59bf698319a6bc93f7b6001027ebc9c1314 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 9 Sep 2021 18:26:37 +0200 Subject: [PATCH 3/9] Make MessageArguments a subtrait of EncodeArguments, and remove Sized --- objc2/src/message/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index 7b7832966..e4bc653d2 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -114,7 +114,7 @@ pub unsafe trait MessageReceiver: private::Sealed { #[cfg_attr(feature = "verify_message", inline(always))] unsafe fn send_message(&self, sel: Sel, args: A) -> Result where - A: MessageArguments + EncodeArguments, + A: MessageArguments, R: Encode, { let this = self.as_raw_receiver(); @@ -150,7 +150,7 @@ pub unsafe trait MessageReceiver: private::Sealed { args: A, ) -> Result where - A: MessageArguments + EncodeArguments, + A: MessageArguments, R: Encode, { let this = self.as_raw_receiver(); @@ -280,7 +280,7 @@ unsafe impl MessageReceiver for Option> { } /// Types that may be used as the arguments of an Objective-C message. -pub trait MessageArguments: Sized { +pub trait MessageArguments: EncodeArguments { /// Invoke an [`Imp`] with the given object, selector, and arguments. /// /// This method is the primitive used when sending messages and should not @@ -291,7 +291,7 @@ pub trait MessageArguments: Sized { macro_rules! message_args_impl { ($($a:ident : $t:ident),*) => ( - impl<$($t),*> MessageArguments for ($($t,)*) { + impl<$($t: Encode),*> MessageArguments for ($($t,)*) { unsafe fn invoke(imp: Imp, obj: *mut Object, sel: Sel, ($($a,)*): Self) -> R { let imp: unsafe extern fn(*mut Object, Sel $(, $t)*) -> R = mem::transmute(imp); From e29349fbda813d3e5d507acaaf8cf17264a806a6 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 1 Oct 2021 14:43:33 +0200 Subject: [PATCH 4/9] Better document MessageArguments::invoke and add Encode bound on return --- objc2/src/message/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index e4bc653d2..99ca35023 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -286,15 +286,19 @@ pub trait MessageArguments: EncodeArguments { /// This method is the primitive used when sending messages and should not /// be called directly; instead, use the `msg_send!` macro or, in cases /// with a dynamic selector, the [`MessageReceiver::send_message`] method. - unsafe fn invoke(imp: Imp, obj: *mut Object, sel: Sel, args: Self) -> R; + unsafe fn invoke(imp: Imp, obj: *mut Object, sel: Sel, args: Self) -> R; } macro_rules! message_args_impl { ($($a:ident : $t:ident),*) => ( impl<$($t: Encode),*> MessageArguments for ($($t,)*) { - unsafe fn invoke(imp: Imp, obj: *mut Object, sel: Sel, ($($a,)*): Self) -> R { - let imp: unsafe extern fn(*mut Object, Sel $(, $t)*) -> R = - mem::transmute(imp); + #[inline] + unsafe fn invoke(imp: Imp, obj: *mut Object, sel: Sel, ($($a,)*): Self) -> R { + // The imp must be cast to the appropriate function pointer + // type before being called; the msgSend functions are not + // parametric, but instead "trampolines" to the actual + // method implementations. + let imp: unsafe extern "C" fn(*mut Object, Sel $(, $t)*) -> R = mem::transmute(imp); imp(obj, sel $(, $a)*) } } From 605857ab5219e1c2b8806d5202640ae701181b49 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 1 Oct 2021 15:16:15 +0200 Subject: [PATCH 5/9] Allow an optional comma after each argument to msg_send! This makes the syntax match Rust expectations a bit closer IMO --- objc2/src/macros.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index 238ddc191..282498d29 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -93,7 +93,9 @@ method's argument's encoding does not match the encoding of the given arguments. let obj: *mut Object; # let obj: *mut Object = 0 as *mut Object; let description: *const Object = msg_send![obj, description]; -let _: () = msg_send![obj, setArg1:1 arg2:2]; +let _: () = msg_send![obj, setArg1: 1 arg2: 2]; +// Or with an optional comma between arguments: +let _: () = msg_send![obj, setArg1: 1, arg2: 2]; # } ``` */ @@ -108,10 +110,10 @@ macro_rules! msg_send { } result }); - (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({ + (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr $(,)?)+) => ({ let sel = $crate::sel!($($name:)+); let result; - match $crate::MessageReceiver::send_super_message(&$obj, $superclass, sel, ($($arg,)*)) { + match $crate::MessageReceiver::send_super_message(&$obj, $superclass, sel, ($($arg,)+)) { Err(s) => panic!("{}", s), Ok(r) => result = r, } @@ -126,10 +128,10 @@ macro_rules! msg_send { } result }); - ($obj:expr, $($name:ident : $arg:expr)+) => ({ + ($obj:expr, $($name:ident : $arg:expr $(,)?)+) => ({ let sel = $crate::sel!($($name:)+); let result; - match $crate::MessageReceiver::send_message(&$obj, sel, ($($arg,)*)) { + match $crate::MessageReceiver::send_message(&$obj, sel, ($($arg,)+)) { Err(s) => panic!("{}", s), Ok(r) => result = r, } From 6d7ddbce9c7de9fe3ee6bd46a141d036f49abd34 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 1 Oct 2021 15:18:21 +0200 Subject: [PATCH 6/9] Manually format usage of msg_send! Rustfmt can't do this for us --- objc2/README.md | 2 +- objc2/src/declare.rs | 2 +- objc2/src/lib.rs | 2 +- objc2/src/message/mod.rs | 4 +-- objc2/src/runtime.rs | 2 +- objc2_foundation/examples/custom_class.rs | 2 +- objc2_foundation/src/array.rs | 22 +++++++++----- objc2_foundation/src/data.rs | 32 ++++++++++++++------ objc2_foundation/src/dictionary.rs | 37 ++++++++++++++++------- objc2_foundation/src/enumerator.rs | 9 ++++-- objc2_foundation/src/string.rs | 9 ++++-- objc2_foundation/src/value.rs | 7 +++-- 12 files changed, 86 insertions(+), 44 deletions(-) diff --git a/objc2/README.md b/objc2/README.md index 2e3af7584..12bf52be4 100644 --- a/objc2/README.md +++ b/objc2/README.md @@ -19,7 +19,7 @@ let cls = class!(NSObject); unsafe { let obj: *mut Object = msg_send![cls, new]; let hash: usize = msg_send![obj, hash]; - let is_kind: BOOL = msg_send![obj, isKindOfClass:cls]; + let is_kind: BOOL = msg_send![obj, isKindOfClass: cls]; // Even void methods must have their return type annotated let _: () = msg_send![obj, release]; } diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index f2ab27497..c6c2506a8 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -372,7 +372,7 @@ mod tests { // Registering the custom class is in test_utils let obj = test_utils::custom_object(); unsafe { - let _: () = msg_send![obj, setFoo:13u32]; + let _: () = msg_send![obj, setFoo: 13u32]; let result: u32 = msg_send![obj, foo]; assert!(result == 13); } diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index 53c3940f3..dcd49adad 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -12,7 +12,7 @@ Objective-C objects can be messaged using the [`msg_send!`](macro.msg_send!.html let cls = class!(NSObject); let obj: *mut Object = msg_send![cls, new]; let hash: usize = msg_send![obj, hash]; -let is_kind: BOOL = msg_send![obj, isKindOfClass:cls]; +let is_kind: BOOL = msg_send![obj, isKindOfClass: cls]; // Even void methods must have their return type annotated let _: () = msg_send![obj, release]; # } diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index 99ca35023..7cfe530d0 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -383,7 +383,7 @@ mod tests { fn test_send_message() { let obj = test_utils::custom_object(); let result: u32 = unsafe { - let _: () = msg_send![obj, setFoo:4u32]; + let _: () = msg_send![obj, setFoo: 4u32]; msg_send![obj, foo] }; assert!(result == 4); @@ -421,7 +421,7 @@ mod tests { let obj = test_utils::custom_subclass_object(); let superclass = test_utils::custom_class(); unsafe { - let _: () = msg_send![obj, setFoo:4u32]; + let _: () = msg_send![obj, setFoo: 4u32]; let foo: u32 = msg_send![super(obj, superclass), foo]; assert!(foo == 4); diff --git a/objc2/src/runtime.rs b/objc2/src/runtime.rs index 374663771..428a2ab67 100644 --- a/objc2/src/runtime.rs +++ b/objc2/src/runtime.rs @@ -533,7 +533,7 @@ mod tests { #[test] fn test_protocol_method() { let class = test_utils::custom_class(); - let result: i32 = unsafe { msg_send![class, addNumber:1 toNumber:2] }; + let result: i32 = unsafe { msg_send![class, addNumber: 1, toNumber: 2] }; assert_eq!(result, 3); } diff --git a/objc2_foundation/examples/custom_class.rs b/objc2_foundation/examples/custom_class.rs index 907cef8a1..e1f3916b1 100644 --- a/objc2_foundation/examples/custom_class.rs +++ b/objc2_foundation/examples/custom_class.rs @@ -82,7 +82,7 @@ fn main() { }); unsafe { - let _: () = msg_send![obj, setNumber:12u32]; + let _: () = msg_send![obj, setNumber: 12u32]; } println!("Number: {}", obj.number()); } diff --git a/objc2_foundation/src/array.rs b/objc2_foundation/src/array.rs index 94c15bc21..b78b13add 100644 --- a/objc2_foundation/src/array.rs +++ b/objc2_foundation/src/array.rs @@ -77,8 +77,11 @@ where { let cls = A::class(); let obj: *mut A = msg_send![cls, alloc]; - let obj: *mut A = msg_send![obj, initWithObjects:refs.as_ptr() - count:refs.len()]; + let obj: *mut A = msg_send![ + obj, + initWithObjects: refs.as_ptr(), + count: refs.len(), + ]; Id::new(NonNull::new_unchecked(obj)) } @@ -135,7 +138,7 @@ pub trait INSArray: INSObject { let range = NSRange::from_range(range); let mut vec = Vec::with_capacity(range.length); unsafe { - let _: () = msg_send![self, getObjects:vec.as_ptr() range:range]; + let _: () = msg_send![self, getObjects: vec.as_ptr(), range: range]; vec.set_len(range.length); } vec @@ -254,13 +257,13 @@ pub type NSSharedArray = NSArray; pub trait INSMutableArray: INSArray { fn add_object(&mut self, obj: Id) { unsafe { - let _: () = msg_send![self, addObject:&*obj]; + let _: () = msg_send![self, addObject: &*obj]; } } fn insert_object_at(&mut self, index: usize, obj: Id) { unsafe { - let _: () = msg_send![self, insertObject:&*obj atIndex:index]; + let _: () = msg_send![self, insertObject: &*obj, atIndex: index]; } } @@ -274,8 +277,11 @@ pub trait INSMutableArray: INSArray { Id::retain(obj.into()) }; unsafe { - let _: () = msg_send![self, replaceObjectAtIndex:index - withObject:&*obj]; + let _: () = msg_send![ + self, + replaceObjectAtIndex: index, + withObject: &*obj, + ]; } old_obj } @@ -333,7 +339,7 @@ pub trait INSMutableArray: INSArray { let context = &mut closure as *mut F as *mut c_void; unsafe { - let _: () = msg_send![self, sortUsingFunction:f context:context]; + let _: () = msg_send![self, sortUsingFunction: f, context: context]; } // Keep the closure alive until the function has run. drop(closure); diff --git a/objc2_foundation/src/data.rs b/objc2_foundation/src/data.rs index 422d69f8e..33cb925e6 100644 --- a/objc2_foundation/src/data.rs +++ b/objc2_foundation/src/data.rs @@ -35,8 +35,11 @@ pub trait INSData: INSObject { let bytes_ptr = bytes.as_ptr() as *const c_void; unsafe { let obj: *mut Self = msg_send![cls, alloc]; - let obj: *mut Self = msg_send![obj, initWithBytes:bytes_ptr - length:bytes.len()]; + let obj: *mut Self = msg_send![ + obj, + initWithBytes: bytes_ptr, + length: bytes.len(), + ]; Id::new(NonNull::new_unchecked(obj)) } } @@ -56,9 +59,12 @@ pub trait INSData: INSObject { let cls = Self::class(); unsafe { let obj: *mut Self = msg_send![cls, alloc]; - let obj: *mut Self = msg_send![obj, initWithBytesNoCopy:bytes_ptr - length:bytes.len() - deallocator:dealloc]; + let obj: *mut Self = msg_send![ + obj, + initWithBytesNoCopy: bytes_ptr, + length: bytes.len(), + deallocator: dealloc, + ]; core::mem::forget(bytes); Id::new(NonNull::new_unchecked(obj)) } @@ -98,8 +104,11 @@ pub trait INSMutableData: INSData { fn append(&mut self, bytes: &[u8]) { let bytes_ptr = bytes.as_ptr() as *const c_void; unsafe { - let _: () = msg_send![self, appendBytes:bytes_ptr - length:bytes.len()]; + let _: () = msg_send![ + self, + appendBytes: bytes_ptr, + length:bytes.len(), + ]; } } @@ -107,9 +116,12 @@ pub trait INSMutableData: INSData { let range = NSRange::from_range(range); let bytes_ptr = bytes.as_ptr() as *const c_void; unsafe { - let _: () = msg_send![self, replaceBytesInRange:range - withBytes:bytes_ptr - length:bytes.len()]; + let _: () = msg_send![ + self, + replaceBytesInRange:range, + withBytes:bytes_ptr, + length:bytes.len(), + ]; } } diff --git a/objc2_foundation/src/dictionary.rs b/objc2_foundation/src/dictionary.rs index 180f2b27e..5151494a1 100644 --- a/objc2_foundation/src/dictionary.rs +++ b/objc2_foundation/src/dictionary.rs @@ -18,9 +18,12 @@ where let cls = D::class(); let count = min(keys.len(), vals.len()); let obj: *mut D = msg_send![cls, alloc]; - let obj: *mut D = msg_send![obj, initWithObjects:vals.as_ptr() - forKeys:keys.as_ptr() - count:count]; + let obj: *mut D = msg_send![ + obj, + initWithObjects: vals.as_ptr(), + forKeys: keys.as_ptr(), + count: count, + ]; Id::new(NonNull::new_unchecked(obj)) } @@ -48,8 +51,11 @@ pub trait INSDictionary: INSObject { let len = self.count(); let mut keys = Vec::with_capacity(len); unsafe { - let _: () = msg_send![self, getObjects:ptr::null_mut::<&Self::Value>() - andKeys:keys.as_mut_ptr()]; + let _: () = msg_send![ + self, + getObjects: ptr::null_mut::<&Self::Value>(), + andKeys: keys.as_mut_ptr(), + ]; keys.set_len(len); } keys @@ -59,8 +65,11 @@ pub trait INSDictionary: INSObject { let len = self.count(); let mut vals = Vec::with_capacity(len); unsafe { - let _: () = msg_send![self, getObjects:vals.as_mut_ptr() - andKeys:ptr::null_mut::<&Self::Key>()]; + let _: () = msg_send![ + self, + getObjects: vals.as_mut_ptr(), + andKeys: ptr::null_mut::<&Self::Key>(), + ]; vals.set_len(len); } vals @@ -71,8 +80,11 @@ pub trait INSDictionary: INSObject { let mut keys = Vec::with_capacity(len); let mut objs = Vec::with_capacity(len); unsafe { - let _: () = msg_send![self, getObjects:objs.as_mut_ptr() - andKeys:keys.as_mut_ptr()]; + let _: () = msg_send![ + self, + getObjects: objs.as_mut_ptr(), + andKeys: keys.as_mut_ptr(), + ]; keys.set_len(len); objs.set_len(len); } @@ -100,7 +112,10 @@ pub trait INSDictionary: INSObject { } } - fn from_keys_and_objects(keys: &[&T], vals: Vec>) -> Id + fn from_keys_and_objects( + keys: &[&T], + vals: Vec>, + ) -> Id where T: INSCopying, { @@ -166,7 +181,7 @@ where #[cfg(test)] mod tests { use alloc::vec; - use objc2::rc::{Owned, Id}; + use objc2::rc::{Id, Owned}; use super::{INSDictionary, NSDictionary}; use crate::{INSArray, INSObject, INSString, NSObject, NSString}; diff --git a/objc2_foundation/src/enumerator.rs b/objc2_foundation/src/enumerator.rs index 8bd3fe9c9..b1d1fedfe 100644 --- a/objc2_foundation/src/enumerator.rs +++ b/objc2_foundation/src/enumerator.rs @@ -95,9 +95,12 @@ fn enumerate<'a, 'b: 'a, C: INSFastEnumeration>( let count: usize = unsafe { // Reborrow state so that we don't move it let state = &mut *state; - msg_send![object, countByEnumeratingWithState:state - objects:buf.as_mut_ptr() - count:buf.len()] + msg_send![ + object, + countByEnumeratingWithState: state, + objects: buf.as_mut_ptr(), + count: buf.len(), + ] }; if count > 0 { diff --git a/objc2_foundation/src/string.rs b/objc2_foundation/src/string.rs index 5e98a8b9d..ba7c3534a 100644 --- a/objc2_foundation/src/string.rs +++ b/objc2_foundation/src/string.rs @@ -63,9 +63,12 @@ pub trait INSString: INSObject { let bytes = string.as_ptr() as *const c_void; unsafe { let obj: *mut Self = msg_send![cls, alloc]; - let obj: *mut Self = msg_send![obj, initWithBytes:bytes - length:string.len() - encoding:UTF8_ENCODING]; + let obj: *mut Self = msg_send![ + obj, + initWithBytes: bytes, + length: string.len(), + encoding: UTF8_ENCODING, + ]; Id::new(NonNull::new_unchecked(obj)) } } diff --git a/objc2_foundation/src/value.rs b/objc2_foundation/src/value.rs index 9f844aa7e..339e0065d 100644 --- a/objc2_foundation/src/value.rs +++ b/objc2_foundation/src/value.rs @@ -43,8 +43,11 @@ pub trait INSValue: INSObject { let encoding = CString::new(Self::Value::ENCODING.to_string()).unwrap(); unsafe { let obj: *mut Self = msg_send![cls, alloc]; - let obj: *mut Self = msg_send![obj, initWithBytes:bytes - objCType:encoding.as_ptr()]; + let obj: *mut Self = msg_send![ + obj, + initWithBytes: bytes, + objCType: encoding.as_ptr(), + ]; Id::new(NonNull::new_unchecked(obj)) } } From 4cabdb95f1d9ffe355f8309afac7328759a81e8d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 1 Oct 2021 15:49:10 +0200 Subject: [PATCH 7/9] Fix exception feature (broken since #21) --- objc2/src/exception.rs | 10 ++++++---- objc2/src/message/mod.rs | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/objc2/src/exception.rs b/objc2/src/exception.rs index 50013c70c..2465e3aa0 100644 --- a/objc2/src/exception.rs +++ b/objc2/src/exception.rs @@ -1,8 +1,8 @@ use core::ptr::NonNull; -use crate::rc::Id; +use crate::rc::{Id, Shared}; use crate::runtime::Object; -use objc2_exception::{r#try, Exception}; +use objc2_exception::r#try; // Comment copied from `objc2_exception` @@ -21,6 +21,8 @@ use objc2_exception::{r#try, Exception}; /// undefined behaviour until `C-unwind` is stabilized, see [RFC-2945]. /// /// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html -pub unsafe fn catch_exception(closure: impl FnOnce() -> R) -> Result> { - r#try(closure).map_err(|e| Id::new(NonNull::new(e).unwrap())) +pub unsafe fn catch_exception( + closure: impl FnOnce() -> R, +) -> Result>> { + r#try(closure).map_err(|e| NonNull::new(e).map(|e| Id::new(e.cast()))) } diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index 7cfe530d0..a46041286 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -14,10 +14,10 @@ macro_rules! objc_try { ($b:block) => { $crate::exception::catch_exception(|| $b).map_err(|exception| { use alloc::borrow::ToOwned; - if exception.is_null() { - MessageError("Uncaught exception nil".to_owned()) + if let Some(exception) = exception { + MessageError(alloc::format!("Uncaught exception {:?}", exception)) } else { - MessageError(alloc::format!("Uncaught exception {:?}", &**exception)) + MessageError("Uncaught exception nil".to_owned()) } }) }; From 1b62354d5ea7f9bd80dc239d55a29ff8c9e95fca Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 3 Oct 2021 22:21:55 +0200 Subject: [PATCH 8/9] Remove ability to send messages to Option types We actually don't want to encourage sending messages to nil objects, since that is only supported for selectors that return pointers --- objc2/src/message/mod.rs | 46 ---------------------------------------- 1 file changed, 46 deletions(-) diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index a46041286..c91a56005 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -1,7 +1,6 @@ use alloc::string::{String, ToString}; use core::fmt; use core::mem; -use core::ptr; use core::ptr::NonNull; use std::error::Error; @@ -78,11 +77,6 @@ pub(crate) mod private { impl<'a, T: Message + ?Sized> Sealed for &'a mut T {} impl Sealed for NonNull {} impl Sealed for Id {} - - impl<'a, T: Message + ?Sized> Sealed for Option<&'a T> {} - impl<'a, T: Message + ?Sized> Sealed for Option<&'a mut T> {} - impl Sealed for Option> {} - impl Sealed for Option> {} } /// Types that can directly be used as the receiver of Objective-C messages. @@ -239,46 +233,6 @@ unsafe impl MessageReceiver for Id { } } -unsafe impl<'a, T: Message + ?Sized> MessageReceiver for Option<&'a T> { - #[inline] - fn as_raw_receiver(&self) -> *mut Object { - match self { - None => ptr::null_mut(), - Some(obj) => obj.as_raw_receiver(), - } - } -} - -unsafe impl<'a, T: Message + ?Sized> MessageReceiver for Option<&'a mut T> { - #[inline] - fn as_raw_receiver(&self) -> *mut Object { - match self { - None => ptr::null_mut(), - Some(obj) => obj.as_raw_receiver(), - } - } -} - -unsafe impl MessageReceiver for Option> { - #[inline] - fn as_raw_receiver(&self) -> *mut Object { - match self { - None => ptr::null_mut(), - Some(obj) => obj.as_raw_receiver(), - } - } -} - -unsafe impl MessageReceiver for Option> { - #[inline] - fn as_raw_receiver(&self) -> *mut Object { - match self { - None => ptr::null_mut(), - Some(id) => id.as_raw_receiver(), - } - } -} - /// Types that may be used as the arguments of an Objective-C message. pub trait MessageArguments: EncodeArguments { /// Invoke an [`Imp`] with the given object, selector, and arguments. From 18b26bac4eb341dc557e8ace283222eb1284fab6 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 3 Oct 2021 22:36:02 +0200 Subject: [PATCH 9/9] Convert internal objc_try! macro to a function --- objc2/src/message/apple/mod.rs | 8 +++++--- objc2/src/message/gnustep.rs | 6 +++--- objc2/src/message/mod.rs | 27 ++++++++++++--------------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/objc2/src/message/apple/mod.rs b/objc2/src/message/apple/mod.rs index d8bed3946..a243f8fa3 100644 --- a/objc2/src/message/apple/mod.rs +++ b/objc2/src/message/apple/mod.rs @@ -1,6 +1,6 @@ use objc2_sys::objc_super; -use super::{Encode, MessageArguments, MessageError}; +use super::{conditional_try, Encode, MessageArguments, MessageError}; use crate::runtime::{Class, Imp, Object, Sel}; #[cfg(target_arch = "x86")] @@ -23,6 +23,7 @@ trait MsgSendFn: Encode { const MSG_SEND_SUPER: Imp; } +#[inline(always)] pub unsafe fn send_unverified( receiver: *mut Object, sel: Sel, @@ -33,9 +34,10 @@ where R: Encode, { let msg_send_fn = R::MSG_SEND; - objc_try!({ A::invoke(msg_send_fn, receiver, sel, args) }) + conditional_try(|| A::invoke(msg_send_fn, receiver, sel, args)) } +#[inline] pub unsafe fn send_super_unverified( receiver: *mut Object, superclass: &Class, @@ -52,5 +54,5 @@ where }; let receiver = &sup as *const objc_super as *mut Object; let msg_send_fn = R::MSG_SEND_SUPER; - objc_try!({ A::invoke(msg_send_fn, receiver, sel, args) }) + conditional_try(|| A::invoke(msg_send_fn, receiver, sel, args)) } diff --git a/objc2/src/message/gnustep.rs b/objc2/src/message/gnustep.rs index ab50fd96a..0f8168107 100644 --- a/objc2/src/message/gnustep.rs +++ b/objc2/src/message/gnustep.rs @@ -1,7 +1,7 @@ use core::mem; use objc2_sys::{objc_msg_lookup, objc_msg_lookup_super, objc_super}; -use super::{Encode, MessageArguments, MessageError}; +use super::{conditional_try, Encode, MessageArguments, MessageError}; use crate::runtime::{Class, Object, Sel}; pub unsafe fn send_unverified( @@ -18,7 +18,7 @@ where } let msg_send_fn = objc_msg_lookup(receiver as *mut _, sel.as_ptr() as *const _); - objc_try!({ A::invoke(msg_send_fn.expect("Null IMP"), receiver, sel, args) }) + conditional_try(|| A::invoke(msg_send_fn.expect("Null IMP"), receiver, sel, args)) } pub unsafe fn send_super_unverified( @@ -36,5 +36,5 @@ where super_class: superclass as *const Class as *const _, }; let msg_send_fn = objc_msg_lookup_super(&sup, sel.as_ptr() as *const _); - objc_try!({ A::invoke(msg_send_fn.expect("Null IMP"), receiver, sel, args) }) + conditional_try(|| A::invoke(msg_send_fn.expect("Null IMP"), receiver, sel, args)) } diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index c91a56005..94daec505 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -9,24 +9,21 @@ use crate::runtime::{Class, Imp, Object, Sel}; use crate::{Encode, EncodeArguments, RefEncode}; #[cfg(feature = "exception")] -macro_rules! objc_try { - ($b:block) => { - $crate::exception::catch_exception(|| $b).map_err(|exception| { - use alloc::borrow::ToOwned; - if let Some(exception) = exception { - MessageError(alloc::format!("Uncaught exception {:?}", exception)) - } else { - MessageError("Uncaught exception nil".to_owned()) - } - }) - }; +unsafe fn conditional_try(f: impl FnOnce() -> R) -> Result { + use alloc::borrow::ToOwned; + crate::exception::catch_exception(f).map_err(|exception| { + if let Some(exception) = exception { + MessageError(alloc::format!("Uncaught exception {:?}", exception)) + } else { + MessageError("Uncaught exception nil".to_owned()) + } + }) } #[cfg(not(feature = "exception"))] -macro_rules! objc_try { - ($b:block) => { - Ok($b) - }; +#[inline(always)] +unsafe fn conditional_try(f: impl FnOnce() -> R) -> Result { + Ok(f()) } mod verify;