Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support super in msg_send_id! #173

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions objc2/src/__macro_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::mem::ManuallyDrop;

use crate::rc::{Id, Ownership};
use crate::runtime::{Class, Sel};
use crate::{Message, MessageArguments, MessageError, MessageReceiver};
Expand Down Expand Up @@ -49,6 +51,16 @@ pub trait MsgSendId<T, U> {
) -> Result<Option<U>, MessageError>;
}

#[doc(hidden)]
pub trait MsgSendSuperId<T, U> {
unsafe fn send_super_message_id<A: MessageArguments>(
obj: T,
superclass: &Class,
sel: Sel,
args: A,
) -> Result<Option<U>, MessageError>;
}

// `new`
impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Id<T, O>>
for RetainSemantics<true, false, false, false>
Expand All @@ -63,6 +75,23 @@ impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Id<T, O>>
}
}

// Super: `new`, TODO: Can this ever happen?
impl<T: ?Sized + Message, O: Ownership> MsgSendSuperId<&'_ Class, Id<T, O>>
for RetainSemantics<true, false, false, false>
{
#[inline(always)]
unsafe fn send_super_message_id<A: MessageArguments>(
obj: &Class,
superclass: &Class,
sel: Sel,
args: A,
) -> Result<Option<Id<T, O>>, 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<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Id<T, O>>
for RetainSemantics<false, true, false, false>
Expand All @@ -77,6 +106,23 @@ impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Id<T, O>>
}
}

// Super: `alloc`, TODO: Can this ever happen?
impl<T: ?Sized + Message, O: Ownership> MsgSendSuperId<&'_ Class, Id<T, O>>
for RetainSemantics<false, true, false, false>
{
#[inline(always)]
unsafe fn send_super_message_id<A: MessageArguments>(
cls: &Class,
superclass: &Class,
sel: Sel,
args: A,
) -> Result<Option<Id<T, O>>, 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<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Id<T, O>>, Id<T, O>>
for RetainSemantics<false, false, true, false>
Expand All @@ -98,6 +144,27 @@ impl<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Id<T, O>>, Id<T, O>>
}
}

// 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<T: ?Sized + Message, O: Ownership> MsgSendSuperId<Id<T, O>, Id<T, O>>
for RetainSemantics<false, false, true, false>
{
#[inline(always)]
unsafe fn send_super_message_id<A: MessageArguments>(
obj: Id<T, O>,
superclass: &Class,
sel: Sel,
args: A,
) -> Result<Option<Id<T, O>>, 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<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, Id<U, O>>
for RetainSemantics<false, false, false, true>
Expand All @@ -112,6 +179,23 @@ impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, Id<U, O
}
}

// Super: `copy` and `mutableCopy`. TODO: Will this ever happen?
impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendSuperId<T, Id<U, O>>
for RetainSemantics<false, false, false, true>
{
#[inline(always)]
unsafe fn send_super_message_id<A: MessageArguments>(
obj: T,
superclass: &Class,
sel: Sel,
args: A,
) -> Result<Option<Id<U, O>>, MessageError> {
unsafe {
MessageReceiver::send_super_message(obj, superclass, sel, args).map(|r| Id::new(r))
}
}
}

// All other selectors
impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, Id<U, O>>
for RetainSemantics<false, false, false, false>
Expand All @@ -128,6 +212,26 @@ impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, Id<U, O>>
}
}

// Super: All other selectors
impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendSuperId<T, Id<U, O>>
for RetainSemantics<false, false, false, false>
{
#[inline(always)]
unsafe fn send_super_message_id<A: MessageArguments>(
obj: T,
superclass: &Class,
sel: Sel,
args: A,
) -> Result<Option<Id<U, O>>, 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.
///
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families>
Expand Down Expand Up @@ -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<Object, Owned> = 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<Object, Owned> = unsafe { msg_send_id![super(obj, cls), init].unwrap() };

let _copy: Id<Object, Shared> = unsafe { msg_send_id![super(&obj, cls), copy].unwrap() };

let _mutable_copy: Id<Object, Shared> =
unsafe { msg_send_id![super(&obj, cls), mutableCopy].unwrap() };

let _desc: Option<Id<Object, Shared>> =
unsafe { msg_send_id![super(&obj, cls), description] };
}

#[test]
fn test_in_selector_family() {
// Common cases
Expand Down
23 changes: 23 additions & 0 deletions objc2/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <RS as $crate::__macro_helpers::MsgSendSuperId<_, _>>::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 <RS as $crate::__macro_helpers::MsgSendSuperId<_, _>>::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);
Expand Down