diff --git a/objc2/src/__macro_helpers.rs b/objc2/src/__macro_helpers.rs index e8a25c457..19929beae 100644 --- a/objc2/src/__macro_helpers.rs +++ b/objc2/src/__macro_helpers.rs @@ -1,3 +1,5 @@ +use core::mem::ManuallyDrop; + use crate::rc::{Id, Ownership}; use crate::runtime::{Class, Sel}; use crate::{Message, MessageArguments, MessageError, MessageReceiver}; @@ -49,6 +51,16 @@ pub trait MsgSendId { ) -> Result, MessageError>; } +#[doc(hidden)] +pub trait MsgSendSuperId { + unsafe fn send_super_message_id( + obj: T, + superclass: &Class, + sel: Sel, + args: A, + ) -> Result, MessageError>; +} + // `new` impl MsgSendId<&'_ Class, Id> for RetainSemantics @@ -63,6 +75,23 @@ impl MsgSendId<&'_ Class, Id> } } +// Super: `new`, TODO: Can this ever happen? +impl MsgSendSuperId<&'_ Class, Id> + for RetainSemantics +{ + #[inline(always)] + unsafe fn send_super_message_id( + obj: &Class, + superclass: &Class, + sel: Sel, + args: A, + ) -> Result>, MessageError> { + unsafe { + MessageReceiver::send_super_message(obj, superclass, sel, args).map(|r| Id::new(r)) + } + } +} + // `alloc`, should mark the return value as "allocated, not initialized" somehow impl MsgSendId<&'_ Class, Id> for RetainSemantics @@ -77,6 +106,23 @@ impl MsgSendId<&'_ Class, Id> } } +// Super: `alloc`, TODO: Can this ever happen? +impl MsgSendSuperId<&'_ Class, Id> + for RetainSemantics +{ + #[inline(always)] + unsafe fn send_super_message_id( + cls: &Class, + superclass: &Class, + sel: Sel, + args: A, + ) -> Result>, MessageError> { + unsafe { + MessageReceiver::send_super_message(cls, superclass, sel, args).map(|r| Id::new(r)) + } + } +} + // `init`, should mark the input value as "allocated, not initialized" somehow impl MsgSendId>, Id> for RetainSemantics @@ -98,6 +144,27 @@ impl MsgSendId>, Id> } } +// Super: `init`. Takes a non-null object and returns a non-initialized object +// +// Should theoretically make it easy to use in declared `init` methods, once +// they've received an ergonomics overhaul +impl MsgSendSuperId, Id> + for RetainSemantics +{ + #[inline(always)] + unsafe fn send_super_message_id( + obj: Id, + superclass: &Class, + sel: Sel, + args: A, + ) -> Result>, MessageError> { + let ptr = Id::consume_as_ptr(ManuallyDrop::new(obj)); + unsafe { + MessageReceiver::send_super_message(ptr, superclass, sel, args).map(|r| Id::new(r)) + } + } +} + // `copy` and `mutableCopy` impl MsgSendId> for RetainSemantics @@ -112,6 +179,23 @@ impl MsgSendId MsgSendSuperId> + for RetainSemantics +{ + #[inline(always)] + unsafe fn send_super_message_id( + obj: T, + superclass: &Class, + sel: Sel, + args: A, + ) -> Result>, MessageError> { + unsafe { + MessageReceiver::send_super_message(obj, superclass, sel, args).map(|r| Id::new(r)) + } + } +} + // All other selectors impl MsgSendId> for RetainSemantics @@ -128,6 +212,26 @@ impl MsgSendId> } } +// Super: All other selectors +impl MsgSendSuperId> + for RetainSemantics +{ + #[inline(always)] + unsafe fn send_super_message_id( + obj: T, + superclass: &Class, + sel: Sel, + args: A, + ) -> Result>, MessageError> { + // All code between the message send and the `retain_autoreleased` + // must be able to be optimized away for this to work. + unsafe { + MessageReceiver::send_super_message(obj, superclass, sel, args) + .map(|r| Id::retain_autoreleased(r)) + } + } +} + /// Checks whether a given selector is said to be in a given selector family. /// /// @@ -280,6 +384,30 @@ mod tests { expected.assert_current(); } + #[test] + #[ignore = "TMP"] + fn test_msg_send_super_id() { + // We send the messages to the class itself instead of it's actual + // superclass, just to verify that the macro works. + // TODO: Better solution! + let cls = class!(NSObject); + + let _obj: Id = unsafe { msg_send_id![super(cls, cls), new].unwrap() }; + + let obj = unsafe { msg_send_id![super(cls, cls), alloc] }; + + let obj = obj.unwrap(); // Required on super + let obj: Id = unsafe { msg_send_id![super(obj, cls), init].unwrap() }; + + let _copy: Id = unsafe { msg_send_id![super(&obj, cls), copy].unwrap() }; + + let _mutable_copy: Id = + unsafe { msg_send_id![super(&obj, cls), mutableCopy].unwrap() }; + + let _desc: Option> = + unsafe { msg_send_id![super(&obj, cls), description] }; + } + #[test] fn test_in_selector_family() { // Common cases diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index 86509da17..43cc2ae96 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -384,6 +384,29 @@ macro_rules! msg_send_bool { /// ``` #[macro_export] macro_rules! msg_send_id { + [super($obj:expr, $superclass:expr), $selector:ident $(,)?] => ({ + $crate::__msg_send_id_helper!(@verify $selector); + let sel = $crate::sel!($selector); + const NAME: &[u8] = stringify!($selector).as_bytes(); + $crate::__msg_send_id_helper!(@get_assert_consts NAME); + let result: Option<$crate::rc::Id<_, _>>; + match >::send_super_message_id($obj, $superclass, sel, ()) { + Err(s) => panic!("{}", s), + Ok(r) => result = r, + } + result + }); + [super($obj:expr, $superclass:expr), $($selector:ident : $argument:expr),+ $(,)?] => ({ + let sel = $crate::sel!($($selector:)+); + const NAME: &[u8] = concat!($(stringify!($selector), ':'),+).as_bytes(); + $crate::__msg_send_id_helper!(@get_assert_consts NAME); + let result: Option<$crate::rc::Id<_, _>>; + match >::send_super_message_id($obj, $superclass, sel, ($($argument,)+)) { + Err(s) => panic!("{}", s), + Ok(r) => result = r, + } + result + }); [$obj:expr, $selector:ident $(,)?] => ({ $crate::__msg_send_id_helper!(@verify $selector); let sel = $crate::sel!($selector);