use crate::encode::{EncodeArgument, EncodeArguments, EncodeReturn}; use crate::rc::{Allocated, Retained}; use crate::runtime::{AnyObject, Bool, Sel}; use crate::Message; mod argument_private { pub trait Sealed {} } /// Represents types that can be converted to/from an [`EncodeArgument`] type. /// /// This is implemented specially for [`bool`] to allow using that as /// Objective-C `BOOL`, where it would otherwise not be allowed (since they /// are not ABI compatible). /// /// This is also done specially for `&mut Retained<_>`-like arguments, to /// allow using those as "out" / pass-by-writeback parameters. pub trait ConvertArgument: argument_private::Sealed { /// The inner type that this can be converted to and from. #[doc(hidden)] type __Inner: EncodeArgument; /// A helper type for out parameters. /// /// When dropped, this will process any necessary change to the /// parameters. #[doc(hidden)] type __WritebackOnDrop: Sized; #[doc(hidden)] fn __from_defined_param(inner: Self::__Inner) -> Self; /// # Safety /// /// The `__WritebackOnDrop` return type must not be leaked, and the /// `__Inner` pointer must not be used after the `__WritebackOnDrop` has /// dropped. /// /// NOTE: The standard way to ensure such a thing is with closures, but /// using those would interact poorly with backtraces of the message send, /// so we're forced to ensure this out of band. #[doc(hidden)] unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop); } // Implemented in writeback.rs impl argument_private::Sealed for &mut Retained {} impl argument_private::Sealed for Option<&mut Retained> {} impl argument_private::Sealed for &mut Option> {} impl argument_private::Sealed for Option<&mut Option>> {} impl argument_private::Sealed for T {} impl ConvertArgument for T { type __Inner = Self; type __WritebackOnDrop = (); #[inline] fn __from_defined_param(inner: Self::__Inner) -> Self { inner } #[inline] unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) { (self, ()) } } impl argument_private::Sealed for bool {} impl ConvertArgument for bool { type __Inner = Bool; type __WritebackOnDrop = (); #[inline] fn __from_defined_param(inner: Self::__Inner) -> Self { inner.as_bool() } #[inline] unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) { (Bool::new(self), ()) } } mod return_private { pub trait Sealed {} } /// Same as [`ConvertArgument`], but for return types. /// /// See `RetainSemantics` for more details. pub trait ConvertReturn: return_private::Sealed { type Inner: EncodeReturn; #[track_caller] unsafe fn convert_message_return( inner: Self::Inner, receiver_ptr: *mut AnyObject, sel: Sel, ) -> Self; fn convert_defined_return(self) -> Self::Inner; } impl return_private::Sealed for T {} impl ConvertReturn for T { type Inner = Self; #[inline] unsafe fn convert_message_return( inner: Self::Inner, _receiver_ptr: *mut AnyObject, _sel: Sel, ) -> Self { inner } #[inline] fn convert_defined_return(self) -> Self::Inner { self } } impl return_private::Sealed for bool {} impl ConvertReturn for bool { type Inner = Bool; #[inline] unsafe fn convert_message_return( inner: Self::Inner, _receiver_ptr: *mut AnyObject, _sel: Sel, ) -> Self { inner.as_bool() } #[inline] fn convert_defined_return(self) -> Self::Inner { Bool::new(self) } } // Implemented in retain_semantics.rs impl return_private::Sealed for Retained {} impl return_private::Sealed for Option> {} impl return_private::Sealed for Allocated {} pub trait ConvertArguments { #[doc(hidden)] type __Inner: EncodeArguments; #[doc(hidden)] type __WritebackOnDrop: Sized; #[doc(hidden)] unsafe fn __into_arguments(self) -> (Self::__Inner, Self::__WritebackOnDrop); } pub trait TupleExtender { #[doc(hidden)] type PlusOneArgument; #[doc(hidden)] fn add_argument(self, arg: T) -> Self::PlusOneArgument; } macro_rules! args_impl { ($($a:ident: $t:ident),*) => ( impl<$($t: ConvertArgument),*> ConvertArguments for ($($t,)*) { type __Inner = ($($t::__Inner,)*); type __WritebackOnDrop = ($($t::__WritebackOnDrop,)*); #[inline] unsafe fn __into_arguments(self) -> (Self::__Inner, Self::__WritebackOnDrop) { let ($($a,)*) = self; // SAFETY: Upheld by caller $(let $a = unsafe { ConvertArgument::__into_argument($a) };)* (($($a.0,)*), ($($a.1,)*)) } } impl<$($t,)* T> TupleExtender for ($($t,)*) { type PlusOneArgument = ($($t,)* T,); #[inline] fn add_argument(self, arg: T) -> Self::PlusOneArgument { let ($($a,)*) = self; ($($a,)* arg,) } } ); } args_impl!(); args_impl!(a: A); args_impl!(a: A, b: B); args_impl!(a: A, b: B, c: C); args_impl!(a: A, b: B, c: C, d: D); args_impl!(a: A, b: B, c: C, d: D, e: E); args_impl!(a: A, b: B, c: C, d: D, e: E, f: F); args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G); args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H); args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I); args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J); args_impl!( a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K ); args_impl!( a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L ); args_impl!( a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M ); args_impl!( a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N ); args_impl!( a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O ); args_impl!( a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L, m: M, n: N, o: O, p: P ); #[cfg(test)] mod tests { use super::*; use core::any::TypeId; use core::ptr; use crate::sel; #[test] fn convert_normally_noop() { assert_eq!( TypeId::of::<::__Inner>(), TypeId::of::() ); assert_eq!(::__from_defined_param(42), 42); assert_eq!(unsafe { ConvertArgument::__into_argument(42i32).0 }, 42); } #[test] fn convert_i8() { assert_eq!( TypeId::of::<::__Inner>(), TypeId::of::() ); assert_eq!(::__from_defined_param(-3), -3); assert_eq!(unsafe { ConvertArgument::__into_argument(-3i32).0 }, -3); } #[test] fn convert_bool() { let receiver_ptr = ptr::null_mut::(); let sel = sel!(foo); assert!(!::__from_defined_param(Bool::NO)); assert!(::__from_defined_param(Bool::YES)); assert!(!unsafe { >::convert_message_return(Bool::NO, receiver_ptr, sel) }); assert!(unsafe { >::convert_message_return(Bool::YES, receiver_ptr, sel) }); assert!(!unsafe { ConvertArgument::__into_argument(false).0 }.as_bool()); assert!(unsafe { ConvertArgument::__into_argument(true).0 }.as_bool()); assert!(!ConvertReturn::<()>::convert_defined_return(false).as_bool()); assert!(ConvertReturn::<()>::convert_defined_return(true).as_bool()); #[cfg(all(target_vendor = "apple", target_os = "macos", target_arch = "x86_64"))] assert_eq!( ::__Inner::ENCODING_ARGUMENT, crate::encode::Encoding::Char, ); } }