use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use core::{fmt, ptr};
use crate::__macro_helpers::defined_ivars::initialize_ivars;
use crate::runtime::{objc_release_fast, AnyClass, AnyObject};
use crate::{DefinedClass, Message};
#[repr(transparent)]
#[derive(Debug)]
#[cfg_attr(
feature = "unstable-coerce-pointee",
derive(std::marker::CoercePointee)
)]
pub struct Allocated<T: ?Sized> {
ptr: *const T, p: PhantomData<T>,
p_auto_traits: PhantomData<AnyObject>,
}
#[cfg(feature = "unstable-arbitrary-self-types")]
impl<T: ?Sized> core::ops::Receiver for Allocated<T> {
type Target = T;
}
impl<T: ?Sized + Message> Allocated<T> {
#[inline]
pub(crate) unsafe fn new(ptr: *mut T) -> Self {
Self {
ptr,
p: PhantomData,
p_auto_traits: PhantomData,
}
}
#[doc(alias = "objc_alloc")]
#[inline]
pub(crate) unsafe fn alloc(cls: &AnyClass) -> Self
where
T: Sized,
{
#[cfg(all(
target_vendor = "apple",
not(all(target_os = "macos", target_arch = "x86"))
))]
{
let obj: *mut T = unsafe { crate::ffi::objc_alloc(cls).cast() };
unsafe { Self::new(obj) }
}
#[cfg(not(all(
target_vendor = "apple",
not(all(target_os = "macos", target_arch = "x86"))
)))]
{
unsafe { crate::msg_send![cls, alloc] }
}
}
#[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: DefinedClass + 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(());
}
#[test]
#[cfg(feature = "unstable-arbitrary-self-types")]
fn arbitrary_self_types() {
use crate::rc::Retained;
use crate::{extern_methods, AnyThread};
impl RcTestObject {
extern_methods!(
#[unsafe(method(init))]
fn init_with_self(self: Allocated<Self>) -> Retained<Self>;
);
}
let _ = RcTestObject::alloc().init_with_self();
}
}