Skip to content

Commit

Permalink
W. trait
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Apr 3, 2022
1 parent 5822a7a commit 8770954
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 22 deletions.
2 changes: 1 addition & 1 deletion objc2/examples/introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn main() {

// Allocate an instance
let obj: Id<Object, Owned> = unsafe {
let obj: Id<Object, Owned> = msg_send_id![cls, alloc].unwrap();
let obj = msg_send_id![cls, alloc];
msg_send_id![obj, init].unwrap()
};
println!("NSObject address: {:p}", obj);
Expand Down
41 changes: 22 additions & 19 deletions objc2/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,33 +184,25 @@ 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")
|| $crate::__starts_with_str($name, b"mutableCopy")
|| $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<ALLOC, INIT, RETAINED>>::__send_message($obj, sel, ()) {
Err(s) => panic!("{}", s),
Ok(r) => result = r,
}
result
}};
}

Expand All @@ -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<Object, Owned> = unsafe {
let obj = msg_send_id![cls, alloc];
msg_send_id![obj, init].unwrap()
};
}

#[test]
fn test_starts_with_str() {
Expand Down
100 changes: 99 additions & 1 deletion objc2/src/rc/alloc.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -11,3 +16,96 @@ unsafe impl<T: ?Sized + RefEncode> RefEncode for Allocated<T> {
}

unsafe impl<T: ?Sized + Message> Message for Allocated<T> {}

// #[doc(hidden)]
// pub trait __MsgSendId<const IS_INIT: bool, T, U> {
// fn __prepare(t: T) -> U;
// }

// impl<T: ?Sized, O: Ownership>
// __MsgSendId<true, Id<Allocated<T>, O>, ManuallyDrop<Id<Allocated<T>, O>>> for ()
// {
// fn __prepare(t: Id<Allocated<T>, O>) -> ManuallyDrop<Id<Allocated<T>, O>> {
// ManuallyDrop::new(t)
// }
// }

// impl<T> __MsgSendId<false, T, T> for () {
// fn __prepare(t: T) -> T {
// t
// }
// }

#[doc(hidden)]
pub struct __Assert<const ALLOC: bool, const INIT: bool, const RETAINED: bool> {}

#[doc(hidden)]
pub trait __MsgSendId<T, U> {
unsafe fn __send_message<A: MessageArguments>(
obj: T,
sel: Sel,
args: A,
) -> Result<U, MessageError>;
}

impl<T: ?Sized + Message, O: Ownership> __MsgSendId<&'_ Class, Id<Allocated<T>, O>>
for __Assert<true, false, true>
{
unsafe fn __send_message<A: MessageArguments>(
cls: &Class,
sel: Sel,
args: A,
) -> Result<Id<Allocated<T>, O>, MessageError> {
unsafe {
MessageReceiver::send_message(&cls, sel, args)
.map(|r| Id::new(r).expect("Failed allocating"))
}
}
}

impl<T: ?Sized + Message, O: Ownership> __MsgSendId<Id<Allocated<T>, O>, Option<Id<T, O>>>
for __Assert<false, true, true>
{
unsafe fn __send_message<A: MessageArguments>(
obj: Id<Allocated<T>, O>,
sel: Sel,
args: A,
) -> Result<Option<Id<T, O>>, MessageError> {
let obj = ManuallyDrop::new(obj);
unsafe { MessageReceiver::send_message(&obj, sel, args).map(|r| Id::new(r)) }
}
}

impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> __MsgSendId<T, Option<Id<U, O>>>
for __Assert<false, false, true>
{
unsafe fn __send_message<A: MessageArguments>(
obj: T,
sel: Sel,
args: A,
) -> Result<Option<Id<U, O>>, MessageError> {
unsafe { MessageReceiver::send_message(&obj, sel, args).map(|r| Id::new(r)) }
}
}

impl<T: MessageReceiver, U: Message, O: Ownership> __MsgSendId<T, Option<Id<U, O>>>
for __Assert<false, false, false>
{
unsafe fn __send_message<A: MessageArguments>(
obj: T,
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_message(&obj, sel, args).map(|r| Id::retain_autoreleased(r))
}
}
}

// impl<T> __MsgSendId<T, T> for __Assert<false> {
// fn __prepare(t: T) -> T {
// t
// }
// }
2 changes: 1 addition & 1 deletion objc2/src/rc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down

0 comments on commit 8770954

Please sign in to comment.