use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use core::{fmt, ptr};
use crate::__macro_helpers::declared_ivars::initialize_ivars;
use crate::runtime::{objc_release_fast, AnyObject};
use crate::{DeclaredClass, Message};
#[repr(transparent)]
#[derive(Debug)]
pub struct Allocated<T: ?Sized> {
ptr: *const T, p: PhantomData<T>,
p_auto_traits: PhantomData<AnyObject>,
}
impl<T: ?Sized + Message> Allocated<T> {
#[inline]
pub(crate) unsafe fn new(ptr: *mut T) -> Self {
Self {
ptr,
p: PhantomData,
p_auto_traits: PhantomData,
}
}
#[inline]
pub fn as_ptr(this: &Self) -> *const T {
this.ptr
}
#[inline]
#[allow(unknown_lints)] #[allow(clippy::needless_pass_by_ref_mut)]
pub fn as_mut_ptr(this: &mut Self) -> *mut T {
this.ptr as *mut T
}
#[inline]
pub(crate) fn into_ptr(this: Self) -> *mut T {
let this = ManuallyDrop::new(this);
this.ptr as *mut T
}
#[inline]
#[track_caller]
pub fn set_ivars(self, ivars: T::Ivars) -> PartialInit<T>
where
T: DeclaredClass + Sized,
{
if let Some(ptr) = NonNull::new(ManuallyDrop::new(self).ptr as *mut T) {
unsafe { initialize_ivars::<T>(ptr, ivars) };
unsafe { PartialInit::new(ptr.as_ptr()) }
} else if cfg!(debug_assertions) {
panic!("tried to initialize instance variables on a NULL allocated object")
} else {
drop(ivars);
unsafe { PartialInit::new(ptr::null_mut()) }
}
}
}
impl<T: ?Sized> Drop for Allocated<T> {
#[inline]
fn drop(&mut self) {
unsafe { objc_release_fast(self.ptr as *mut _) };
}
}
impl<T: ?Sized> fmt::Pointer for Allocated<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.ptr, f)
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct PartialInit<T: ?Sized> {
ptr: *const T, p: PhantomData<T>,
p_auto_traits: PhantomData<AnyObject>,
}
impl<T: ?Sized + Message> PartialInit<T> {
#[inline]
pub(crate) unsafe fn new(ptr: *mut T) -> Self {
Self {
ptr,
p: PhantomData,
p_auto_traits: PhantomData,
}
}
#[inline]
pub fn as_ptr(this: &Self) -> *const T {
this.ptr
}
#[inline]
#[allow(unknown_lints)] #[allow(clippy::needless_pass_by_ref_mut)]
pub fn as_mut_ptr(this: &mut Self) -> *mut T {
this.ptr as *mut T
}
#[inline]
pub(crate) fn into_ptr(this: Self) -> *mut T {
let this = ManuallyDrop::new(this);
this.ptr as *mut T
}
}
impl<T: ?Sized> Drop for PartialInit<T> {
#[inline]
fn drop(&mut self) {
unsafe { objc_release_fast(self.ptr as *mut _) };
}
}
impl<T: ?Sized> fmt::Pointer for PartialInit<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.ptr, f)
}
}
#[cfg(test)]
mod tests {
use core::panic::{RefUnwindSafe, UnwindSafe};
use static_assertions::assert_not_impl_any;
use super::*;
use crate::rc::RcTestObject;
use crate::runtime::NSObject;
#[test]
fn auto_traits() {
assert_not_impl_any!(Allocated<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin);
assert_not_impl_any!(PartialInit<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin);
}
#[repr(C)]
struct MyObject<'a> {
inner: NSObject,
p: PhantomData<&'a str>,
}
#[allow(unused)]
fn assert_allocated_variance<'b>(obj: Allocated<MyObject<'static>>) -> Allocated<MyObject<'b>> {
obj
}
#[allow(unused)]
fn assert_partialinit_variance<'b>(
obj: PartialInit<MyObject<'static>>,
) -> PartialInit<MyObject<'b>> {
obj
}
#[test]
#[cfg_attr(
debug_assertions,
should_panic = "tried to initialize instance variables on a NULL allocated object"
)]
fn test_set_ivars_null() {
let obj: Allocated<RcTestObject> = unsafe { Allocated::new(ptr::null_mut()) };
let _ = obj.set_ivars(());
}
}