diff --git a/objc2/examples/introspection.rs b/objc2/examples/introspection.rs index d3a3a4a00..fa878dd2b 100644 --- a/objc2/examples/introspection.rs +++ b/objc2/examples/introspection.rs @@ -20,7 +20,7 @@ fn main() { // Allocate an instance let obj: Id = unsafe { - let obj: Id = msg_send_id![cls, alloc].unwrap(); + let obj = msg_send_id![cls, alloc]; msg_send_id![obj, init].unwrap() }; println!("NSObject address: {:p}", obj); diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index 7b665f29c..f6c9bb61e 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -184,9 +184,10 @@ macro_rules! msg_send_id { const NAME: &[u8] = concat!($(stringify!($name), ':'),+).as_bytes(); $crate::msg_send_id!(@__inner $obj, NAME, $($name: $arg),+) }); - (@__inner $obj:expr, $name:ident, $($sel:tt)+) => {{ - const IS_INIT: bool = $crate::__starts_with_str($name, b"init"); - const IS_RETAINED: bool = { + (@__inner $obj:expr, $name:ident, $($rest:tt)+) => {{ + const ALLOC: bool = $crate::__starts_with_str($name, b"alloc"); + const INIT: bool = $crate::__starts_with_str($name, b"init"); + const RETAINED: bool = { $crate::__starts_with_str($name, b"alloc") || $crate::__starts_with_str($name, b"new") || $crate::__starts_with_str($name, b"copy") @@ -194,23 +195,14 @@ macro_rules! msg_send_id { || $crate::__starts_with_str($name, b"init") }; - ::std::println!("IS_INIT: {}", IS_INIT); - ::std::println!("IS_RETAINED: {}", IS_RETAINED); - - let result = if IS_INIT { - // TODO: Ensure `obj` is Id here - let obj = ::core::mem::ManuallyDrop::new($obj); - $crate::msg_send![obj, $($sel)+] - } else { - $crate::msg_send![$obj, $($sel)+] - }; - if IS_RETAINED { - $crate::rc::Id::new(result) - } else { - // All code between the `msg_send!` and the `retain_autoreleased` must - // be able to be optimized away for this to work. - $crate::rc::Id::retain_autoreleased(result) + use $crate::rc::{__MsgSendId, __Assert}; + let sel = $crate::sel!($($rest)+); + let result; + match <__Assert>::__send_message($obj, sel, ()) { + Err(s) => panic!("{}", s), + Ok(r) => result = r, } + result }}; } @@ -232,6 +224,17 @@ pub const fn __starts_with_str(haystack: &[u8], needle: &[u8]) -> bool { #[cfg(test)] mod tests { use super::*; + use crate::rc::{Id, Owned}; + use crate::runtime::Object; + + #[test] + fn test_macro() { + let cls = class!(NSObject); + let _obj: Id = unsafe { + let obj = msg_send_id![cls, alloc]; + msg_send_id![obj, init].unwrap() + }; + } #[test] fn test_starts_with_str() { diff --git a/objc2/src/rc/alloc.rs b/objc2/src/rc/alloc.rs index d0b618a32..341628112 100644 --- a/objc2/src/rc/alloc.rs +++ b/objc2/src/rc/alloc.rs @@ -1,4 +1,9 @@ -use crate::{Message, RefEncode}; +use core::mem::ManuallyDrop; + +use crate::runtime::{Class, Sel}; +use crate::{Message, MessageArguments, MessageError, MessageReceiver, RefEncode}; + +use super::{Id, Ownership}; /// TODO #[repr(transparent)] @@ -11,3 +16,96 @@ unsafe impl RefEncode for Allocated { } unsafe impl Message for Allocated {} + +// #[doc(hidden)] +// pub trait __MsgSendId { +// fn __prepare(t: T) -> U; +// } + +// impl +// __MsgSendId, O>, ManuallyDrop, O>>> for () +// { +// fn __prepare(t: Id, O>) -> ManuallyDrop, O>> { +// ManuallyDrop::new(t) +// } +// } + +// impl __MsgSendId for () { +// fn __prepare(t: T) -> T { +// t +// } +// } + +#[doc(hidden)] +pub struct __Assert {} + +#[doc(hidden)] +pub trait __MsgSendId { + unsafe fn __send_message( + obj: T, + sel: Sel, + args: A, + ) -> Result; +} + +impl __MsgSendId<&'_ Class, Id, O>> + for __Assert +{ + unsafe fn __send_message( + cls: &Class, + sel: Sel, + args: A, + ) -> Result, O>, MessageError> { + unsafe { + MessageReceiver::send_message(&cls, sel, args) + .map(|r| Id::new(r).expect("Failed allocating")) + } + } +} + +impl __MsgSendId, O>, Option>> + for __Assert +{ + unsafe fn __send_message( + obj: Id, O>, + sel: Sel, + args: A, + ) -> Result>, MessageError> { + let obj = ManuallyDrop::new(obj); + unsafe { MessageReceiver::send_message(&obj, sel, args).map(|r| Id::new(r)) } + } +} + +impl __MsgSendId>> + for __Assert +{ + unsafe fn __send_message( + obj: T, + sel: Sel, + args: A, + ) -> Result>, MessageError> { + unsafe { MessageReceiver::send_message(&obj, sel, args).map(|r| Id::new(r)) } + } +} + +impl __MsgSendId>> + for __Assert +{ + unsafe fn __send_message( + obj: T, + 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_message(&obj, sel, args).map(|r| Id::retain_autoreleased(r)) + } + } +} + +// impl __MsgSendId for __Assert { +// fn __prepare(t: T) -> T { +// t +// } +// } diff --git a/objc2/src/rc/mod.rs b/objc2/src/rc/mod.rs index 73a08576b..73484af6b 100644 --- a/objc2/src/rc/mod.rs +++ b/objc2/src/rc/mod.rs @@ -63,7 +63,7 @@ mod id_traits; mod ownership; mod weak_id; -pub use self::alloc::Allocated; +pub use self::alloc::{Allocated, __Assert, __MsgSendId}; pub use self::autorelease::{autoreleasepool, AutoreleasePool, AutoreleaseSafe}; pub use self::id::Id; pub use self::id_traits::{DefaultId, SliceId, SliceIdMut};