use alloc::boxed::Box;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::ptr::NonNull;
use crate::encode::{EncodeConvert, Encoding};
use crate::rc::{Id, Ownership};
use crate::Message;
use super::InnerIvarType;
pub struct IvarDrop<T> {
p: PhantomData<T>,
}
impl<T: Sized> super::ivar::private::Sealed for IvarDrop<Box<T>> {}
unsafe impl<T: Sized> InnerIvarType for IvarDrop<Box<T>> {
const __ENCODING: Encoding = <*const c_void as EncodeConvert>::__ENCODING;
type __Inner = Option<Box<T>>;
type Output = Box<T>;
const __MAY_DROP: bool = true;
#[inline]
unsafe fn __to_ref(inner: &Self::__Inner) -> &Self::Output {
match inner {
Some(inner) => inner,
None => unsafe { box_unreachable() },
}
}
#[inline]
unsafe fn __to_mut(inner: &mut Self::__Inner) -> &mut Self::Output {
match inner {
Some(inner) => inner,
None => unsafe { box_unreachable() },
}
}
#[inline]
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
inner.cast()
}
}
impl<T: Sized> super::ivar::private::Sealed for IvarDrop<Option<Box<T>>> {}
unsafe impl<T: Sized> InnerIvarType for IvarDrop<Option<Box<T>>> {
const __ENCODING: Encoding = <*const c_void as EncodeConvert>::__ENCODING;
type __Inner = Option<Box<T>>;
type Output = Option<Box<T>>;
const __MAY_DROP: bool = true;
#[inline]
unsafe fn __to_ref(this: &Self::__Inner) -> &Self::Output {
this
}
#[inline]
unsafe fn __to_mut(this: &mut Self::__Inner) -> &mut Self::Output {
this
}
#[inline]
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
inner.cast()
}
}
impl<T: Message, O: Ownership> super::ivar::private::Sealed for IvarDrop<Id<T, O>> {}
unsafe impl<T: Message, O: Ownership> InnerIvarType for IvarDrop<Id<T, O>> {
const __ENCODING: Encoding = <*const T as EncodeConvert>::__ENCODING;
type __Inner = Option<Id<T, O>>;
type Output = Id<T, O>;
const __MAY_DROP: bool = true;
#[inline]
unsafe fn __to_ref(inner: &Self::__Inner) -> &Self::Output {
match inner {
Some(inner) => inner,
None => unsafe { id_unreachable() },
}
}
#[inline]
unsafe fn __to_mut(inner: &mut Self::__Inner) -> &mut Self::Output {
match inner {
Some(inner) => inner,
None => unsafe { id_unreachable() },
}
}
#[inline]
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
inner.cast()
}
}
impl<T: Message, O: Ownership> super::ivar::private::Sealed for IvarDrop<Option<Id<T, O>>> {}
unsafe impl<T: Message, O: Ownership> InnerIvarType for IvarDrop<Option<Id<T, O>>> {
const __ENCODING: Encoding = <*const T as EncodeConvert>::__ENCODING;
type __Inner = Option<Id<T, O>>;
type Output = Option<Id<T, O>>;
const __MAY_DROP: bool = true;
#[inline]
unsafe fn __to_ref(this: &Self::__Inner) -> &Self::Output {
this
}
#[inline]
unsafe fn __to_mut(this: &mut Self::__Inner) -> &mut Self::Output {
this
}
#[inline]
fn __to_ptr(inner: NonNull<Self::__Inner>) -> NonNull<Self::Output> {
inner.cast()
}
}
#[inline]
unsafe fn id_unreachable() -> ! {
#[cfg(debug_assertions)]
{
unreachable!("an Id in instance variables must always be initialized before use!")
}
#[cfg(not(debug_assertions))]
unsafe {
core::hint::unreachable_unchecked()
}
}
#[inline]
unsafe fn box_unreachable() -> ! {
#[cfg(debug_assertions)]
{
unreachable!("a Box in instance variables must always be initialized before use!")
}
#[cfg(not(debug_assertions))]
unsafe {
core::hint::unreachable_unchecked()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::declare::{Ivar, IvarType};
use crate::foundation::NSObject;
use crate::rc::{Allocated, Owned, RcTestObject, Shared, ThreadTestData};
use crate::runtime::Object;
use crate::{declare_class, msg_send, msg_send_id, ClassType};
struct TestIvar1;
unsafe impl IvarType for TestIvar1 {
type Type = IvarDrop<Box<u8>>;
const NAME: &'static str = "_abc";
}
struct TestIvar2;
unsafe impl IvarType for TestIvar2 {
type Type = IvarDrop<Option<Box<u8>>>;
const NAME: &'static str = "_abc";
}
struct TestIvar3;
unsafe impl IvarType for TestIvar3 {
type Type = IvarDrop<Id<Object, Shared>>;
const NAME: &'static str = "_abc";
}
struct TestIvar4;
unsafe impl IvarType for TestIvar4 {
type Type = IvarDrop<Option<Id<Object, Owned>>>;
const NAME: &'static str = "_abc";
}
declare_class!(
#[derive(Debug, PartialEq)]
struct IvarTester {
ivar1: IvarDrop<Id<RcTestObject, Shared>>,
ivar2: IvarDrop<Option<Id<RcTestObject, Owned>>>,
ivar3: IvarDrop<Box<Id<RcTestObject, Owned>>>,
ivar4: IvarDrop<Option<Box<Id<RcTestObject, Owned>>>>,
}
unsafe impl ClassType for IvarTester {
type Super = NSObject;
}
unsafe impl IvarTester {
#[sel(init)]
fn init(&mut self) -> Option<&mut Self> {
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
this.map(|this| {
Ivar::write(&mut this.ivar1, Id::into_shared(RcTestObject::new()));
*this.ivar2 = Some(RcTestObject::new());
Ivar::write(&mut this.ivar3, Box::new(RcTestObject::new()));
*this.ivar4 = Some(Box::new(RcTestObject::new()));
this
})
}
#[sel(initInvalid)]
fn init_invalid(&mut self) -> Option<&mut Self> {
unsafe { msg_send![super(self), init] }
}
}
);
#[test]
fn test_alloc_dealloc() {
let expected = ThreadTestData::current();
let obj: Id<Allocated<IvarTester>, Owned> =
unsafe { msg_send_id![IvarTester::class(), alloc] };
expected.assert_current();
drop(obj);
expected.assert_current();
}
#[test]
fn test_init_drop() {
let mut expected = ThreadTestData::current();
let mut obj: Id<IvarTester, Owned> = unsafe { msg_send_id![IvarTester::class(), new] };
expected.alloc += 4;
expected.init += 4;
expected.assert_current();
*obj.ivar1 = (*obj.ivar1).clone();
expected.retain += 1;
expected.release += 1;
expected.assert_current();
*obj.ivar2 = None;
expected.release += 1;
expected.dealloc += 1;
expected.assert_current();
drop(obj);
expected.release += 3;
expected.dealloc += 3;
expected.assert_current();
}
#[test]
#[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")]
#[should_panic = "an Id in instance variables must always be initialized before use"]
fn test_init_invalid_ref() {
let obj: Id<IvarTester, Owned> =
unsafe { msg_send_id![msg_send_id![IvarTester::class(), alloc], initInvalid] };
std::println!("{:?}", obj.ivar1);
}
#[test]
#[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")]
#[should_panic = "an Id in instance variables must always be initialized before use"]
fn test_init_invalid_mut() {
let mut obj: Id<IvarTester, Owned> =
unsafe { msg_send_id![msg_send_id![IvarTester::class(), alloc], initInvalid] };
*obj.ivar1 = RcTestObject::new().into();
}
}