Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

33
vendor/objc2/src/__framework_prelude.rs vendored Normal file
View File

@@ -0,0 +1,33 @@
//! Helper import prelude for framework crates.
// Note: While this is not public, it is still a breaking change to remove
// entries in here, since framework crates rely on it.
pub use core::ffi::c_void;
pub use core::marker::PhantomData;
pub use core::ptr::NonNull;
pub use std::os::raw::{
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
c_ulong, c_ulonglong, c_ushort,
};
pub use crate::encode::{Encode, Encoding, RefEncode};
pub use crate::ffi::{NSInteger, NSIntegerMax, NSUInteger, NSUIntegerMax, IMP};
pub use crate::mutability::{
Immutable, ImmutableWithMutableSubclass, InteriorMutable, IsIdCloneable, IsMainThreadOnly,
IsRetainable, MainThreadOnly, Mutable, MutableWithImmutableSuperclass,
};
pub use crate::rc::{Allocated, DefaultId, DefaultRetained, Id, Retained};
pub use crate::runtime::{
AnyClass, AnyObject, Bool, NSObject, NSObjectProtocol, ProtocolObject, Sel,
};
pub use crate::{
__inner_extern_class, extern_category, extern_class, extern_methods, extern_protocol,
ClassType, Message, ProtocolType,
};
// TODO
pub type AnyProtocol = AnyObject;
pub type TodoFunction = *const c_void;
pub type TodoClass = AnyObject;
pub type TodoProtocols = AnyObject;

View File

@@ -0,0 +1,114 @@
use core::ptr;
use core::str;
use core::sync::atomic::{AtomicPtr, Ordering};
use std::ffi::CStr;
use std::os::raw::c_char;
use crate::ffi;
use crate::runtime::{AnyClass, Sel};
/// Allows storing a [`Sel`] in a static and lazily loading it.
#[derive(Debug)]
pub struct CachedSel {
ptr: AtomicPtr<ffi::objc_selector>,
}
impl CachedSel {
/// Constructs a new [`CachedSel`].
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
ptr: AtomicPtr::new(ptr::null_mut()),
}
}
// Mark as cold since this should only ever be called once (or maybe twice
// if running on multiple threads).
#[cold]
unsafe fn fetch(&self, name: *const c_char) -> Sel {
// The panic inside `Sel::register_unchecked` is unfortunate, but
// strict correctness is more important than speed
// SAFETY: Input is a non-null, NUL-terminated C-string pointer.
//
// We know this, because we construct it in `sel!` ourselves
let sel = unsafe { Sel::register_unchecked(name) };
self.ptr
.store(sel.as_ptr() as *mut ffi::objc_selector, Ordering::Relaxed);
sel
}
/// Returns the cached selector. If no selector is yet cached, registers
/// one with the given name and stores it.
#[inline]
pub unsafe fn get(&self, name: &str) -> Sel {
// `Relaxed` should be fine since `sel_registerName` is thread-safe.
let ptr = self.ptr.load(Ordering::Relaxed);
if let Some(sel) = unsafe { Sel::from_ptr(ptr) } {
sel
} else {
// SAFETY: Checked by caller
unsafe { self.fetch(name.as_ptr().cast()) }
}
}
}
/// Allows storing a [`AnyClass`] reference in a static and lazily loading it.
#[derive(Debug)]
pub struct CachedClass {
ptr: AtomicPtr<AnyClass>,
}
impl CachedClass {
/// Constructs a new [`CachedClass`].
#[allow(clippy::new_without_default)]
pub const fn new() -> CachedClass {
CachedClass {
ptr: AtomicPtr::new(ptr::null_mut()),
}
}
// Mark as cold since this should only ever be called once (or maybe twice
// if running on multiple threads).
#[cold]
#[track_caller]
unsafe fn fetch(&self, name: *const c_char) -> &'static AnyClass {
let ptr: *const AnyClass = unsafe { ffi::objc_getClass(name) }.cast();
self.ptr.store(ptr as *mut AnyClass, Ordering::Relaxed);
if let Some(cls) = unsafe { ptr.as_ref() } {
cls
} else {
// Recover the name from the pointer. We do it like this so that
// we don't have to pass the length of the class to this method,
// improving binary size.
let name = unsafe { CStr::from_ptr(name) };
let name = str::from_utf8(name.to_bytes()).unwrap();
panic!("class {name} could not be found")
}
}
/// Returns the cached class. If no class is yet cached, gets one with
/// the given name and stores it.
#[inline]
#[track_caller]
pub unsafe fn get(&self, name: &str) -> &'static AnyClass {
// `Relaxed` should be fine since `objc_getClass` is thread-safe.
let ptr = self.ptr.load(Ordering::Relaxed);
if let Some(cls) = unsafe { ptr.as_ref() } {
cls
} else {
// SAFETY: Checked by caller
unsafe { self.fetch(name.as_ptr().cast()) }
}
}
}
#[cfg(test)]
mod tests {
#[test]
#[should_panic = "class NonExistantClass could not be found"]
#[cfg(not(feature = "unstable-static-class"))]
fn test_not_found() {
let _ = crate::class!(NonExistantClass);
}
}

View File

@@ -0,0 +1,117 @@
//! Common selectors.
//!
//! These are put here to deduplicate the cached selector, and when using
//! `unstable-static-sel`, the statics.
//!
//! Note that our assembly tests of `unstable-static-sel-inlined` output a GOT
//! entry for such accesses, but that is just a limitation of our tests - the
//! actual assembly is as one would expect.
use crate::runtime::Sel;
use crate::{__sel_data, __sel_inner};
#[inline]
pub fn alloc_sel() -> Sel {
__sel_inner!(__sel_data!(alloc), "alloc")
}
#[inline]
pub fn init_sel() -> Sel {
__sel_inner!(__sel_data!(init), "init")
}
#[inline]
pub fn new_sel() -> Sel {
__sel_inner!(__sel_data!(new), "new")
}
#[inline]
pub fn dealloc_sel() -> Sel {
__sel_inner!(__sel_data!(dealloc), "dealloc")
}
/// An undocumented selector called by the Objective-C runtime when
/// initalizing instance variables.
#[inline]
#[allow(dead_code)] // May be useful in the future
fn cxx_construct_sel() -> Sel {
__sel_inner!(".cxx_construct\0", ".cxx_construct")
}
/// Objective-C runtimes call `.cxx_destruct` as part of the final `dealloc`
/// call inside `NSObject` (and has done so since macOS 10.4).
///
/// While [GCC does document it somewhat][gcc-docs], this is still severely
/// undocumented in clang - but since the selector is emitted into the final
/// binary, it is fine to rely on it being used.
///
/// Unfortunately though, this only works if the class has been declared
/// statically, since in that case a flag is set to inform the runtime that it
/// needs to run destructors. So unfortunately we can't use this on
/// dynamically declared classes.
///
///
/// # ABI
///
/// The ABI of `.cxx_destruct` in Apple's runtime is actually that it does NOT
/// take a selector, unlike every other Objective-C method, see:
/// <https://github.com/apple-oss-distributions/objc4/blob/objc4-906/runtime/objc-class.mm#L457>
///
/// So the signature is `extern "C" fn(*mut AnyObject)`.
///
/// This is likely because it's not a real Objective-C method that can be
/// called from userspace / objc_msgSend, and it's more efficient to not pass
/// the selector.
///
/// Note that even if Apple decides to suddenly add the selector one day,
/// ignoring it will still be sound, since the function uses the C calling
/// convention, where such an ignored parameter would be allowed on all
/// relevant architectures.
///
/// TODO: Unsure whether "C-unwind" is allowed?
///
/// [gcc-docs]: https://gcc.gnu.org/onlinedocs/gcc/Objective-C-and-Objective-C_002b_002b-Dialect-Options.html#index-fobjc-call-cxx-cdtors
#[inline]
#[allow(dead_code)] // May be useful in the future
fn cxx_destruct_sel() -> Sel {
__sel_inner!(".cxx_destruct\0", ".cxx_destruct")
}
#[cfg(test)]
mod tests {
use core::sync::atomic::{AtomicBool, Ordering};
use crate::rc::Retained;
use crate::runtime::ClassBuilder;
use crate::runtime::NSObject;
use crate::{msg_send_id, ClassType};
use super::*;
/// Test the unfortunate fact that we can't use .cxx_destruct on dynamic classes.
#[test]
fn test_destruct_dynamic() {
static HAS_RUN: AtomicBool = AtomicBool::new(false);
let mut builder = ClassBuilder::new("TestCxxDestruct", NSObject::class()).unwrap();
unsafe extern "C" fn destruct(_: *mut NSObject, _: Sel) {
HAS_RUN.store(true, Ordering::Relaxed);
}
// Note: The ABI is not upheld here, but its fine for this test
unsafe { builder.add_method(cxx_destruct_sel(), destruct as unsafe extern "C" fn(_, _)) };
let cls = builder.register();
let obj: Retained<NSObject> = unsafe { msg_send_id![cls, new] };
drop(obj);
let has_run_destruct = HAS_RUN.load(Ordering::Relaxed);
// This does work on GNUStep, but unfortunately not in Apple's objc4
if cfg!(feature = "gnustep-1-7") {
assert!(has_run_destruct);
} else {
assert!(!has_run_destruct);
}
}
}

View File

@@ -0,0 +1,330 @@
use crate::encode::{EncodeArgument, EncodeArguments, EncodeReturn};
use crate::rc::Retained;
use crate::runtime::Bool;
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" 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.
#[doc(hidden)]
type __StoredBeforeMessage: Sized;
#[doc(hidden)]
fn __from_declared_param(inner: Self::__Inner) -> Self;
#[doc(hidden)]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage);
#[doc(hidden)]
#[inline]
unsafe fn __process_after_message_send(_stored: Self::__StoredBeforeMessage) {}
}
// Implemented in writeback.rs
impl<T: Message> argument_private::Sealed for &mut Retained<T> {}
impl<T: Message> argument_private::Sealed for Option<&mut Retained<T>> {}
impl<T: Message> argument_private::Sealed for &mut Option<Retained<T>> {}
impl<T: Message> argument_private::Sealed for Option<&mut Option<Retained<T>>> {}
impl<T: EncodeArgument> argument_private::Sealed for T {}
impl<T: EncodeArgument> ConvertArgument for T {
type __Inner = Self;
type __StoredBeforeMessage = ();
#[inline]
fn __from_declared_param(inner: Self::__Inner) -> Self {
inner
}
#[inline]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
(self, ())
}
}
impl argument_private::Sealed for bool {}
impl ConvertArgument for bool {
type __Inner = Bool;
type __StoredBeforeMessage = ();
#[inline]
fn __from_declared_param(inner: Self::__Inner) -> Self {
inner.as_bool()
}
#[inline]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
(Bool::new(self), ())
}
}
mod return_private {
pub trait Sealed {}
}
/// Same as [`ConvertArgument`], but for return types.
pub trait ConvertReturn: return_private::Sealed {
/// The inner type that this can be converted to and from.
#[doc(hidden)]
type __Inner: EncodeReturn;
#[doc(hidden)]
fn __into_declared_return(self) -> Self::__Inner;
#[doc(hidden)]
fn __from_return(inner: Self::__Inner) -> Self;
}
impl<T: EncodeReturn> return_private::Sealed for T {}
impl<T: EncodeReturn> ConvertReturn for T {
type __Inner = Self;
#[inline]
fn __into_declared_return(self) -> Self::__Inner {
self
}
#[inline]
fn __from_return(inner: Self::__Inner) -> Self {
inner
}
}
impl return_private::Sealed for bool {}
impl ConvertReturn for bool {
type __Inner = Bool;
#[inline]
fn __into_declared_return(self) -> Self::__Inner {
Bool::new(self)
}
#[inline]
fn __from_return(inner: Self::__Inner) -> Self {
inner.as_bool()
}
}
pub trait ConvertArguments {
#[doc(hidden)]
type __Inner: EncodeArguments;
#[doc(hidden)]
type __StoredBeforeMessage: Sized;
#[doc(hidden)]
fn __into_arguments(self) -> (Self::__Inner, Self::__StoredBeforeMessage);
#[doc(hidden)]
unsafe fn __process_after_message_send(_stored: Self::__StoredBeforeMessage);
}
pub trait TupleExtender<T> {
#[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 __StoredBeforeMessage = ($($t::__StoredBeforeMessage,)*);
#[inline]
fn __into_arguments(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
let ($($a,)*) = self;
$(let $a = ConvertArgument::__into_argument($a);)*
(($($a.0,)*), ($($a.1,)*))
}
#[inline]
unsafe fn __process_after_message_send(($($a,)*): Self::__StoredBeforeMessage) {
$(
unsafe { <$t as ConvertArgument>::__process_after_message_send($a) };
)*
}
}
impl<$($t,)* T> TupleExtender<T> 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;
#[test]
fn convert_normally_noop() {
assert_eq!(
TypeId::of::<<i32 as ConvertArgument>::__Inner>(),
TypeId::of::<i32>()
);
assert_eq!(<i32 as ConvertArgument>::__from_declared_param(42), 42);
assert_eq!(ConvertArgument::__into_argument(42i32).0, 42);
}
#[test]
fn convert_i8() {
assert_eq!(
TypeId::of::<<i8 as ConvertArgument>::__Inner>(),
TypeId::of::<i8>()
);
assert_eq!(<i8 as ConvertArgument>::__from_declared_param(-3), -3);
assert_eq!(ConvertArgument::__into_argument(-3i32).0, -3);
}
#[test]
fn convert_bool() {
assert!(!<bool as ConvertArgument>::__from_declared_param(Bool::NO));
assert!(<bool as ConvertArgument>::__from_declared_param(Bool::YES));
assert!(!<bool as ConvertReturn>::__from_return(Bool::NO));
assert!(<bool as ConvertReturn>::__from_return(Bool::YES));
assert!(!ConvertArgument::__into_argument(false).0.as_bool());
assert!(ConvertArgument::__into_argument(true).0.as_bool());
assert!(!ConvertReturn::__into_declared_return(false).as_bool());
assert!(ConvertReturn::__into_declared_return(true).as_bool());
#[cfg(all(target_vendor = "apple", target_os = "macos", target_arch = "x86_64"))]
assert_eq!(
<bool as ConvertArgument>::__Inner::ENCODING_ARGUMENT,
crate::encode::Encoding::Char,
);
}
}

View File

@@ -0,0 +1,433 @@
#[cfg(debug_assertions)]
use alloc::vec::Vec;
use core::marker::PhantomData;
#[cfg(debug_assertions)]
use std::collections::HashSet;
use crate::encode::{Encode, Encoding};
use crate::rc::{Allocated, Retained};
use crate::runtime::{
AnyClass, AnyObject, ClassBuilder, MessageReceiver, MethodImplementation, Sel,
};
#[cfg(debug_assertions)]
use crate::runtime::{AnyProtocol, MethodDescription};
use crate::{ClassType, DeclaredClass, Message, ProtocolType};
use super::declared_ivars::{register_with_ivars, setup_dealloc};
use super::{CopyOrMutCopy, Init, MaybeUnwrap, New, Other};
use crate::mutability;
/// Helper type for implementing `MethodImplementation` with a receiver of
/// `Allocated<T>`, without exposing that implementation to users.
//
// Must be private, soundness of MethodImplementation relies on this.
#[doc(hidden)]
#[repr(transparent)]
#[derive(Debug)]
#[allow(dead_code)]
pub struct IdReturnValue(pub(crate) *mut AnyObject);
// SAFETY: `IdReturnValue` is `#[repr(transparent)]`
unsafe impl Encode for IdReturnValue {
const ENCODING: Encoding = <*mut AnyObject>::ENCODING;
}
// One could imagine a different design where we had a method like
// `fn convert_receiver()`, but that won't work in `declare_class!` since we
// can't actually modify the `self` argument (e.g. `let self = foo(self)` is
// not allowed).
//
// See `MsgSendId` and `RetainSemantics` for details on the retain semantics
// we're following here.
pub trait MessageRecieveId<Receiver, Ret> {
fn into_return(obj: Ret) -> IdReturnValue;
}
// Receiver and return type have no correlation
impl<Receiver, Ret> MessageRecieveId<Receiver, Ret> for New
where
Receiver: MessageReceiver,
Ret: MaybeOptionId,
{
#[inline]
fn into_return(obj: Ret) -> IdReturnValue {
obj.consumed_return()
}
}
// Explicitly left unimplemented for now!
// impl MessageRecieveId<impl MessageReceiver, Allocated<T>> for Alloc {}
// Note: `MethodImplementation` allows for `Allocated` as the receiver, so we
// restrict it here to only be when the selector is `init`.
//
// Additionally, the receiver and return type must have the same generic
// parameter `T`.
impl<Ret, T> MessageRecieveId<Allocated<T>, Ret> for Init
where
T: Message,
Ret: MaybeOptionId<Input = Option<Retained<T>>>,
{
#[inline]
fn into_return(obj: Ret) -> IdReturnValue {
obj.consumed_return()
}
}
// Receiver and return type have no correlation
impl<Receiver, Ret> MessageRecieveId<Receiver, Ret> for CopyOrMutCopy
where
Receiver: MessageReceiver,
Ret: MaybeOptionId,
{
#[inline]
fn into_return(obj: Ret) -> IdReturnValue {
obj.consumed_return()
}
}
// Receiver and return type have no correlation
impl<Receiver, Ret> MessageRecieveId<Receiver, Ret> for Other
where
Receiver: MessageReceiver,
Ret: MaybeOptionId,
{
#[inline]
fn into_return(obj: Ret) -> IdReturnValue {
obj.autorelease_return()
}
}
/// Helper trait for specifying an `Retained<T>` or an `Option<Retained<T>>`.
///
/// (Both of those are valid return types from declare_class! `#[method_id]`).
pub trait MaybeOptionId: MaybeUnwrap {
fn consumed_return(self) -> IdReturnValue;
fn autorelease_return(self) -> IdReturnValue;
}
impl<T: Message> MaybeOptionId for Retained<T> {
#[inline]
fn consumed_return(self) -> IdReturnValue {
let ptr: *mut T = Retained::into_raw(self);
IdReturnValue(ptr.cast())
}
#[inline]
fn autorelease_return(self) -> IdReturnValue {
let ptr: *mut T = Retained::autorelease_return(self);
IdReturnValue(ptr.cast())
}
}
impl<T: Message> MaybeOptionId for Option<Retained<T>> {
#[inline]
fn consumed_return(self) -> IdReturnValue {
let ptr: *mut T = Retained::consume_as_ptr_option(self);
IdReturnValue(ptr.cast())
}
#[inline]
fn autorelease_return(self) -> IdReturnValue {
let ptr: *mut T = Retained::autorelease_return_option(self);
IdReturnValue(ptr.cast())
}
}
/// Helper for ensuring that `ClassType::Mutability` is implemented correctly
/// for subclasses.
pub trait ValidSubclassMutability<T: mutability::Mutability> {}
// Root
impl ValidSubclassMutability<mutability::Immutable> for mutability::Root {}
impl ValidSubclassMutability<mutability::Mutable> for mutability::Root {}
impl<MS, IS> ValidSubclassMutability<mutability::ImmutableWithMutableSubclass<MS>>
for mutability::Root
where
MS: ?Sized + ClassType<Mutability = mutability::MutableWithImmutableSuperclass<IS>>,
IS: ?Sized + ClassType<Mutability = mutability::ImmutableWithMutableSubclass<MS>>,
{
}
impl ValidSubclassMutability<mutability::InteriorMutable> for mutability::Root {}
impl ValidSubclassMutability<mutability::MainThreadOnly> for mutability::Root {}
// Immutable
impl ValidSubclassMutability<mutability::Immutable> for mutability::Immutable {}
// Mutable
impl ValidSubclassMutability<mutability::Mutable> for mutability::Mutable {}
// ImmutableWithMutableSubclass
impl<MS, IS> ValidSubclassMutability<mutability::MutableWithImmutableSuperclass<IS>>
for mutability::ImmutableWithMutableSubclass<MS>
where
MS: ?Sized + ClassType<Mutability = mutability::MutableWithImmutableSuperclass<IS>>,
IS: ?Sized + ClassType<Mutability = mutability::ImmutableWithMutableSubclass<MS>>,
{
}
// Only valid when `NSCopying`/`NSMutableCopying` is not implemented!
impl<MS: ?Sized + ClassType> ValidSubclassMutability<mutability::Immutable>
for mutability::ImmutableWithMutableSubclass<MS>
{
}
// MutableWithImmutableSuperclass
// Only valid when `NSCopying`/`NSMutableCopying` is not implemented!
impl<IS: ?Sized + ClassType> ValidSubclassMutability<mutability::Mutable>
for mutability::MutableWithImmutableSuperclass<IS>
{
}
// InteriorMutable
impl ValidSubclassMutability<mutability::InteriorMutable> for mutability::InteriorMutable {}
impl ValidSubclassMutability<mutability::MainThreadOnly> for mutability::InteriorMutable {}
// MainThreadOnly
impl ValidSubclassMutability<mutability::MainThreadOnly> for mutability::MainThreadOnly {}
/// Ensure that:
/// 1. The type is not a root class (it's superclass implements `ClassType`,
/// and it's mutability is not `Root`), and therefore also implements basic
/// memory management methods, as required by `unsafe impl Message`.
/// 2. The mutability is valid according to the superclass' mutability.
#[inline]
pub fn assert_mutability_matches_superclass_mutability<T>()
where
T: ?Sized + ClassType,
T::Super: ClassType,
T::Mutability: mutability::Mutability,
<T::Super as ClassType>::Mutability: ValidSubclassMutability<T::Mutability>,
{
// Noop
}
#[derive(Debug)]
pub struct ClassBuilderHelper<T: ?Sized> {
builder: ClassBuilder,
p: PhantomData<T>,
}
#[track_caller]
fn failed_declaring_class(name: &str) -> ! {
panic!("could not create new class {name}. Perhaps a class with that name already exists?")
}
impl<T: DeclaredClass> ClassBuilderHelper<T> {
#[inline]
#[track_caller]
#[allow(clippy::new_without_default)]
pub fn new() -> Self
where
T::Super: ClassType,
{
let mut builder = match ClassBuilder::new(T::NAME, <T::Super as ClassType>::class()) {
Some(builder) => builder,
None => failed_declaring_class(T::NAME),
};
setup_dealloc::<T>(&mut builder);
Self {
builder,
p: PhantomData,
}
}
#[inline]
pub fn add_protocol_methods<P>(&mut self) -> ClassProtocolMethodsBuilder<'_, T>
where
P: ?Sized + ProtocolType,
{
let protocol = P::protocol();
if let Some(protocol) = protocol {
self.builder.add_protocol(protocol);
}
#[cfg(debug_assertions)]
{
ClassProtocolMethodsBuilder {
builder: self,
protocol,
required_instance_methods: protocol
.map(|p| p.method_descriptions(true))
.unwrap_or_default(),
optional_instance_methods: protocol
.map(|p| p.method_descriptions(false))
.unwrap_or_default(),
registered_instance_methods: HashSet::new(),
required_class_methods: protocol
.map(|p| p.class_method_descriptions(true))
.unwrap_or_default(),
optional_class_methods: protocol
.map(|p| p.class_method_descriptions(false))
.unwrap_or_default(),
registered_class_methods: HashSet::new(),
}
}
#[cfg(not(debug_assertions))]
{
ClassProtocolMethodsBuilder { builder: self }
}
}
// Addition: This restricts to callee `T`
#[inline]
pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
where
F: MethodImplementation<Callee = T>,
{
// SAFETY: Checked by caller
unsafe { self.builder.add_method(sel, func) }
}
#[inline]
pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
where
F: MethodImplementation<Callee = AnyClass>,
{
// SAFETY: Checked by caller
unsafe { self.builder.add_class_method(sel, func) }
}
#[inline]
pub fn register(self) -> (&'static AnyClass, isize, isize) {
register_with_ivars::<T>(self.builder)
}
}
/// Helper for ensuring that:
/// - Only methods on the protocol are overriden.
/// - TODO: The methods have the correct signature.
/// - All required methods are overridden.
#[derive(Debug)]
pub struct ClassProtocolMethodsBuilder<'a, T: ?Sized> {
builder: &'a mut ClassBuilderHelper<T>,
#[cfg(debug_assertions)]
protocol: Option<&'static AnyProtocol>,
#[cfg(debug_assertions)]
required_instance_methods: Vec<MethodDescription>,
#[cfg(debug_assertions)]
optional_instance_methods: Vec<MethodDescription>,
#[cfg(debug_assertions)]
registered_instance_methods: HashSet<Sel>,
#[cfg(debug_assertions)]
required_class_methods: Vec<MethodDescription>,
#[cfg(debug_assertions)]
optional_class_methods: Vec<MethodDescription>,
#[cfg(debug_assertions)]
registered_class_methods: HashSet<Sel>,
}
impl<T: DeclaredClass> ClassProtocolMethodsBuilder<'_, T> {
// Addition: This restricts to callee `T`
#[inline]
pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
where
F: MethodImplementation<Callee = T>,
{
#[cfg(debug_assertions)]
if let Some(protocol) = self.protocol {
let _types = self
.required_instance_methods
.iter()
.chain(&self.optional_instance_methods)
.find(|desc| desc.sel == sel)
.map(|desc| desc.types)
.unwrap_or_else(|| {
panic!(
"failed overriding protocol method -[{protocol} {sel}]: method not found"
)
});
}
// SAFETY: Checked by caller
unsafe { self.builder.add_method(sel, func) };
#[cfg(debug_assertions)]
if !self.registered_instance_methods.insert(sel) {
unreachable!("already added")
}
}
#[inline]
pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
where
F: MethodImplementation<Callee = AnyClass>,
{
#[cfg(debug_assertions)]
if let Some(protocol) = self.protocol {
let _types = self
.required_class_methods
.iter()
.chain(&self.optional_class_methods)
.find(|desc| desc.sel == sel)
.map(|desc| desc.types)
.unwrap_or_else(|| {
panic!(
"failed overriding protocol method +[{protocol} {sel}]: method not found"
)
});
}
// SAFETY: Checked by caller
unsafe { self.builder.add_class_method(sel, func) };
#[cfg(debug_assertions)]
if !self.registered_class_methods.insert(sel) {
unreachable!("already added")
}
}
#[cfg(debug_assertions)]
pub fn finish(self) {
let superclass = self.builder.builder.superclass();
if let Some(protocol) = self.protocol {
for desc in &self.required_instance_methods {
if self.registered_instance_methods.contains(&desc.sel) {
continue;
}
// TODO: Don't do this when `NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION`
if superclass
.and_then(|superclass| superclass.instance_method(desc.sel))
.is_some()
{
continue;
}
panic!(
"must implement required protocol method -[{protocol} {}]",
desc.sel
)
}
}
if let Some(protocol) = self.protocol {
for desc in &self.required_class_methods {
if self.registered_class_methods.contains(&desc.sel) {
continue;
}
// TODO: Don't do this when `NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION`
if superclass
.and_then(|superclass| superclass.class_method(desc.sel))
.is_some()
{
continue;
}
panic!(
"must implement required protocol method +[{protocol} {}]",
desc.sel
);
}
}
}
#[inline]
#[cfg(not(debug_assertions))]
pub fn finish(self) {}
}

View File

@@ -0,0 +1,939 @@
//! # Supporting code for instance variables on declared classes.
//!
//! Adding instance variables to Objective-C classes is fairly simple, it can
//! be done using `ClassBuilder::add_ivar`.
//!
//! However, things become more complicated once we have to handle `Drop`,
//! deallocation and unwind safety; remember, `dealloc` may be called even on
//! newly, non-initialized instances.
//!
//! Note that Swift [doesn't handle this][swift-deinit-unsound], but that
//! doesn't mean we can simply stick our heads in the sand.
//!
//! Basically, instead of storing the ivars directly, we store it as the
//! following tagged enum:
//! ```
//! #[repr(u8)]
//! enum ActualIvar<T: objc2::DeclaredClass> {
//! Allocated = 0,
//! PartialInit(T::Ivars),
//! Finalized(T::Ivars),
//! }
//! ```
//!
//! For performance reasons, we unfortunately can't write it that cleanly: we
//! want the data and the drop flag as two separate ivars instead of combining
//! them into one, since that will give the layout algorithm in the
//! Objective-C runtime more information to work with, and it allows us to
//! selectively omit the drop flag or the data storage when either is not
//! needed.
//!
//! Ideally, we'd be able to somehow statically detect when the ivars have a
//! zero niche, which would allow us to know if the type is safe to drop when
//! zero-initialized:
//! ```ignore
//! None::<T::Ivars>.is_all_zeroes_bitpattern()
//! ```
//!
//! However, detecting if the `None` is all zeroes requires reading the bytes,
//! which is [unsound for types that may have padding][unsound-read-padding],
//! since that padding is uninitialized.
//!
//! So this is an optimization that we don't yet do, but that may be possible
//! in the future using something like `bytemuck::ZeroableInOption`.
//!
//! [swift-deinit-unsound]: https://github.com/apple/swift/issues/68734
//! [unsound-read-padding]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ea068e8d9e55801aa9520ea914eb2822
use alloc::borrow::Cow;
use alloc::format;
use core::mem;
use core::ptr::{self, NonNull};
use crate::encode::{Encode, Encoding};
use crate::runtime::{AnyClass, AnyObject, ClassBuilder, MessageReceiver, Sel};
use crate::{sel, ClassType, DeclaredClass};
/// A type representing the drop flags that may be set for a type.
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum DropFlag {
/// Set to zero to ensure that this is the default when created by the
/// Objective-C runtime.
///
/// Ivars are [documented][obj-init-zeroed] to be zero-initialized after
/// allocation, and that has been true since at least [the Objective-C
/// version shipped with Mac OS X 10.0][objc4-208-init].
///
/// [obj-init-zeroed]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW7
/// [objc4-208-init]: https://github.com/apple-oss-distributions/objc4/blob/objc4-208/runtime/objc-class.m#L367
#[allow(dead_code)]
Allocated = 0x00,
/// Used when `mem::needs_drop::<T::Ivars>()`, or with debug assertions enabled.
InitializedIvars = 0x0f,
/// Used when `mem::needs_drop::<T>()`, or with debug assertions enabled.
Finalized = 0xff,
}
// SAFETY: The DropFlag is #[repr(u8)]
unsafe impl Encode for DropFlag {
const ENCODING: Encoding = u8::ENCODING;
}
pub trait DeclaredIvarsHelper {
const HAS_IVARS: bool;
const HAS_DROP_FLAG: bool;
}
impl<T: DeclaredClass> DeclaredIvarsHelper for T {
/// Only add ivar if we need the runtime to allocate memory for it.
///
/// We can avoid doing so if the type is a zero-sized type (ZST), and the
/// required alignment is less than the alignment of a pointer (objects
/// are guaranteed to have at least that alignment themselves).
const HAS_IVARS: bool = {
mem::size_of::<T::Ivars>() > 0
|| mem::align_of::<T::Ivars>() > mem::align_of::<*mut AnyObject>()
};
/// Only add drop flag if the type or the ivars need it.
///
/// `needs_drop::<T>` can reliably detect a direct implementation of
/// `Drop`, since the type only includes `ManuallyDrop` or `PhantomData`
/// fields.
const HAS_DROP_FLAG: bool = mem::needs_drop::<T>() || mem::needs_drop::<T::Ivars>();
}
/// Helper function for getting a pointer to the instance variable.
///
/// # Safety
///
/// The pointer must be valid, and the instance variable offset (if it has
/// any) must have been initialized.
#[inline]
unsafe fn ptr_to_ivar<T: ?Sized + DeclaredClass>(ptr: NonNull<T>) -> NonNull<T::Ivars> {
// This is called even when there is no ivars, but that's fine, since in
// that case the ivar is zero-sized, and the offset will be zero, so we
// can still compute a valid pointer to the ivar.
//
// debug_assert!(T::HAS_IVARS);
// SAFETY: That an instance variable with the given type exists at the
// specified offset is ensured by `DeclaredClass` trait implementor.
unsafe { AnyObject::ivar_at_offset::<T::Ivars>(ptr.cast(), T::__ivars_offset()) }
}
/// Helper function for getting a pointer to the drop flag.
///
/// # Safety
///
/// The pointer must be valid and have an initialized drop flag.
#[inline]
unsafe fn ptr_to_drop_flag<T: DeclaredClass>(ptr: NonNull<T>) -> *mut DropFlag {
debug_assert!(T::HAS_DROP_FLAG, "type did not have drop flag");
// SAFETY: That a drop flag exists at the specified offset is ensured
// by caller.
unsafe { AnyObject::ivar_at_offset::<DropFlag>(ptr.cast(), T::__drop_flag_offset()).as_ptr() }
}
pub(crate) fn setup_dealloc<T: DeclaredClass>(builder: &mut ClassBuilder)
where
T::Super: ClassType,
{
// Add dealloc if the class or the ivars need dropping.
if mem::needs_drop::<T>() || mem::needs_drop::<T::Ivars>() {
let func: unsafe extern "C" fn(_, _) = dealloc::<T>;
// SAFETY: The function signature is correct, and method contract is
// upheld inside `dealloc`.
unsafe { builder.add_method(sel!(dealloc), func) };
} else {
// Users should not rely on this ommision, it is only an optimization.
}
}
/// The `dealloc` Objective-C method.
///
/// See the following links for more details about `dealloc`:
/// - <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#dealloc>
/// - <https://developer.apple.com/documentation/objectivec/nsobject/1571947-dealloc>
/// - <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW2>
///
/// TODO: Change this to `extern "C-unwind"`, unwinding in dealloc is allowed.
unsafe extern "C" fn dealloc<T: DeclaredClass>(this: NonNull<T>, cmd: Sel)
where
T::Super: ClassType,
{
/// Helper function for marking the cold path when branching.
#[inline]
#[cold]
fn cold_path() {}
// SAFETY: `dealloc` is only registered when there is a need for dropping,
// and hence a need for a drop flag.
let drop_flag = unsafe { *ptr_to_drop_flag(this) };
if mem::needs_drop::<T>() {
match drop_flag {
// Don't deallocate the current instance if it has not been fully
// initialized.
//
// Note that we still run the superclass deinitializer below.
DropFlag::Allocated | DropFlag::InitializedIvars => cold_path(),
// SAFETY: This is the `dealloc` method, so we know that the type
// never needs to be deallocated again.
//
// Additionally, we know that the type was fully initialized, since
// that's what the drop flag says.
//
// TODO: This can unwind, is it correct to just let that
// propagate?
DropFlag::Finalized => unsafe { ptr::drop_in_place(this.as_ptr()) },
}
}
// TODO: Debug assertions that the retain count is still 1 here.
// Note: This should be done inside `.cxx_destruct`, since if a superclass
// calls an overwritten method in its `dealloc`, it can access
// deinitialized instance variables; but we can't do that without
// generating statics, so we have to do it in `dealloc` for now.
//
// It is very important that we do this after the `Drop` of the class
// itself above, though.
//
// Another possibility would be to read the contents of the ivars onto the
// stack here, and only deinitialize after the superclass' `dealloc`, but
// that would break the pinning guarantee that ivars otherwise have.
if mem::needs_drop::<T::Ivars>() {
match drop_flag {
// Do nothing if the ivars have not been initialized.
DropFlag::Allocated => cold_path(),
DropFlag::InitializedIvars | DropFlag::Finalized => {
// SAFETY: The instance variable is initialized, so it is
// valid to drop here.
//
// TODO: This can unwind, is it correct to just let that
// propagate?
unsafe { ptr::drop_in_place(ptr_to_ivar(this).as_ptr()) };
}
}
}
// The superclass' "marker" that this stores is wrapped in `ManuallyDrop`,
// we drop it by calling the superclass' `dealloc` method instead.
//
// Note: ARC does this automatically, which means most Objective-C code in
// the wild don't contain this call; but we _are_ ARC, so we must do this.
//
// SAFETY: The argument and return types are correct, and we make sure to
// only call this once.
unsafe {
MessageReceiver::send_super_message(
this,
<T as ClassType>::Super::class(),
cmd, // Reuse the selector
(), // No arguments
)
}
}
/// Register the class, and get the ivar offsets.
#[inline]
pub(crate) fn register_with_ivars<T: DeclaredClass>(
mut builder: ClassBuilder,
) -> (&'static AnyClass, isize, isize) {
let (ivar_name, drop_flag_name): (Cow<'static, str>, Cow<'static, str>) = {
if cfg!(feature = "gnustep-1-7") {
// GNUStep does not support a subclass having an ivar with the
// same name as a superclass, so let's use the class name as the
// ivar name to ensure uniqueness.
(
format!("{}_ivars", T::NAME).into(),
format!("{}_drop_flag", T::NAME).into(),
)
} else {
("ivars".into(), "drop_flag".into())
}
};
if T::HAS_IVARS {
// TODO: Consider not adding a encoding - Swift doesn't do it.
let ivar_encoding = Encoding::Array(
mem::size_of::<T::Ivars>() as u64,
match mem::align_of::<T::Ivars>() {
1 => &u8::ENCODING,
2 => &u16::ENCODING,
4 => &u32::ENCODING,
// The alignment of `u64` may not be 8 on all architectures
8 if mem::align_of::<u64>() == 8 => &u64::ENCODING,
alignment => panic!("unsupported alignment {alignment} for `{}::Ivars`", T::NAME),
},
);
unsafe { builder.add_ivar_inner::<T::Ivars>(&ivar_name, &ivar_encoding) };
}
if T::HAS_DROP_FLAG {
// TODO: Maybe we can reuse the drop flag when subclassing an already
// declared class?
builder.add_ivar::<DropFlag>(&drop_flag_name);
}
let cls = builder.register();
let ivars_offset = if T::HAS_IVARS {
// Monomorphized error handling
// Intentionally not #[track_caller], we expect this error to never occur
fn get_ivar_failed() -> ! {
unreachable!("failed retrieving instance variable on newly declared class")
}
cls.instance_variable(&ivar_name)
.unwrap_or_else(|| get_ivar_failed())
.offset()
} else {
// Fallback to an offset of zero.
//
// This is fine, since any reads here will only be via. zero-sized
// ivars, where the actual pointer doesn't matter.
0
};
let drop_flag_offset = if T::HAS_DROP_FLAG {
// Monomorphized error handling
// Intentionally not #[track_caller], we expect this error to never occur
fn get_drop_flag_failed() -> ! {
unreachable!("failed retrieving drop flag instance variable on newly declared class")
}
cls.instance_variable(&drop_flag_name)
.unwrap_or_else(|| get_drop_flag_failed())
.offset()
} else {
// Fall back to an offset of zero.
//
// This is fine, since the drop flag is never actually used in the
// cases where it was not added.
0
};
(cls, ivars_offset, drop_flag_offset)
}
/// # Safety
///
/// The pointer must be a valid, newly allocated instance.
#[inline]
#[track_caller]
pub(crate) unsafe fn initialize_ivars<T: DeclaredClass>(ptr: NonNull<T>, val: T::Ivars) {
// Debug assert the state of the drop flag
if T::HAS_DROP_FLAG && cfg!(debug_assertions) {
// SAFETY: Just checked that the drop flag is available.
match unsafe { *ptr_to_drop_flag(ptr) } {
DropFlag::Allocated => {
// Allow initialization after allocation
}
DropFlag::InitializedIvars => {
panic!("tried to initialize ivars after they were already initialized")
}
DropFlag::Finalized => {
panic!("tried to initialize ivars on an already initialized object")
}
}
}
// SAFETY:
// - Caller ensures the pointer is valid.
// - The location is properly aligned by `ClassBuilder::add_ivar`.
// - This write is done as part of initialization, so we know that the
// pointer is not shared elsewhere.
unsafe { ptr_to_ivar(ptr).as_ptr().write(val) };
// Write to drop flag that we've initialized the instance variables.
//
// Note: We intentionally only do this _after_ writing to the ivars,
// for better unwind safety.
if T::HAS_DROP_FLAG && (mem::needs_drop::<T::Ivars>() || cfg!(debug_assertions)) {
// SAFETY: Just checked that the drop flag is available.
unsafe { ptr_to_drop_flag(ptr).write(DropFlag::InitializedIvars) }
}
}
/// # Safety
///
/// The pointer must be valid and finalized (i.e. all super initializers must
/// have been run).
#[inline]
#[track_caller]
pub(crate) unsafe fn set_finalized<T: DeclaredClass>(ptr: NonNull<T>) {
// Debug assert the state of the drop flag
if T::HAS_DROP_FLAG && cfg!(debug_assertions) {
// SAFETY: Just checked that the drop flag is available.
match unsafe { *ptr_to_drop_flag(ptr) } {
DropFlag::Allocated => {
panic!("tried to finalize an object that was not yet fully initialized")
}
DropFlag::InitializedIvars => {
// Allow finalizing after initialization
}
DropFlag::Finalized => {
panic!("tried to finalize an already finalized object")
}
}
}
// Write to drop flag that we've fully initialized the class.
if T::HAS_DROP_FLAG && (mem::needs_drop::<T>() || cfg!(debug_assertions)) {
// SAFETY: Just checked that the drop flag is available.
unsafe { ptr_to_drop_flag(ptr).write(DropFlag::Finalized) }
}
}
/// # Safety
///
/// The pointer must be valid and the instance variables must be initialized.
#[inline]
#[track_caller]
pub(crate) unsafe fn get_initialized_ivar_ptr<T: DeclaredClass>(
ptr: NonNull<T>,
) -> NonNull<T::Ivars> {
// Debug assert the state of the drop flag
if T::HAS_DROP_FLAG && cfg!(debug_assertions) {
// SAFETY: Just checked that the drop flag is available.
match unsafe { *ptr_to_drop_flag(ptr) } {
DropFlag::Allocated => {
panic!("tried to access uninitialized instance variable")
}
DropFlag::InitializedIvars => {
// Allow accessing even if not finalized, since we only set
// that state _after_ it actually happens, while accesses may
// be done by the superclass initializer in e.g. an
// overwritten method.
}
DropFlag::Finalized => {
// Allow accessing if finalized
}
}
}
// SAFETY: That the pointer is valid is ensured by caller.
unsafe { ptr_to_ivar(ptr) }
}
#[cfg(test)]
mod tests {
use std::println;
use std::sync::Mutex;
use alloc::vec::Vec;
use super::*;
use crate::mutability::{InteriorMutable, Mutable};
use crate::rc::{Allocated, PartialInit, RcTestObject, Retained, ThreadTestData};
use crate::runtime::NSObject;
use crate::{declare_class, msg_send, msg_send_id};
/// Initialize superclasses, but not own class.
unsafe fn init_only_superclasses<T: DeclaredClass>(obj: Allocated<T>) -> Retained<T>
where
T::Super: ClassType,
{
unsafe { Retained::from_raw(msg_send![super(Allocated::into_ptr(obj)), init]) }.unwrap()
}
/// Initialize, but fail to finalize (which is only done by `msg_send_id!`).
unsafe fn init_no_finalize<T: DeclaredClass>(obj: Allocated<T>) -> Retained<T>
where
T::Super: ClassType,
T::Ivars: Default,
{
let obj = obj.set_ivars(Default::default());
unsafe { Retained::from_raw(msg_send![super(PartialInit::into_ptr(obj)), init]) }.unwrap()
}
/// Initialize properly.
unsafe fn init<T: DeclaredClass>(obj: Allocated<T>) -> Retained<T> {
unsafe { msg_send_id![obj, init] }
}
#[test]
fn assert_size() {
assert_eq!(mem::size_of::<DropFlag>(), 1);
}
#[test]
fn test_dealloc_and_dealloc_subclasses() {
#[derive(Debug, PartialEq)]
enum Operation {
DropIvar,
DropClass,
}
static OPERATIONS: Mutex<Vec<Operation>> = Mutex::new(Vec::new());
#[derive(Default)]
struct IvarThatImplsDrop;
impl Drop for IvarThatImplsDrop {
fn drop(&mut self) {
OPERATIONS.lock().unwrap().push(Operation::DropIvar);
}
}
#[track_caller]
fn check<const N: usize>(expected: [Operation; N]) {
let mut operations = OPERATIONS.lock().unwrap();
assert_eq!(&**operations, expected);
operations.clear();
}
// First class
declare_class!(
struct ImplsDrop;
unsafe impl ClassType for ImplsDrop {
type Super = NSObject;
type Mutability = InteriorMutable;
const NAME: &'static str = "ImplsDrop";
}
impl DeclaredClass for ImplsDrop {
type Ivars = ();
}
unsafe impl ImplsDrop {
#[method_id(init)]
fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
unsafe { msg_send_id![super(this.set_ivars(())), init] }
}
}
);
impl Drop for ImplsDrop {
fn drop(&mut self) {
OPERATIONS.lock().unwrap().push(Operation::DropClass);
}
}
let _ = ImplsDrop::alloc();
check([]);
let _ = unsafe { init_only_superclasses(ImplsDrop::alloc()) };
check([]);
let _ = unsafe { init_no_finalize(ImplsDrop::alloc()) };
check([]);
let _ = unsafe { init(ImplsDrop::alloc()) };
check([Operation::DropClass]);
// Subclass
declare_class!(
struct IvarsImplDrop;
unsafe impl ClassType for IvarsImplDrop {
type Super = ImplsDrop;
type Mutability = InteriorMutable;
const NAME: &'static str = "IvarsImplDrop";
}
impl DeclaredClass for IvarsImplDrop {
type Ivars = IvarThatImplsDrop;
}
unsafe impl IvarsImplDrop {
#[method_id(init)]
fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
unsafe { msg_send_id![super(this.set_ivars(IvarThatImplsDrop)), init] }
}
}
);
let _ = IvarsImplDrop::alloc();
check([]);
let _ = unsafe { init_only_superclasses(IvarsImplDrop::alloc()) };
check([Operation::DropClass]);
let _ = unsafe { init_no_finalize(IvarsImplDrop::alloc()) };
check([Operation::DropIvar, Operation::DropClass]);
let _ = unsafe { init(IvarsImplDrop::alloc()) };
check([Operation::DropIvar, Operation::DropClass]);
// Further subclass
declare_class!(
struct BothIvarsAndTypeImplsDrop;
unsafe impl ClassType for BothIvarsAndTypeImplsDrop {
type Super = IvarsImplDrop;
type Mutability = InteriorMutable;
const NAME: &'static str = "BothIvarsAndTypeImplsDrop";
}
impl DeclaredClass for BothIvarsAndTypeImplsDrop {
type Ivars = IvarThatImplsDrop;
}
unsafe impl BothIvarsAndTypeImplsDrop {
#[method_id(init)]
fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
unsafe { msg_send_id![super(this.set_ivars(IvarThatImplsDrop)), init] }
}
}
);
impl Drop for BothIvarsAndTypeImplsDrop {
fn drop(&mut self) {
OPERATIONS.lock().unwrap().push(Operation::DropClass);
}
}
let _ = BothIvarsAndTypeImplsDrop::alloc();
check([]);
let _ = unsafe { init_only_superclasses(BothIvarsAndTypeImplsDrop::alloc()) };
check([Operation::DropIvar, Operation::DropClass]);
let _ = unsafe { init_no_finalize(BothIvarsAndTypeImplsDrop::alloc()) };
check([
Operation::DropIvar,
Operation::DropIvar,
Operation::DropClass,
]);
let _ = unsafe { init(BothIvarsAndTypeImplsDrop::alloc()) };
check([
Operation::DropClass,
Operation::DropIvar,
Operation::DropIvar,
Operation::DropClass,
]);
}
#[test]
fn test_no_generated_dealloc_if_not_needed() {
#[allow(unused)]
struct Ivar {
field1: u8,
field2: bool,
}
declare_class!(
struct IvarsNoDrop;
unsafe impl ClassType for IvarsNoDrop {
type Super = NSObject;
type Mutability = InteriorMutable;
const NAME: &'static str = "IvarsNoDrop";
}
impl DeclaredClass for IvarsNoDrop {
type Ivars = Ivar;
}
);
assert!(!mem::needs_drop::<IvarsNoDrop>());
assert!(!mem::needs_drop::<Ivar>());
assert_eq!(
IvarsNoDrop::class().instance_method(sel!(dealloc)),
NSObject::class().instance_method(sel!(dealloc)),
);
}
#[test]
fn zst_ivar() {
#[derive(Default, Debug)]
struct Ivar;
declare_class!(
struct IvarZst;
unsafe impl ClassType for IvarZst {
type Super = NSObject;
type Mutability = Mutable;
const NAME: &'static str = "IvarZst";
}
impl DeclaredClass for IvarZst {
type Ivars = Ivar;
}
unsafe impl IvarZst {
#[method_id(init)]
fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
unsafe { msg_send_id![super(this.set_ivars(Ivar)), init] }
}
}
);
assert_eq!(
IvarZst::class().instance_size(),
NSObject::class().instance_size(),
);
let ivar_name = if cfg!(feature = "gnustep-1-7") {
"IvarZst_ivars"
} else {
"ivars"
};
assert!(IvarZst::class().instance_variable(ivar_name).is_none());
let mut obj = unsafe { init(IvarZst::alloc()) };
println!("{:?}", obj.ivars());
*obj.ivars_mut() = Ivar;
}
#[test]
#[should_panic = "unsupported alignment 16 for `HasIvarWithHighAlignment::Ivars`"]
fn test_generate_ivar_high_alignment() {
#[repr(align(16))]
struct HighAlignment;
declare_class!(
struct HasIvarWithHighAlignment;
unsafe impl ClassType for HasIvarWithHighAlignment {
type Super = NSObject;
type Mutability = InteriorMutable;
const NAME: &'static str = "HasIvarWithHighAlignment";
}
impl DeclaredClass for HasIvarWithHighAlignment {
type Ivars = HighAlignment;
}
);
// Have to allocate up to the desired alignment, but no need to go
// further, since the object is zero-sized.
assert_eq!(HasIvarWithHighAlignment::class().instance_size(), 16);
let ivar_name = if cfg!(feature = "gnustep-1-7") {
"IvarZst_ivars"
} else {
"ivars"
};
let ivar = HasIvarWithHighAlignment::class()
.instance_variable(ivar_name)
.unwrap();
assert_eq!(ivar.offset(), 16);
}
#[test]
#[allow(clippy::assigning_clones)]
fn test_ivar_access() {
declare_class!(
struct RcIvar;
unsafe impl ClassType for RcIvar {
type Super = NSObject;
type Mutability = Mutable;
const NAME: &'static str = "RcIvar";
}
impl DeclaredClass for RcIvar {
type Ivars = Option<Retained<RcTestObject>>;
}
unsafe impl RcIvar {
#[method_id(init)]
fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
let this = this.set_ivars(Some(RcTestObject::new()));
unsafe { msg_send_id![super(this), init] }
}
}
);
let mut expected = ThreadTestData::current();
let _ = RcIvar::alloc();
expected.assert_current();
let _ = unsafe { init_only_superclasses(RcIvar::alloc()) };
expected.assert_current();
// Ivar access is valid even if the class is not finalized.
let mut obj = unsafe { init_no_finalize(RcIvar::alloc()) };
expected.assert_current();
*obj.ivars_mut() = Some(RcTestObject::new());
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
drop(obj);
expected.release += 1;
expected.drop += 1;
expected.assert_current();
let mut obj = unsafe { init(RcIvar::alloc()) };
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
*obj.ivars_mut() = obj.ivars().clone();
expected.retain += 1;
expected.release += 1;
expected.assert_current();
drop(obj);
expected.release += 1;
expected.drop += 1;
expected.assert_current();
#[derive(Default, Debug, PartialEq, Eq)]
struct RcIvarSubclassIvars {
int: i32,
obj: Retained<RcTestObject>,
}
declare_class!(
struct RcIvarSubclass;
unsafe impl ClassType for RcIvarSubclass {
type Super = RcIvar;
type Mutability = Mutable;
const NAME: &'static str = "RcIvarSubclass";
}
impl DeclaredClass for RcIvarSubclass {
type Ivars = RcIvarSubclassIvars;
}
unsafe impl RcIvarSubclass {
#[method_id(init)]
fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
let this = this.set_ivars(RcIvarSubclassIvars {
int: 42,
obj: RcTestObject::new(),
});
unsafe { msg_send_id![super(this), init] }
}
}
);
let mut obj = unsafe { init(RcIvarSubclass::alloc()) };
expected.alloc += 2;
expected.init += 2;
expected.assert_current();
assert_eq!(obj.ivars().int, 42);
obj.ivars_mut().int += 1;
assert_eq!(obj.ivars().int, 43);
obj.ivars_mut().obj = (**obj).ivars().clone().unwrap();
expected.retain += 1;
expected.release += 1;
expected.drop += 1;
expected.assert_current();
*(**obj).ivars_mut() = None;
expected.release += 1;
expected.assert_current();
drop(obj);
expected.release += 1;
expected.drop += 1;
expected.assert_current();
let obj = unsafe { init_only_superclasses(RcIvarSubclass::alloc()) };
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
// Accessing superclass ivars is valid
println!("{:?}", (**obj).ivars());
drop(obj);
expected.release += 1;
expected.drop += 1;
expected.assert_current();
}
#[test]
#[cfg_attr(not(debug_assertions), ignore = "only panics with debug assertions")]
#[should_panic = "tried to access uninitialized instance variable"]
fn access_invalid() {
declare_class!(
struct InvalidAccess;
unsafe impl ClassType for InvalidAccess {
type Super = NSObject;
type Mutability = InteriorMutable;
const NAME: &'static str = "InvalidAccess";
}
impl DeclaredClass for InvalidAccess {
// Type has to have a drop flag to detect invalid access
type Ivars = Retained<NSObject>;
}
);
let obj = unsafe { init_only_superclasses(InvalidAccess::alloc()) };
println!("{:?}", obj.ivars());
}
#[test]
#[should_panic = "panic in drop"]
#[cfg_attr(
any(feature = "unstable-c-unwind", target_arch = "x86"),
ignore = "panicking in Drop requires that we actually implement `dealloc` as `C-unwind`"
)]
fn test_panic_in_drop() {
declare_class!(
struct DropPanics;
unsafe impl ClassType for DropPanics {
type Super = NSObject;
type Mutability = InteriorMutable;
const NAME: &'static str = "DropPanics";
}
impl DeclaredClass for DropPanics {}
);
impl Drop for DropPanics {
fn drop(&mut self) {
panic!("panic in drop");
}
}
let obj = DropPanics::alloc().set_ivars(());
let obj: Retained<DropPanics> = unsafe { msg_send_id![super(obj), init] };
drop(obj);
}
#[test]
#[should_panic = "panic in ivar drop"]
#[cfg_attr(
any(feature = "unstable-c-unwind", target_arch = "x86"),
ignore = "panicking in Drop requires that we actually implement `dealloc` as `C-unwind`"
)]
fn test_panic_in_ivar_drop() {
struct DropPanics;
impl Drop for DropPanics {
fn drop(&mut self) {
panic!("panic in ivar drop");
}
}
declare_class!(
struct IvarDropPanics;
unsafe impl ClassType for IvarDropPanics {
type Super = NSObject;
type Mutability = InteriorMutable;
const NAME: &'static str = "IvarDropPanics";
}
impl DeclaredClass for IvarDropPanics {
type Ivars = DropPanics;
}
);
let obj = IvarDropPanics::alloc().set_ivars(DropPanics);
let obj: Retained<IvarDropPanics> = unsafe { msg_send_id![super(obj), init] };
drop(obj);
}
}

View File

@@ -0,0 +1,195 @@
/// Helper for specifying the retain semantics for a given selector family.
///
/// Note that we can't actually check if a method is in a method family; only
/// whether the _selector_ is in a _selector_ family.
///
/// The slight difference here is:
/// - The method may be annotated with the `objc_method_family` attribute,
/// which would cause it to be in a different family. That this is not the
/// case is part of the `unsafe` contract of `msg_send_id!`.
/// - The method may not obey the added restrictions of the method family.
/// The added restrictions are:
/// - `new`, `alloc`, `copy` and `mutableCopy`: The method must return a
/// retainable object pointer type - we ensure this by making
/// `message_send_id` return `Retained`.
/// - `init`: The method must be an instance method and must return an
/// Objective-C pointer type - We ensure this by taking `Allocated<T>`,
/// which means it can't be a class method!
///
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments>
// TODO: Use an enum instead of u8 here when stable
#[derive(Debug)]
pub struct RetainSemantics<const INNER: u8> {}
pub type New = RetainSemantics<1>;
pub type Alloc = RetainSemantics<2>;
pub type Init = RetainSemantics<3>;
pub type CopyOrMutCopy = RetainSemantics<4>;
pub type Other = RetainSemantics<5>;
pub const fn retain_semantics(selector: &str) -> u8 {
let selector = selector.as_bytes();
match (
in_selector_family(selector, b"new"),
in_selector_family(selector, b"alloc"),
in_selector_family(selector, b"init"),
in_selector_family(selector, b"copy"),
in_selector_family(selector, b"mutableCopy"),
) {
(true, false, false, false, false) => 1,
(false, true, false, false, false) => 2,
(false, false, true, false, false) => 3,
(false, false, false, true, false) => 4,
(false, false, false, false, true) => 4,
(false, false, false, false, false) => 5,
_ => unreachable!(),
}
}
/// Checks whether a given selector is said to be in a given selector family.
///
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families>
const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool {
// Skip leading underscores from selector
loop {
selector = match selector {
[b'_', rest @ ..] => rest,
_ => break,
}
}
// Compare each character
loop {
(selector, family) = match (selector, family) {
// Remaining items
([s, selector @ ..], [f, family @ ..]) => {
if *s == *f {
// Next iteration
(selector, family)
} else {
// Family does not begin with selector
return false;
}
}
// Equal
([], []) => {
return true;
}
// Selector can't be part of familiy if smaller than it
([], _) => {
return false;
}
// Remaining items in selector
// -> ensure next character is not lowercase
([s, ..], []) => {
return !s.is_ascii_lowercase();
}
}
}
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::*;
#[test]
fn test_in_selector_family() {
#[track_caller]
fn assert_in_family(selector: &str, family: &str) {
assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
let selector = selector.to_string() + "\0";
assert!(in_selector_family(selector.as_bytes(), family.as_bytes()));
}
#[track_caller]
fn assert_not_in_family(selector: &str, family: &str) {
assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
let selector = selector.to_string() + "\0";
assert!(!in_selector_family(selector.as_bytes(), family.as_bytes()));
}
// Common cases
assert_in_family("alloc", "alloc");
assert_in_family("allocWithZone:", "alloc");
assert_not_in_family("dealloc", "alloc");
assert_not_in_family("initialize", "init");
assert_not_in_family("decimalNumberWithDecimal:", "init");
assert_in_family("initWithCapacity:", "init");
assert_in_family("_initButPrivate:withParam:", "init");
assert_not_in_family("description", "init");
assert_not_in_family("inIT", "init");
assert_not_in_family("init", "copy");
assert_not_in_family("copyingStuff:", "copy");
assert_in_family("copyWithZone:", "copy");
assert_not_in_family("initWithArray:copyItems:", "copy");
assert_in_family("copyItemAtURL:toURL:error:", "copy");
assert_not_in_family("mutableCopying", "mutableCopy");
assert_in_family("mutableCopyWithZone:", "mutableCopy");
assert_in_family("mutableCopyWithZone:", "mutableCopy");
assert_in_family(
"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
"new",
);
assert_in_family(
"newScriptingObjectOfClass:forValueForKey:withContentsValue:properties:",
"new",
);
assert_not_in_family("newsstandAssetDownload", "new");
// Trying to weed out edge-cases:
assert_in_family("__abcDef", "abc");
assert_in_family("_abcDef", "abc");
assert_in_family("abcDef", "abc");
assert_in_family("___a", "a");
assert_in_family("__a", "a");
assert_in_family("_a", "a");
assert_in_family("a", "a");
assert_not_in_family("_abcdef", "abc");
assert_not_in_family("_abcdef", "def");
assert_not_in_family("_bcdef", "abc");
assert_not_in_family("a_bc", "abc");
assert_not_in_family("abcdef", "abc");
assert_not_in_family("abcdef", "def");
assert_not_in_family("abcdef", "abb");
assert_not_in_family("___", "a");
assert_not_in_family("_", "a");
assert_not_in_family("", "a");
assert_in_family("copy", "copy");
assert_in_family("copy:", "copy");
assert_in_family("copyMe", "copy");
assert_in_family("_copy", "copy");
assert_in_family("_copy:", "copy");
assert_in_family("_copyMe", "copy");
assert_not_in_family("copying", "copy");
assert_not_in_family("copying:", "copy");
assert_not_in_family("_copying", "copy");
assert_not_in_family("Copy", "copy");
assert_not_in_family("COPY", "copy");
// Empty family (not supported)
assert_in_family("___", "");
assert_in_family("__", "");
assert_in_family("_", "");
assert_in_family("", "");
assert_not_in_family("_a", "");
assert_not_in_family("a", "");
assert_in_family("_A", "");
assert_in_family("A", "");
// Double-colon selectors
assert_in_family("abc::abc::", "abc");
assert_in_family("abc:::", "abc");
assert_in_family("abcDef::xyz:", "abc");
// Invalid selector (probably)
assert_not_in_family("::abc:", "abc");
}
}

117
vendor/objc2/src/__macro_helpers/mod.rs vendored Normal file
View File

@@ -0,0 +1,117 @@
pub use core::borrow::{Borrow, BorrowMut};
pub use core::cell::UnsafeCell;
pub use core::convert::{AsMut, AsRef};
pub use core::marker::{PhantomData, Sized};
pub use core::mem::{size_of, ManuallyDrop, MaybeUninit};
pub use core::ops::{Deref, DerefMut};
pub use core::option::Option::{self, None, Some};
pub use core::primitive::{bool, isize, str, u8};
pub use core::{compile_error, concat, panic, stringify};
// TODO: Use `core::cell::LazyCell`
pub use std::sync::Once;
mod cache;
mod common_selectors;
mod convert;
mod declare_class;
pub(crate) mod declared_ivars;
mod method_family;
mod msg_send;
mod msg_send_id;
mod writeback;
pub use self::cache::{CachedClass, CachedSel};
pub use self::common_selectors::{alloc_sel, dealloc_sel, init_sel, new_sel};
pub use self::convert::{ConvertArgument, ConvertArguments, ConvertReturn, TupleExtender};
pub use self::declare_class::{
assert_mutability_matches_superclass_mutability, ClassBuilderHelper,
ClassProtocolMethodsBuilder, IdReturnValue, MaybeOptionId, MessageRecieveId,
ValidSubclassMutability,
};
pub use self::declared_ivars::DeclaredIvarsHelper;
pub use self::method_family::{
retain_semantics, Alloc, CopyOrMutCopy, Init, New, Other, RetainSemantics,
};
pub use self::msg_send::MsgSend;
pub use self::msg_send_id::{MaybeUnwrap, MsgSendId, MsgSendSuperId};
/// Disallow using this passed in value in const and statics for forwards
/// compatibility (this function is not a `const` function).
#[inline]
pub fn disallow_in_static<T>(item: &'static T) -> &'static T {
item
}
/// Helper struct for emitting the module info that macOS 32-bit requires.
///
/// <https://github.com/llvm/llvm-project/blob/release/13.x/clang/lib/CodeGen/CGObjCMac.cpp#L5211-L5234>
#[repr(C)]
#[derive(Debug)]
pub struct ModuleInfo {
version: usize,
size: usize,
name: *const u8,
symtab: *const (),
}
// SAFETY: ModuleInfo is immutable.
unsafe impl Sync for ModuleInfo {}
impl ModuleInfo {
/// This is hardcoded in clang as 7.
const VERSION: usize = 7;
pub const fn new(name: *const u8) -> Self {
Self {
version: Self::VERSION,
size: core::mem::size_of::<Self>(),
name,
// We don't expose any symbols
symtab: core::ptr::null(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "objc2-proc-macros")]
use crate::__hash_idents;
#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_different() {
assert_ne!(__hash_idents!(abc), __hash_idents!(def));
}
#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_same_no_equal() {
assert_ne!(__hash_idents!(abc), __hash_idents!(abc));
assert_ne!(__hash_idents!(abc def ghi), __hash_idents!(abc def ghi));
}
#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_exact_same_ident() {
macro_rules! x {
($x:ident) => {
(__hash_idents!($x), __hash_idents!($x))
};
}
let (ident1, ident2) = x!(abc);
// This is a limitation of `__hash_idents`, ideally we'd like these
// to be different!
assert_eq!(ident1, ident2);
}
#[test]
#[cfg_attr(
not(all(target_vendor = "apple", target_os = "macos", target_arch = "x86")),
ignore = "Only relevant on macOS 32-bit"
)]
fn ensure_size_of_module_info() {
assert_eq!(core::mem::size_of::<ModuleInfo>(), 16);
}
}

View File

@@ -0,0 +1,285 @@
use core::mem::ManuallyDrop;
use core::ptr;
use crate::encode::RefEncode;
use crate::mutability::IsMutable;
use crate::rc::Retained;
use crate::runtime::{AnyClass, AnyObject, MessageReceiver, Sel};
use crate::{ClassType, Encode, Message};
use super::{ConvertArguments, ConvertReturn, TupleExtender};
pub trait MsgSend: Sized {
type Inner: ?Sized + RefEncode;
fn into_raw_receiver(self) -> *mut AnyObject;
#[inline]
#[track_caller]
unsafe fn send_message<A, R>(self, sel: Sel, args: A) -> R
where
A: ConvertArguments,
R: ConvertReturn,
{
let (args, stored) = A::__into_arguments(args);
// SAFETY: Upheld by caller
let result = unsafe { MessageReceiver::send_message(self.into_raw_receiver(), sel, args) };
// TODO: If we want `objc_retainAutoreleasedReturnValue` to
// work, we must not do any work before it has been run; so
// somehow, we should do that _before_ this call!
//
// SAFETY: The argument was passed to the message sending
// function, and the stored values are only processed this
// once. See `src/__macro_helpers/writeback.rs` for
// details.
unsafe { A::__process_after_message_send(stored) };
R::__from_return(result)
}
#[inline]
#[track_caller]
unsafe fn send_super_message<A, R>(self, superclass: &AnyClass, sel: Sel, args: A) -> R
where
A: ConvertArguments,
R: ConvertReturn,
{
let (args, stored) = A::__into_arguments(args);
// SAFETY: Upheld by caller
let result = unsafe {
MessageReceiver::send_super_message(self.into_raw_receiver(), superclass, sel, args)
};
// SAFETY: Same as in send_message above.
unsafe { A::__process_after_message_send(stored) };
R::__from_return(result)
}
#[inline]
#[track_caller]
unsafe fn send_super_message_static<A, R>(self, sel: Sel, args: A) -> R
where
Self::Inner: ClassType,
<Self::Inner as ClassType>::Super: ClassType,
A: ConvertArguments,
R: ConvertReturn,
{
unsafe { self.send_super_message(<Self::Inner as ClassType>::Super::class(), sel, args) }
}
// Error functions below. See MsgSendId::send_message_id_error for further
// details.
//
// Some of this could be abstracted away using closures, but that would
// interfere with `#[track_caller]`, so we avoid doing that.
#[inline]
#[track_caller]
unsafe fn send_message_error<A, E>(self, sel: Sel, args: A) -> Result<(), Retained<E>>
where
*mut *mut E: Encode,
A: TupleExtender<*mut *mut E>,
<A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
E: Message,
{
let mut err: *mut E = ptr::null_mut();
let args = args.add_argument(&mut err);
let res: bool = unsafe { self.send_message(sel, args) };
if res {
Ok(())
} else {
Err(unsafe { encountered_error(err) })
}
}
#[inline]
#[track_caller]
unsafe fn send_super_message_error<A, E>(
self,
superclass: &AnyClass,
sel: Sel,
args: A,
) -> Result<(), Retained<E>>
where
*mut *mut E: Encode,
A: TupleExtender<*mut *mut E>,
<A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
E: Message,
{
let mut err: *mut E = ptr::null_mut();
let args = args.add_argument(&mut err);
let res: bool = unsafe { self.send_super_message(superclass, sel, args) };
if res {
Ok(())
} else {
Err(unsafe { encountered_error(err) })
}
}
#[inline]
#[track_caller]
unsafe fn send_super_message_static_error<A, E>(
self,
sel: Sel,
args: A,
) -> Result<(), Retained<E>>
where
Self::Inner: ClassType,
<Self::Inner as ClassType>::Super: ClassType,
*mut *mut E: Encode,
A: TupleExtender<*mut *mut E>,
<A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
E: Message,
{
let mut err: *mut E = ptr::null_mut();
let args = args.add_argument(&mut err);
let res: bool = unsafe { self.send_super_message_static(sel, args) };
if res {
Ok(())
} else {
Err(unsafe { encountered_error(err) })
}
}
}
#[cold]
#[track_caller]
unsafe fn encountered_error<E: Message>(err: *mut E) -> Retained<E> {
// SAFETY: Ensured by caller
unsafe { Retained::retain(err) }
.expect("error parameter should be set if the method returns NO")
}
impl<T: ?Sized + MessageReceiver> MsgSend for T {
type Inner = T::__Inner;
#[inline]
fn into_raw_receiver(self) -> *mut AnyObject {
MessageReceiver::__as_raw_receiver(self)
}
}
impl<'a, T: ?Sized + Message> MsgSend for &'a Retained<T> {
type Inner = T;
#[inline]
fn into_raw_receiver(self) -> *mut AnyObject {
(Retained::as_ptr(self) as *mut T).cast()
}
}
impl<'a, T: ?Sized + Message + IsMutable> MsgSend for &'a mut Retained<T> {
type Inner = T;
#[inline]
fn into_raw_receiver(self) -> *mut AnyObject {
Retained::as_mut_ptr(self).cast()
}
}
impl<T: ?Sized + Message> MsgSend for ManuallyDrop<Retained<T>> {
type Inner = T;
#[inline]
fn into_raw_receiver(self) -> *mut AnyObject {
Retained::into_raw(ManuallyDrop::into_inner(self)).cast()
}
}
#[cfg(test)]
mod tests {
use crate::rc::{autoreleasepool, RcTestObject, ThreadTestData};
use crate::runtime::NSObject;
use crate::{
declare_class, msg_send, msg_send_id, mutability, test_utils, ClassType, DeclaredClass,
};
use super::*;
#[test]
fn test_send_message_manuallydrop() {
let obj = ManuallyDrop::new(test_utils::custom_object());
unsafe {
let _: () = msg_send![obj, release];
};
// `obj` is consumed, can't use here
}
macro_rules! test_error_bool {
($expected:expr, $($obj:tt)*) => {
// Succeeds
let res: Result<(), Retained<RcTestObject>> = unsafe {
msg_send![$($obj)*, boolAndShouldError: false, error: _]
};
assert_eq!(res, Ok(()));
$expected.assert_current();
// Errors
let res = autoreleasepool(|_pool| {
// `Ok` type is inferred to be `()`
let res: Retained<RcTestObject> = unsafe {
msg_send![$($obj)*, boolAndShouldError: true, error: _]
}.expect_err("not err");
$expected.alloc += 1;
$expected.init += 1;
$expected.autorelease += 1;
$expected.retain += 1;
$expected.assert_current();
res
});
$expected.release += 1;
$expected.assert_current();
drop(res);
$expected.release += 1;
$expected.drop += 1;
$expected.assert_current();
}
}
declare_class!(
#[derive(Debug, PartialEq, Eq)]
struct RcTestObjectSubclass;
unsafe impl ClassType for RcTestObjectSubclass {
#[inherits(NSObject)]
type Super = RcTestObject;
type Mutability = mutability::Immutable;
const NAME: &'static str = "RcTestObjectSubclass";
}
impl DeclaredClass for RcTestObjectSubclass {}
);
#[cfg_attr(not(test), allow(unused))]
impl RcTestObjectSubclass {
fn new() -> Retained<Self> {
unsafe { msg_send_id![Self::class(), new] }
}
}
#[test]
fn test_error_bool() {
let mut expected = ThreadTestData::current();
let cls = RcTestObject::class();
test_error_bool!(expected, cls);
let obj = RcTestObject::new();
expected.alloc += 1;
expected.init += 1;
test_error_bool!(expected, &obj);
let obj = RcTestObjectSubclass::new();
expected.alloc += 1;
expected.init += 1;
test_error_bool!(expected, &obj);
test_error_bool!(expected, super(&obj));
test_error_bool!(expected, super(&obj, RcTestObjectSubclass::class()));
test_error_bool!(expected, super(&obj, RcTestObject::class()));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,389 @@
//! Support for passing "out"-parameters to `msg_send!` and family.
//!
//! See clang's documentation:
//! <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#passing-to-an-out-parameter-by-writeback>
//!
//! Note: We differ from that in that we do not create a temporary, whoose
//! address we then work on; instead, we directly reuse the pointer that the
//! user provides (since, if it's a mutable pointer, we know that it's not
//! shared elsewhere in the program, and hence it is safe to modify directly).
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use super::ConvertArgument;
use crate::rc::Retained;
use crate::Message;
// Note the `'static` bound here - this may not be necessary, but I'm unsure
// of the exact requirements, so we better keep it for now.
impl<T: Message + 'static> ConvertArgument for &mut Retained<T> {
// We use `*mut T` as the inner value instead of `NonNull<T>`, since we
// want to do debug checking that the value hasn't unexpectedly been
// overwritten to contain NULL (which is clear UB, but the user might have
// made a mistake).
type __Inner = NonNull<*mut T>;
type __StoredBeforeMessage = (
// A copy of the argument, so that we can retain it after the message
// send. Ideally, we'd work with e.g. `&mut *mut T`, but we can't do
// that inside the generic context of `MessageArguments::__invoke`.
Self::__Inner,
// A pointer to the old value stored in the `Retained`, so that we can
// release if after the message send.
NonNull<T>,
);
#[inline]
fn __from_declared_param(_inner: Self::__Inner) -> Self {
todo!("`&mut Retained<_>` is not supported in `declare_class!` yet")
}
#[inline]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
let ptr: NonNull<Retained<T>> = NonNull::from(self);
// `Retained` is `#[repr(transparent)]` over `NonNull`.
let ptr: NonNull<NonNull<T>> = ptr.cast();
// SAFETY: The value came from `&mut _`, and we only read a pointer.
let old: NonNull<T> = unsafe { *ptr.as_ptr() };
// `NonNull<T>` has the same layout as `*mut T`.
let ptr: NonNull<*mut T> = ptr.cast();
(ptr, (ptr, old))
}
#[inline]
unsafe fn __process_after_message_send((ptr, old): Self::__StoredBeforeMessage) {
// In terms of provenance, we roughly want to do the following:
// ```
// fn do(value: &mut Retained<T>) {
// let old = value.clone();
// msg_send![... value ...];
// let _ = value.clone();
// drop(old);
// }
// ```
//
// Which is definitly valid under stacked borrows! See also this
// playground link for testing something equivalent in Miri:
// <https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ef8ecfb54a11b9a59ae17cc7edfbef3d>
//
//
//
// In Objective-C terms, we want to retain the new value and release
// the old, and importantly, in that order (such that we don't dealloc
// the value if it didn't change). So something like this:
// ```
// fn do(value: &mut Retained) {
// let old = *value;
// msg_send![... value ...];
// objc_retain(*value);
// objc_release(old);
// }
// ```
//
// Note that using a mutable `Retained<T>` is perfectly sound, since while
// we may intermittently have a retain count of 2 to the value, after
// the function returns we're guaranteed to be back to 1.
// SAFETY: Caller ensures that the pointer is either left as-is, or is
// safe to retain at this point.
let new: Option<Retained<T>> = unsafe { Retained::retain(*ptr.as_ptr()) };
// We ignore the result of `retain`, since it always returns the same
// value as was given (and it would be unnecessary work to write that
// value back into `ptr` again).
let _new = ManuallyDrop::new(new);
#[cfg(debug_assertions)]
if _new.is_none() {
panic!("found that NULL was written to `&mut Retained<_>`, which is UB! You should handle this with `&mut Option<Retained<_>>` instead");
}
// SAFETY: The old pointer was valid when it was constructed.
//
// If the message send modified `ptr`, they would have left a +1
// retain count on the old pointer; so either we have +1 from that, or
// the message send didn't modify the pointer and we instead have +1
// retain count from the `retain` above.
let _: Retained<T> = unsafe { Retained::new_nonnull(old) };
}
}
impl<T: Message + 'static> ConvertArgument for &mut Option<Retained<T>> {
type __Inner = NonNull<*mut T>;
type __StoredBeforeMessage = (Self::__Inner, *mut T);
#[inline]
fn __from_declared_param(_inner: Self::__Inner) -> Self {
todo!("`&mut Option<Retained<_>>` is not supported in `declare_class!` yet")
}
#[inline]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
let ptr: NonNull<Option<Retained<T>>> = NonNull::from(self);
// `Option<Retained<T>>` has the same memory layout as `*mut T`.
let ptr: NonNull<*mut T> = ptr.cast();
// SAFETY: Same as for `&mut Retained`
let old: *mut T = unsafe { *ptr.as_ptr() };
(ptr, (ptr, old))
}
#[inline]
unsafe fn __process_after_message_send((ptr, old): Self::__StoredBeforeMessage) {
// SAFETY: Same as for `&mut Retained`
let new: Option<Retained<T>> = unsafe { Retained::retain(*ptr.as_ptr()) };
let _ = ManuallyDrop::new(new);
// SAFETY: Same as for `&mut Retained`
//
// Note: We explicitly keep the `if old == nil { objc_release(old) }`
// check, since we expect that the user would often do:
//
// ```
// let mut value = None
// do(&mut value);
// ```
//
// And in that case, we can elide the `objc_release`!
let _: Option<Retained<T>> = unsafe { Retained::from_raw(old) };
}
}
// Note: For `Option<&mut ...>` we explicitly want to do the `if Some` checks
// before anything else, since whether `None` or `Some` was passed is often
// known at compile-time, and for the `None` case it would be detrimental to
// have extra `retain/release` calls here.
impl<T: Message + 'static> ConvertArgument for Option<&mut Retained<T>> {
type __Inner = Option<NonNull<*mut T>>;
type __StoredBeforeMessage = Option<(NonNull<*mut T>, NonNull<T>)>;
#[inline]
fn __from_declared_param(_inner: Self::__Inner) -> Self {
todo!("`Option<&mut Retained<_>>` is not supported in `declare_class!` yet")
}
#[inline]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
if let Some(this) = self {
let (ptr, stored) = this.__into_argument();
(Some(ptr), Some(stored))
} else {
(None, None)
}
}
#[inline]
unsafe fn __process_after_message_send(stored: Self::__StoredBeforeMessage) {
if let Some(stored) = stored {
// SAFETY: Checked by caller
unsafe { <&mut Retained<T>>::__process_after_message_send(stored) };
}
}
}
impl<T: Message + 'static> ConvertArgument for Option<&mut Option<Retained<T>>> {
type __Inner = Option<NonNull<*mut T>>;
type __StoredBeforeMessage = Option<(NonNull<*mut T>, *mut T)>;
#[inline]
fn __from_declared_param(_inner: Self::__Inner) -> Self {
todo!("`Option<&mut Option<Retained<_>>>` is not supported in `declare_class!` yet")
}
#[inline]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
if let Some(this) = self {
let (ptr, stored) = this.__into_argument();
(Some(ptr), Some(stored))
} else {
(None, None)
}
}
#[inline]
unsafe fn __process_after_message_send(stored: Self::__StoredBeforeMessage) {
if let Some(stored) = stored {
// SAFETY: Checked by caller
unsafe { <&mut Option<Retained<T>>>::__process_after_message_send(stored) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rc::{autoreleasepool, Allocated, RcTestObject, ThreadTestData};
use crate::{msg_send, msg_send_id, ClassType};
#[test]
fn test_bool_error() {
let mut expected = ThreadTestData::current();
fn bool_error(should_error: bool, error: Option<&mut Option<Retained<RcTestObject>>>) {
let cls = RcTestObject::class();
let did_succeed: bool =
unsafe { msg_send![cls, boolAndShouldError: should_error, error: error] };
assert_ne!(should_error, did_succeed);
}
bool_error(false, None);
bool_error(true, None);
expected.assert_current();
fn helper(
expected: &mut ThreadTestData,
should_error: bool,
mut error: Option<Retained<RcTestObject>>,
) {
std::dbg!(should_error, &error);
autoreleasepool(|_| {
bool_error(should_error, Some(&mut error));
if should_error {
expected.alloc += 1;
expected.init += 1;
expected.autorelease += 1;
}
expected.assert_current();
});
if should_error {
expected.release += 1;
}
expected.assert_current();
if error.is_some() {
expected.release += 1;
expected.drop += 1;
}
drop(error);
expected.assert_current();
}
helper(&mut expected, false, None);
expected.retain += 1;
helper(&mut expected, true, None);
expected.alloc += 1;
expected.init += 1;
expected.retain += 1;
expected.release += 1;
helper(&mut expected, false, Some(RcTestObject::new()));
expected.alloc += 1;
expected.init += 1;
expected.retain += 1;
expected.release += 1;
expected.drop += 1;
helper(&mut expected, true, Some(RcTestObject::new()));
}
#[test]
#[cfg_attr(
any(
not(debug_assertions),
all(not(target_pointer_width = "64"), feature = "catch-all")
),
ignore = "invokes UB which is only caught with debug_assertions"
)]
#[should_panic = "found that NULL was written to `&mut Retained<_>`, which is UB! You should handle this with `&mut Option<Retained<_>>` instead"]
fn test_debug_check_ub() {
let cls = RcTestObject::class();
let mut param: Retained<_> = RcTestObject::new();
let _: () = unsafe { msg_send![cls, outParamNull: &mut param] };
}
// TODO: Fix this in release mode with Apple's runtime
const AUTORELEASE_SKIPPED: bool = cfg!(feature = "gnustep-1-7");
#[test]
fn test_id_interaction() {
let mut expected = ThreadTestData::current();
let cls = RcTestObject::class();
let mut err: Retained<RcTestObject> = RcTestObject::new();
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
autoreleasepool(|_| {
let obj: Option<Retained<RcTestObject>> =
unsafe { msg_send_id![cls, idAndShouldError: false, error: &mut err] };
expected.alloc += 1;
expected.init += 1;
if !AUTORELEASE_SKIPPED {
expected.autorelease += 1;
expected.retain += 1;
}
expected.retain += 1;
expected.release += 1;
expected.assert_current();
drop(obj);
expected.release += 1;
if AUTORELEASE_SKIPPED {
expected.drop += 1;
}
expected.assert_current();
});
if !AUTORELEASE_SKIPPED {
expected.release += 1;
expected.drop += 1;
}
expected.assert_current();
drop(err);
expected.release += 1;
expected.drop += 1;
expected.assert_current();
}
#[test]
fn test_error_alloc() {
let mut expected = ThreadTestData::current();
// Succeeds
let mut error: Option<Retained<RcTestObject>> = None;
let res: Allocated<RcTestObject> = unsafe {
msg_send_id![RcTestObject::class(), allocAndShouldError: false, error: &mut error]
};
expected.alloc += 1;
expected.assert_current();
assert!(!Allocated::as_ptr(&res).is_null());
assert!(error.is_none());
drop(res);
expected.release += 1;
// Drop flag ensures uninitialized do not drop
// expected.drop += 1;
expected.assert_current();
// Errors
let res: Retained<RcTestObject> = autoreleasepool(|_pool| {
let mut error = None;
let res: Allocated<RcTestObject> = unsafe {
msg_send_id![RcTestObject::class(), allocAndShouldError: true, error: &mut error]
};
expected.alloc += 1;
expected.init += 1;
expected.autorelease += 1;
expected.retain += 1;
expected.assert_current();
assert!(Allocated::as_ptr(&res).is_null());
error.unwrap()
});
expected.release += 1;
expected.assert_current();
drop(res);
expected.release += 1;
expected.drop += 1;
expected.assert_current();
}
}

1034
vendor/objc2/src/encode.rs vendored Normal file

File diff suppressed because it is too large Load Diff

372
vendor/objc2/src/exception.rs vendored Normal file
View File

@@ -0,0 +1,372 @@
//! # `@throw` and `@try/@catch` exceptions.
//!
//! By default, if the [`msg_send!`] macro causes an exception to be thrown,
//! this will unwind into Rust, resulting in undefined behavior. However, this
//! crate has an `"catch-all"` feature which, when enabled, wraps each
//! [`msg_send!`] in a `@catch` and panics if an exception is caught,
//! preventing Objective-C from unwinding into Rust.
//!
//! Most of the functionality in this module is only available when the
//! `"exception"` feature is enabled.
//!
//! See the following links for more information:
//! - [Exception Programming Topics for Cocoa](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html)
//! - [The Objective-C Programming Language - Exception Handling](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocExceptionHandling.html)
//! - [Exception Handling in LLVM](https://llvm.org/docs/ExceptionHandling.html)
//!
//! [`msg_send!`]: crate::msg_send
// TODO: Test this with panic=abort, and ensure that the code-size is
// reasonable in that case.
#[cfg(feature = "exception")]
use core::ffi::c_void;
use core::fmt;
#[cfg(feature = "exception")]
use core::mem;
use core::ops::Deref;
use core::panic::RefUnwindSafe;
use core::panic::UnwindSafe;
#[cfg(feature = "exception")]
use core::ptr;
use std::error::Error;
use crate::encode::{Encoding, RefEncode};
#[cfg(feature = "exception")]
use crate::ffi;
use crate::rc::{autoreleasepool_leaking, Retained};
use crate::runtime::__nsstring::nsstring_to_str;
use crate::runtime::{AnyClass, AnyObject, NSObject, NSObjectProtocol};
use crate::{extern_methods, sel, Message};
/// An Objective-C exception.
///
/// While highly recommended that any exceptions you intend to throw are
/// subclasses of `NSException`, this is not required by the runtime (similar
/// to how Rust can panic with arbitary payloads using [`panic_any`]).
///
/// [`panic_any`]: std::panic::panic_any
#[repr(transparent)]
pub struct Exception(AnyObject);
unsafe impl RefEncode for Exception {
const ENCODING_REF: Encoding = Encoding::Object;
}
unsafe impl Message for Exception {}
impl Deref for Exception {
type Target = AnyObject;
#[inline]
fn deref(&self) -> &AnyObject {
&self.0
}
}
impl AsRef<AnyObject> for Exception {
#[inline]
fn as_ref(&self) -> &AnyObject {
self
}
}
impl Exception {
fn is_nsexception(&self) -> Option<bool> {
if self.class().responds_to(sel!(isKindOfClass:)) {
// SAFETY: We only use `isKindOfClass:` on NSObject
let obj: *const Exception = self;
let obj = unsafe { obj.cast::<NSObject>().as_ref().unwrap() };
// Get class dynamically instead of with `class!` macro
Some(obj.isKindOfClass(AnyClass::get("NSException")?))
} else {
Some(false)
}
}
}
extern_methods!(
unsafe impl Exception {
// Only safe on NSException
// Returns NSString
#[method_id(name)]
unsafe fn name(&self) -> Option<Retained<NSObject>>;
// Only safe on NSException
// Returns NSString
#[method_id(reason)]
unsafe fn reason(&self) -> Option<Retained<NSObject>>;
}
);
// Note: We can't implement `Send` nor `Sync` since the exception could be
// anything!
impl fmt::Debug for Exception {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "exception ")?;
// Attempt to present a somewhat usable error message if the exception
// is an instance of NSException.
if let Some(true) = self.is_nsexception() {
autoreleasepool_leaking(|pool| {
// SAFETY: Just checked that object is an NSException
let (name, reason) = unsafe { (self.name(), self.reason()) };
// SAFETY: `name` and `reason` are guaranteed to be NSString.
let name = name
.as_deref()
.map(|name| unsafe { nsstring_to_str(name, pool) });
let reason = reason
.as_deref()
.map(|reason| unsafe { nsstring_to_str(reason, pool) });
let obj: &AnyObject = self.as_ref();
write!(f, "{obj:?} '{}'", name.unwrap_or_default())?;
if let Some(reason) = reason {
write!(f, " reason:{reason}")?;
} else {
write!(f, " reason:(NULL)")?;
}
Ok(())
})
} else {
// Fall back to `AnyObject` Debug
write!(f, "{:?}", self.0)
}
}
}
impl fmt::Display for Exception {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
autoreleasepool_leaking(|pool| {
if let Some(true) = self.is_nsexception() {
// SAFETY: Just checked that object is an NSException
let reason = unsafe { self.reason() };
if let Some(reason) = &reason {
// SAFETY: `reason` is guaranteed to be NSString.
let reason = unsafe { nsstring_to_str(reason, pool) };
return write!(f, "{reason}");
}
}
write!(f, "unknown exception")
})
}
}
impl Error for Exception {}
impl UnwindSafe for Exception {}
impl RefUnwindSafe for Exception {}
/// Throws an Objective-C exception.
///
/// This is the Objective-C equivalent of Rust's [`panic!`].
///
///
/// # Safety
///
/// This unwinds from Objective-C, and the exception must be caught using an
/// Objective-C exception handler like [`catch`] (and specifically not
/// [`catch_unwind`]).
///
/// This also invokes undefined behaviour until `C-unwind` is stabilized, see
/// [RFC-2945] - you can try this out on nightly using the `unstable-c-unwind`
/// feature flag.
///
/// [`catch_unwind`]: std::panic::catch_unwind
/// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
#[inline]
#[cfg(feature = "exception")] // For consistency, not strictly required
pub unsafe fn throw(exception: Retained<Exception>) -> ! {
// We consume the exception object since we can't make any guarantees
// about its mutability.
let ptr = exception.0.as_ptr() as *mut ffi::objc_object;
// SAFETY: The object is valid and non-null (nil exceptions are not valid
// in the old runtime).
unsafe { ffi::objc_exception_throw(ptr) }
}
#[cfg(feature = "exception")]
unsafe fn try_no_ret<F: FnOnce()>(closure: F) -> Result<(), Option<Retained<Exception>>> {
#[cfg(not(feature = "unstable-c-unwind"))]
let f = {
extern "C" fn try_objc_execute_closure<F>(closure: &mut Option<F>)
where
F: FnOnce(),
{
// This is always passed Some, so it's safe to unwrap
let closure = closure.take().unwrap();
closure();
}
let f: extern "C" fn(&mut Option<F>) = try_objc_execute_closure;
let f: extern "C" fn(*mut c_void) = unsafe { mem::transmute(f) };
f
};
#[cfg(feature = "unstable-c-unwind")]
let f = {
extern "C-unwind" fn try_objc_execute_closure<F>(closure: &mut Option<F>)
where
F: FnOnce(),
{
// This is always passed Some, so it's safe to unwrap
let closure = closure.take().unwrap();
closure();
}
let f: extern "C-unwind" fn(&mut Option<F>) = try_objc_execute_closure;
let f: extern "C-unwind" fn(*mut c_void) = unsafe { mem::transmute(f) };
f
};
// Wrap the closure in an Option so it can be taken
let mut closure = Some(closure);
let context: *mut Option<F> = &mut closure;
let context = context.cast();
let mut exception = ptr::null_mut();
let success = unsafe { ffi::try_catch(f, context, &mut exception) };
if success == 0 {
Ok(())
} else {
// SAFETY:
// The exception is always a valid object or NULL.
//
// Since we do a retain inside `extern/exception.m`, the object has
// +1 retain count.
//
// Code throwing an exception know that they don't hold sole access to
// that object any more, so even if the type was originally mutable,
// it is okay to create a new `Retained` to it here.
Err(unsafe { Retained::from_raw(exception.cast()) })
}
}
/// Tries to execute the given closure and catches an Objective-C exception
/// if one is thrown.
///
/// This is the Objective-C equivalent of Rust's [`catch_unwind`].
/// Accordingly, if your Rust code is compiled with `panic=abort` this cannot
/// catch the exception.
///
/// [`catch_unwind`]: std::panic::catch_unwind
///
///
/// # Errors
///
/// Returns a `Result` that is either `Ok` if the closure succeeded without an
/// exception being thrown, or an `Err` with the exception. The exception is
/// automatically released.
///
/// The exception is `None` in the extremely exceptional case that the
/// exception object is `nil`. This should basically never happen, but is
/// technically possible on some systems with `@throw nil`, or in OOM
/// situations.
///
///
/// # Safety
///
/// The given closure must not panic (e.g. normal Rust unwinding into this
/// causes undefined behaviour).
///
/// Additionally, this unwinds through the closure from Objective-C, which is
/// undefined behaviour until `C-unwind` is stabilized, see [RFC-2945] - you
/// can try this out on nightly using the `unstable-c-unwind` feature flag.
///
/// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
#[cfg(feature = "exception")]
pub unsafe fn catch<R>(
closure: impl FnOnce() -> R + UnwindSafe,
) -> Result<R, Option<Retained<Exception>>> {
let mut value = None;
let value_ref = &mut value;
let closure = move || {
*value_ref = Some(closure());
};
let result = unsafe { try_no_ret(closure) };
// If the try succeeded, value was set so it's safe to unwrap
result.map(|()| value.unwrap_or_else(|| unreachable!()))
}
#[cfg(test)]
#[cfg(feature = "exception")]
mod tests {
use alloc::format;
use alloc::string::ToString;
use core::panic::AssertUnwindSafe;
use super::*;
use crate::msg_send_id;
#[test]
fn test_catch() {
let mut s = "Hello".to_string();
let result = unsafe {
catch(move || {
s.push_str(", World!");
s
})
};
assert_eq!(result.unwrap(), "Hello, World!");
}
#[test]
#[cfg_attr(
all(target_vendor = "apple", target_os = "macos", target_arch = "x86"),
ignore = "`NULL` exceptions are invalid on 32-bit / w. fragile runtime"
)]
fn test_catch_null() {
let s = "Hello".to_string();
let result = unsafe {
catch(move || {
if !s.is_empty() {
ffi::objc_exception_throw(ptr::null_mut())
}
s.len()
})
};
assert!(result.unwrap_err().is_none());
}
#[test]
#[cfg_attr(
feature = "catch-all",
ignore = "Panics inside `catch` when catch-all is enabled"
)]
fn test_catch_unknown_selector() {
let obj = AssertUnwindSafe(NSObject::new());
let ptr = Retained::as_ptr(&obj);
let result = unsafe {
catch(|| {
let _: Retained<NSObject> = msg_send_id![&*obj, copy];
})
};
let err = result.unwrap_err().unwrap();
assert_eq!(
format!("{err}"),
format!("-[NSObject copyWithZone:]: unrecognized selector sent to instance {ptr:?}"),
);
}
#[test]
fn test_throw_catch_object() {
let obj = NSObject::new();
// TODO: Investigate why this is required on GNUStep!
let _obj2 = obj.clone();
let obj: Retained<Exception> = unsafe { Retained::cast(obj) };
let ptr: *const Exception = &*obj;
let result = unsafe { catch(|| throw(obj)) };
let obj = result.unwrap_err().unwrap();
assert_eq!(format!("{obj:?}"), format!("exception <NSObject: {ptr:p}>"));
assert!(ptr::eq(&*obj, ptr));
}
}

233
vendor/objc2/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,233 @@
//! # Objective-C interface and runtime bindings
//!
//! Quick links:
//! - [All Topics][crate::topics].
//! - [About framework crates][crate::topics::about_generated].
//! - [List of framework crates][crate::topics::about_generated::list].
//!
//! Objective-C was the standard programming language on Apple platforms like
//! macOS, iOS, iPadOS, tvOS and watchOS. It is an object-oriented language
//! centered around "sending messages" to its instances - this can for the
//! most part be viewed as a function call.
//!
//! It has since been superseded by Swift, but most of the core libraries and
//! frameworks that are in use on Apple systems are still written in
//! Objective-C, and hence we would like the ability to interract with these
//! using Rust. This crate enables you to do that, in as safe a manner as
//! possible.
//!
//!
//! ## Basic usage
//!
//! This example illustrates major parts of the functionality in this crate:
//!
//! First, we allocate a new [`NSObject`] using [`ClassType::alloc`].
//! Next, we initialize this object. It is ensured to be deallocated using
//! [`rc::Retained`].
//! Now we're free to send messages to the object to our hearts desire using
//! the [`msg_send!`] or [`msg_send_id!`] macros (depending on the return type
//! of the method).
//! Finally, the `Retained` goes out of scope, and the object is released and
//! deallocated.
//!
//! ```
//! use objc2::{msg_send, msg_send_id, ClassType};
//! use objc2::ffi::NSUInteger;
//! use objc2::rc::Retained;
//! use objc2::runtime::{NSObject, NSObjectProtocol};
//!
//! // Creation
//!
//! let obj1: Retained<NSObject> = unsafe {
//! msg_send_id![NSObject::alloc(), init]
//! };
//! // Or
//! let obj2 = NSObject::new();
//!
//! // Usage
//!
//! let hash1: NSUInteger = unsafe { msg_send![&obj1, hash] };
//! let hash2: NSUInteger = unsafe { msg_send![&obj2, hash] };
//! assert_ne!(hash1, hash2);
//!
//! let is_kind: bool = unsafe {
//! msg_send![&obj1, isKindOfClass: NSObject::class()]
//! };
//! assert!(is_kind);
//!
//! let obj1_self: Retained<NSObject> = unsafe { msg_send_id![&obj1, self] };
//! assert_eq!(obj1, obj1_self);
//!
//! // Deallocation on drop
//! ```
//!
//! Note that this example contains a lot of `unsafe` (which should all
//! ideally be justified with a `// SAFETY` comment). This is required because
//! our compiler can verify very little about the Objective-C invocation,
//! including all argument and return types used in [`msg_send!`]. We could
//! have accidentally made `hash` an `f32`, or any other type, and this would
//! trigger undefined behaviour!
//!
//! See [the framework crates] for much more ergonomic usage of the system
//! frameworks like `Foundation`, `AppKit`, `UIKit` and so on.
//!
//! Anyhow, all of this `unsafe` nicely leads us to another feature that this
//! crate has:
//!
//! [`NSObject`]: crate::runtime::NSObject
//! [`rc::Retained`]: crate::rc::Retained
//! [the framework crates]: crate::topics::about_generated
//!
//!
//! ## Encodings and message type verification
//!
//! The Objective-C runtime includes encodings for each method that describe
//! the argument and return types. See the [`encode`] module for a full
//! overview of what this is.
//!
//! The important part is: To make message sending safer, all arguments and
//! return values for messages must implement [`encode::Encode`]. This allows
//! the Rust compiler to prevent you from passing e.g. a [`Vec`] into
//! Objective-C, which would both be UB and leak the vector.
//!
//! Furthermore, we can take advantage of the encodings provided by the
//! runtime to verify that the types used in Rust actually match the types
//! encoded for the method. This is not a perfect solution for ensuring safety
//! (some Rust types have the same Objective-C encoding, but are not
//! equivalent, such as `&T` and `*const T`), but it gets us much closer to
//! it!
//!
//! When `debug_assertions` are enabled we check the encoding every time you
//! send a message, and the message send will panic if they are not
//! equivalent.
//!
//! To take the example above, if we changed the `hash` method's return type
//! as in the following example, it'll panic if debug assertions are enabled:
//!
//! ```should_panic
//! # use objc2::{msg_send, ClassType};
//! # use objc2::runtime::NSObject;
//! #
//! # let obj1 = NSObject::new();
//! #
//! // Wrong return type - this is UB!
//! let hash1: f32 = unsafe { msg_send![&obj1, hash] };
//! #
//! # panic!("does not panic in release mode, so for testing we make it!");
//! ```
//!
//! This library contains further such debug checks.
//!
//! [`Vec`]: std::vec::Vec
//!
//!
//! ## Crate features
//!
//! This crate exports several optional cargo features, see [`Cargo.toml`] for
//! an overview and description of these.
//!
//! [`Cargo.toml`]: https://github.com/madsmtm/objc2/blob/master/crates/objc2/Cargo.toml
//!
//!
//! ## Support for other Operating Systems
//!
//! The bindings can be used on Linux or *BSD utilizing the
//! [GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2),
//! see the [`objc-sys`][`objc_sys`] crate for how to configure this.
//!
//!
//! ## Other functionality
//!
//! That was a quick introduction, this library also has [support for handling
//! exceptions][exc], [the ability to declare Objective-C
//! classes][declare_class!], [advanced reference-counting utilities][rc], and more -
//! peruse the documentation at will!
//!
//! [exc]: crate::exception
//! [rc]: crate::rc
#![no_std]
#![cfg_attr(
feature = "unstable-autoreleasesafe",
feature(negative_impls, auto_traits)
)]
#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![warn(missing_docs)]
#![warn(clippy::missing_errors_doc)]
#![warn(clippy::missing_panics_doc)]
// Update in Cargo.toml as well.
#![doc(html_root_url = "https://docs.rs/objc2/0.5.2")]
#[cfg(not(feature = "alloc"))]
compile_error!("The `alloc` feature currently must be enabled.");
#[cfg(not(feature = "std"))]
compile_error!("The `std` feature currently must be enabled.");
extern crate alloc;
extern crate std;
#[doc(no_inline)]
pub use objc_sys as ffi;
#[doc(no_inline)]
pub use self::encode::{Encode, Encoding, RefEncode};
pub use self::top_level_traits::{ClassType, DeclaredClass, Message, ProtocolType};
#[cfg(feature = "objc2-proc-macros")]
#[doc(hidden)]
pub use objc2_proc_macros::__hash_idents;
#[cfg(not(feature = "objc2-proc-macros"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __hash_idents {
// Noop; used to make our other macros a bit easier to read
($($x:tt)*) => {
()
};
}
// Note: While this is not public, it is still a breaking change to change,
// since framework crates rely on it.
#[doc(hidden)]
pub mod __framework_prelude;
#[doc(hidden)]
pub mod __macro_helpers;
pub mod encode;
pub mod exception;
mod macros;
pub mod mutability;
pub mod rc;
pub mod runtime;
#[cfg(test)]
mod test_utils;
mod top_level_traits;
#[cfg(any(doc, doctest, test))]
pub mod topics;
mod verify;
/// Deprecated location for a few things that are now in the [`runtime`]
/// module.
#[deprecated = "Moved to the `runtime` module"]
pub mod declare {
pub use super::runtime::{ClassBuilder, ProtocolBuilder};
use super::*;
/// Use [`runtime::ClassBuilder`] instead.
#[deprecated = "Use `runtime::ClassBuilder` instead."]
pub type ClassDecl = runtime::ClassBuilder;
/// Use [`runtime::ProtocolBuilder`] instead.
#[deprecated = "Use `runtime::ProtocolBuilder` instead."]
pub type ProtocolDecl = runtime::ProtocolBuilder;
}
// Link to Foundation to make NSObject work
#[cfg_attr(target_vendor = "apple", link(name = "Foundation", kind = "framework"))]
#[cfg_attr(
all(feature = "gnustep-1-7", not(feature = "unstable-compiler-rt")),
link(name = "gnustep-base", kind = "dylib")
)]
extern "C" {}

View File

@@ -0,0 +1,334 @@
/// Parse the given attributes, and gate the output on any `cfg` attributes
/// that were present in the set.
///
/// This is implemented as a tt-muncher, taking the following arguments:
/// - The attributes to be processed
/// - The output that the `cfg` attributes will be attached to
#[doc(hidden)]
#[macro_export]
macro_rules! __extract_and_apply_cfg_attributes {
// Base case
{
() // No attributes left to process
$($output:tt)*
} => {
$($output)*
};
// `cfg` attribute
{
(
#[cfg $($args:tt)*]
$($m_rest:tt)*
)
$($output:tt)*
} => {
// Apply the attribute and continue
#[cfg $($args)*]
{
$crate::__extract_and_apply_cfg_attributes! {
($($m_rest)*)
$($output)*
}
}
};
// Other attributes
{
(
#[$($m_ignored:tt)*]
$($m_rest:tt)*
)
$($output:tt)*
} => {
// Ignore the attribute, and continue parsing the rest
$crate::__extract_and_apply_cfg_attributes! {
($($m_rest)*)
$($output)*
}
};
}
/// Extract `#[method(...)]` or `#[method_id(...)]` and the `#[optional]`
/// attribute, and send it to another macro.
///
/// This will ensure that there is one and only one of the method attributes
/// present.
///
/// This takes the following arguments:
/// 1. The attributes to parse.
/// ($($m:tt)*)
///
/// 2. The output macro.
/// ($out_macro:path)
///
/// Further arguments are passed on to the output macro, with the following
/// arguments appended to it:
/// 1. The `method` or `method_id` attribute.
/// (#[$method_or_method_id:ident($($sel:tt)*)])
///
/// 2. The retain semantics, if any was present in the selector for
/// `#[method_id(...)]`.
///
/// One of `New`, `Alloc`, `Init`, `CopyOrMutCopy` and `Other`.
/// ($($retain_semantics:ident)?)
///
/// 3. The `optional` attribute, if any.
/// ($(#[optional])?)
///
/// 4. The remaining attributes.
/// ($(#[$($m_checked:tt)*])*)
#[doc(hidden)]
#[macro_export]
macro_rules! __extract_custom_attributes {
{
($($m:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__extract_custom_attributes_inner! {
($($m)*)
// No already parsed attributes
() // method/method_id
() // retain semantics
() // optional
() // checked
($out_macro)
$($macro_args)*
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extract_custom_attributes_inner {
// No method/method_id attribute found
{
// No attributes left to process
()
// And we found no `method` or `method_id` attributes
()
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__macro_helpers::compile_error!("must specify the desired selector using `#[method(...)]` or `#[method_id(...)]`");
};
// Base case
{
// No attributes left to process
()
// And we found a `method` or `method_id` attribute
($($m_method:tt)*)
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
// Output
$out_macro! {
$($macro_args)*
// Append attributes to the end of the macro arguments
($($m_method)*)
($($retain_semantics)*)
($($m_optional)*)
($($m_checked)*)
}
};
// `method` attribute
{
(
#[method($($sel:tt)*)]
$($rest:tt)*
)
// If no existing `method` nor `method_id` attributes exist
()
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__extract_custom_attributes_inner! {
($($rest)*)
// Add method attribute
(#[method($($sel)*)])
($($retain_semantics)*)
($($m_optional)*)
($($m_checked)*)
($out_macro)
$($macro_args)*
}
};
// Duplicate `method` attributes
{
(
#[method($($sel:tt)*)]
$($rest:tt)*
)
($($m_method:tt)*)
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__macro_helpers::compile_error!("cannot specify the `method`/`method_id` attribute twice");
};
// `method_id` attribute with retain semantics
{
(
#[method_id(@__retain_semantics $retain_semantics:ident $($sel:tt)*)]
$($rest:tt)*
)
// If no existing `method` nor `method_id` attributes exist
()
()
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__extract_custom_attributes_inner! {
($($rest)*)
// Add method_id attribute
(#[method_id($($sel)*)])
($retain_semantics)
($($m_optional)*)
($($m_checked)*)
($out_macro)
$($macro_args)*
}
};
// `method_id` attribute
{
(
#[method_id($($sel:tt)*)]
$($rest:tt)*
)
// If no existing `method` nor `method_id` attributes exist
()
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__extract_custom_attributes_inner! {
($($rest)*)
// Add method_id attribute
(#[method_id($($sel)*)])
($($retain_semantics)*)
($($m_optional)*)
($($m_checked)*)
($out_macro)
$($macro_args)*
}
};
// Duplicate `method_id` attributes
{
(
#[method_id($($sel:tt)*)]
$($rest:tt)*
)
($($m_method:tt)*)
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__macro_helpers::compile_error!("cannot specify the `method`/`method_id` attribute twice");
};
// `optional` attribute
{
(
#[optional]
$($rest:tt)*
)
($($m_method:tt)*)
($($retain_semantics:tt)*)
// If no existing `optional` attributes exist
()
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__extract_custom_attributes_inner! {
($($rest)*)
($($m_method)*)
($($retain_semantics)*)
// Add optional attribute
(#[optional])
($($m_checked)*)
($out_macro)
$($macro_args)*
}
};
// Duplicate `optional` attributes
{
(
#[optional]
$($rest:tt)*
)
($($m_method:tt)*)
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__macro_helpers::compile_error!("cannot specify the `optional` attribute twice");
};
// Other attributes
{
(
#[$($checked:tt)*]
$($rest:tt)*
)
($($m_method:tt)*)
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__extract_custom_attributes_inner! {
($($rest)*)
($($m_method)*)
($($retain_semantics)*)
($($m_optional)*)
(
$($m_checked)*
// The attribute is appended to the current set, since we've
// been consuming the attributes from the front.
#[$($checked)*]
)
($out_macro)
$($macro_args)*
}
};
}

View File

@@ -0,0 +1,317 @@
/// Forward selector and arguments to `MsgSend::send_message[_error]`.
///
/// Note: We can't forward to `msg_send!` since that doesn't support selectors
/// with space between.
#[doc(hidden)]
#[macro_export]
macro_rules! __method_msg_send {
// Selector with no arguments
(
($receiver:expr)
($sel:ident)
()
()
()
) => {
$crate::__msg_send_helper! {
($receiver)
(send_message)
($sel)
()
}
};
// Skip using `MainThreadMarker` in the message send.
//
// This is a purely textual match, and using e.g.
// `Foundation::MainThreadMarker` would fail - but that would just be
// detected as giving a wrong number of arguments, so it's fine for now.
(
($receiver:expr)
($($sel_rest:tt)*)
($arg:ident: MainThreadMarker $(, $($params_rest:tt)*)?)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
) => ({
let _ = $arg;
$crate::__method_msg_send! {
($receiver)
($($sel_rest)*)
($($($params_rest)*)?)
($($sel_parsed)*)
($($arg_parsed)*)
}
});
// Parse each argument-selector pair
(
($receiver:expr)
($($sel:ident)? : $($sel_rest:tt)*)
($arg:ident : $_arg_ty:ty $(, $($params_rest:tt)*)?)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
) => {
$crate::__method_msg_send! {
($receiver)
($($sel_rest)*)
($($($params_rest)*)?)
($($sel_parsed)* $($sel)? :)
($($arg_parsed)* $arg,)
}
};
// Handle path separator token
(
($receiver:expr)
($($sel:ident)? :: $($sel_rest:tt)*)
($arg1:ident : $_arg_ty1:ty, $arg2:ident : $_arg_ty2:ty $(, $($params_rest:tt)*)?)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
) => {
$crate::__method_msg_send! {
($receiver)
($($sel_rest)*)
($($($params_rest)*)?)
($($sel_parsed)* $($sel)? : :)
($($arg_parsed)* $arg1, $arg2,)
}
};
// Normal return
(
($receiver:expr)
()
()
// Notice the "+" here; we must make sure we actually _did_ parse
// a selector, and haven't just gotten an empty `#[method()]`.
($($sel_parsed:tt)+)
($($arg_parsed:tt)*)
) => {
$crate::__msg_send_helper! {
($receiver)
(send_message)
($($sel_parsed)*)
($($arg_parsed)*)
}
};
// Error return
(
($receiver:expr)
// `sel:_` without a corresponding argument
($sel:ident : _)
()
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
) => {
$crate::__msg_send_helper! {
($receiver)
// Use error method
(send_message_error)
($($sel_parsed)* $sel :)
($($arg_parsed)*)
}
};
// Variadic method
(
($receiver:expr)
($($sel:ident : _)?)
($($arg:ident :)? ...)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
) => ({
$crate::__macro_helpers::compile_error!(
"variadic methods are not yet supported"
)
});
// Mismatched selector/argument
(
($receiver:expr)
($($sel_rest:tt)*)
($($params_rest:tt)*)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
) => ({
$crate::__macro_helpers::compile_error!(
"number of arguments in function and selector did not match"
)
});
}
/// Same as `__method_msg_send`, just for `msg_send_id!`.
#[doc(hidden)]
#[macro_export]
macro_rules! __method_msg_send_id {
// Selector with no arguments
(
($receiver:expr)
($sel:ident)
()
()
()
($($retain_semantics:ident)?)
) => {
$crate::__msg_send_id_helper! {
($receiver)
($($retain_semantics)?)
(MsgSendId)
(send_message_id)
($sel)
()
}
};
// Skip using `MainThreadMarker` in the message send.
//
// This is a purely textual match, and using e.g.
// `Foundation::MainThreadMarker` would fail - but that would just be
// detected as giving a wrong number of arguments, so it's fine for now.
(
($receiver:expr)
($($sel_rest:tt)*)
($arg:ident: MainThreadMarker $(, $($params_rest:tt)*)?)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
($($retain_semantics:ident)?)
) => ({
let _ = $arg;
$crate::__method_msg_send_id! {
($receiver)
($($sel_rest)*)
($($($params_rest)*)?)
($($sel_parsed)*)
($($arg_parsed)*)
($($retain_semantics)?)
}
});
// Parse each argument-selector pair
(
($receiver:expr)
($($sel:ident)? : $($sel_rest:tt)*)
($arg:ident : $_arg_ty:ty $(, $($params_rest:tt)*)?)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
($($retain_semantics:ident)?)
) => {
$crate::__method_msg_send_id! {
($receiver)
($($sel_rest)*)
($($($params_rest)*)?)
($($sel_parsed)* $($sel)? :)
($($arg_parsed)* $arg,)
($($retain_semantics)?)
}
};
// Handle path separator token
(
($receiver:expr)
($($sel:ident)? :: $($sel_rest:tt)*)
($arg1:ident : $_arg_ty1:ty, $arg2:ident : $_arg_ty2:ty $(, $($params_rest:tt)*)?)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
($($retain_semantics:ident)?)
) => {
$crate::__method_msg_send_id! {
($receiver)
($($sel_rest)*)
($($($params_rest)*)?)
($($sel_parsed)* $($sel)? : :)
($($arg_parsed)* $arg1, $arg2,)
($($retain_semantics)?)
}
};
// Normal return
(
($receiver:expr)
()
()
// Notice the "+" here; we must make sure we actually _did_ parse
// a selector, and haven't just gotten an empty `#[method()]`.
($($sel_parsed:tt)+)
($($arg_parsed:tt)*)
($($retain_semantics:ident)?)
) => {
$crate::__msg_send_id_helper! {
($receiver)
($($retain_semantics)?)
(MsgSendId)
(send_message_id)
($($sel_parsed)*)
($($arg_parsed)*)
}
};
// Error return
(
($receiver:expr)
// `sel:_` without a corresponding argument
($sel:ident : _)
()
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
($($retain_semantics:ident)?)
) => {
$crate::__msg_send_id_helper! {
($receiver)
($($retain_semantics)?)
(MsgSendId)
// Use error method
(send_message_id_error)
($($sel_parsed)* $sel :)
($($arg_parsed)*)
}
};
// Variadic method
(
($receiver:expr)
($($sel:ident : _)?)
($($arg:ident :)? ...)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
($($retain_semantics:ident)?)
) => ({
$crate::__macro_helpers::compile_error!(
"variadic methods are not yet supported"
)
});
// Mismatched selector/argument
(
($receiver:expr)
($($sel_rest:tt)*)
($($params_rest:tt)*)
($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
($($retain_semantics:ident)?)
) => ({
$crate::__macro_helpers::compile_error!(
"number of arguments in function and selector did not match"
)
});
}

View File

@@ -0,0 +1,230 @@
#[doc(hidden)]
#[macro_export]
macro_rules! __msg_send_parse {
// No arguments
{
($error_fn:ident)
// Intentionally empty
()
()
($selector:ident $(,)?)
($fn:ident)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__msg_send_parse! {
($error_fn)
($selector)
()
()
($fn)
($out_macro)
$($macro_args)*
}
};
// tt-munch remaining `selector: argument` pairs, looking for a pattern
// that ends with `sel: _`.
{
($_error_fn:ident)
($($selector_output:tt)*)
($($argument_output:tt)*)
()
($fn:ident)
($out_macro:path)
$($macro_args:tt)*
} => ({
$out_macro! {
$($macro_args)*
($fn)
($($selector_output)*)
($($argument_output)*)
}
});
{
($error_fn:ident)
($($selector_output:tt)*)
($($argument_output:tt)*)
($selector:ident: _ $(,)?)
($fn:ident)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__msg_send_parse! {
($error_fn)
($($selector_output)* $selector:)
// Don't pass an argument
($($argument_output)*)
()
// Instead, we change the called function to the error function.
($error_fn)
($out_macro)
$($macro_args)*
}
};
{
($error_fn:ident)
($($selector_output:tt)*)
($($argument_output:tt)*)
($selector:ident : $argument:expr $(, $($rest:tt)*)?)
($fn:ident)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__msg_send_parse! {
($error_fn)
($($selector_output)* $selector:)
($($argument_output)* $argument,)
($($($rest)*)?)
($fn)
($out_macro)
$($macro_args)*
}
};
// Handle calls without comma between `selector: argument` pair.
{
($error_fn:ident)
// Intentionally empty
()
()
($($selector:ident : $argument:expr)*)
($fn:ident)
($out_macro:path)
$($macro_args:tt)*
} => {{
$crate::__comma_between_args!(
($fn)
($(
", ",
$crate::__macro_helpers::stringify!($selector),
": ",
$crate::__macro_helpers::stringify!($argument),
)+)
$($macro_args)*
);
$crate::__msg_send_parse! {
($error_fn)
()
()
($($selector : $argument),*)
($fn)
($out_macro)
$($macro_args)*
}
}};
}
#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "unstable-msg-send-always-comma"))]
macro_rules! __comma_between_args {
($($args:tt)*) => {};
}
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "unstable-msg-send-always-comma")]
macro_rules! __comma_between_args {
// msg_send!
(
(send_super_message_static)
($($args:tt)*)
($obj:expr)
) => {
$crate::__comma_between_args_inner! {
("msg_send")
("super", $crate::__macro_helpers::stringify!(($obj)), $($args)*)
}
};
(
(send_super_message)
($($args:tt)*)
($obj:expr, $superclass:expr)
) => {
$crate::__comma_between_args_inner! {
("msg_send")
("super", $crate::__macro_helpers::stringify!(($obj, $superclass)), $($args)*)
}
};
(
(send_message)
($($args:tt)*)
($obj:expr)
) => {
$crate::__comma_between_args_inner! {
("msg_send")
($crate::__macro_helpers::stringify!($obj), $($args)*)
}
};
// msg_send_id!
(
(send_super_message_id_static)
($($args:tt)*)
($obj:expr)
()
(MsgSendSuperId)
) => {
$crate::__comma_between_args_inner! {
("msg_send_id")
("super", $crate::__macro_helpers::stringify!(($obj)), $($args)*)
}
};
(
(send_super_message_id)
($($args:tt)*)
($obj:expr, $superclass:expr)
()
(MsgSendSuperId)
) => {
$crate::__comma_between_args_inner! {
("msg_send_id")
("super", $crate::__macro_helpers::stringify!(($obj, $superclass)), $($args)*)
}
};
(
(send_message_id)
($($args:tt)*)
($obj:expr)
()
(MsgSendId)
) => {
$crate::__comma_between_args_inner! {
("msg_send_id")
($crate::__macro_helpers::stringify!($obj), $($args)*)
}
};
}
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "unstable-msg-send-always-comma")]
macro_rules! __comma_between_args_inner {
(
($macro_name:literal)
($($args:tt)*)
) => {
#[deprecated = $crate::__macro_helpers::concat!(
"using ", $macro_name, "! without a comma between arguments is ",
"technically not valid macro syntax, and may break in a future ",
"version of Rust. You should use the following instead:\n",
$macro_name, "![", $($args)* "]"
)]
#[inline]
fn __msg_send_missing_comma() {}
__msg_send_missing_comma();
};
}

View File

@@ -0,0 +1,222 @@
/// Detect instance vs. class method.
///
/// Will add:
/// ```ignore
/// (builder_method:ident)
/// (receiver:expr)
/// (receiver_ty:ty)
/// (params_prefix*)
/// (params_rest*)
/// ```
#[doc(hidden)]
#[macro_export]
macro_rules! __rewrite_self_param {
{
($($params:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$crate::__rewrite_self_param_inner! {
// Duplicate params out so that we can match on `self`, while still
// using it as a function parameter
($($params)*)
($($params)*)
($out_macro)
$($macro_args)*
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __rewrite_self_param_inner {
// Instance method
{
(&self $($__params_rest:tt)*)
(&$self:ident $(, $($params_rest:tt)*)?)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_method)
($self)
(&Self)
(
&$self,
_: $crate::runtime::Sel,
)
($($($params_rest)*)?)
}
};
{
(&mut self $($__params_rest:tt)*)
(&mut $self:ident $(, $($params_rest:tt)*)?)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_method)
($self)
(&mut Self)
(
&mut $self,
_: $crate::runtime::Sel,
)
($($($params_rest)*)?)
}
};
{
(self: $__self_ty:ty $(, $($__params_rest:tt)*)?)
($self:ident: $self_ty:ty $(, $($params_rest:tt)*)?)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_method)
($self)
($self_ty)
(
$self: $self_ty,
_: $crate::runtime::Sel,
)
($($($params_rest)*)?)
}
};
{
(mut self: $__self_ty:ty $(, $($__params_rest:tt)*)?)
($mut:ident $self:ident: $self_ty:ty $(, $($params_rest:tt)*)?)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_method)
($self)
($self_ty)
(
$mut $self: $self_ty,
_: $crate::runtime::Sel,
)
($($($params_rest)*)?)
}
};
// `this: Type` or `_this: Type` instance method
// Workaround for arbitary self types being unstable
// https://doc.rust-lang.org/nightly/unstable-book/language-features/arbitrary-self-types.html
{
(mut this: $__self_ty:ty $(, $($__params_rest:tt)*)?)
($mut:ident $this:ident: $this_ty:ty $(, $($params_rest:tt)*)?)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_method)
($this)
($this_ty)
(
$mut $this: $this_ty,
_: $crate::runtime::Sel,
)
($($($params_rest)*)?)
}
};
{
(this: $__self_ty:ty $(, $($__params_rest:tt)*)?)
($this:ident: $this_ty:ty $(, $($params_rest:tt)*)?)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_method)
($this)
($this_ty)
(
$this: $this_ty,
_: $crate::runtime::Sel,
)
($($($params_rest)*)?)
}
};
{
(mut _this: $__self_ty:ty $(, $($__params_rest:tt)*)?)
($mut:ident $this:ident: $this_ty:ty $(, $($params_rest:tt)*)?)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_method)
($this)
($this_ty)
(
$mut $this: $this_ty,
_: $crate::runtime::Sel,
)
($($($params_rest)*)?)
}
};
{
(_this: $__self_ty:ty $(, $($__params_rest:tt)*)?)
($this:ident: $this_ty:ty $(, $($params_rest:tt)*)?)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_method)
($this)
($this_ty)
(
$this: $this_ty,
_: $crate::runtime::Sel,
)
($($($params_rest)*)?)
}
};
// Class method
{
($($__params:tt)*)
($($params_rest:tt)*)
($out_macro:path)
$($macro_args:tt)*
} => {
$out_macro! {
$($macro_args)*
(add_class_method)
(<Self as $crate::ClassType>::class())
(&$crate::runtime::AnyClass)
(
_: &$crate::runtime::AnyClass,
_: $crate::runtime::Sel,
)
($($params_rest)*)
}
};
}

1212
vendor/objc2/src/macros/declare_class.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
/// Not yet public API.
//
// Note: While this is not public, it is still a breaking change to change
// the API for this, since framework crates rely on it.
#[doc(hidden)]
#[macro_export]
macro_rules! extern_category {
(
$(#[$m:meta])*
$v:vis unsafe trait $name:ident {
$($methods:tt)*
}
$(#[$impl_m:meta])*
unsafe impl $category:ident for $ty:ty {}
) => {
$(#[$m])*
$v unsafe trait $name: ClassType {
// TODO: Do this better
$crate::__extern_protocol_rewrite_methods! {
$($methods)*
}
#[doc(hidden)]
const __UNSAFE_INNER: ();
}
$(#[$impl_m])*
unsafe impl $category for $ty {
const __UNSAFE_INNER: () = ();
}
};
}

506
vendor/objc2/src/macros/extern_class.rs vendored Normal file
View File

@@ -0,0 +1,506 @@
/// Create a new type to represent a class.
///
/// This is similar to an `@interface` declaration in Objective-C.
///
/// It is useful for things like `objc2-foundation`, which needs to create
/// interfaces to existing, externally defined classes like `NSString`,
/// `NSURL` and so on, but can also be useful for users that have custom
/// classes written in Objective-C that they want to access from Rust.
///
///
/// # Specification
///
/// The syntax is similar enough to Rust syntax that if you invoke the macro
/// with parentheses (as opposed to curly brackets), [`rustfmt` will be able to
/// format the contents][rustfmt-macros] (so e.g. as `extern_class!( ... );`).
///
/// The macro creates an opaque struct containing the superclass (which means
/// that auto traits are inherited from the superclass), and implements the
/// following traits for it to allow easier usage as an Objective-C object:
///
/// - [`RefEncode`][crate::RefEncode]
/// - [`Message`][crate::Message]
/// - [`ClassType`][crate::ClassType]
/// - [`Deref<Target = $superclass>`][core::ops::Deref]
/// - [`DerefMut`][core::ops::DerefMut]
/// - [`AsRef<$inheritance_chain>`][AsRef]
/// - [`AsMut<$inheritance_chain>`][AsMut]
/// - [`Borrow<$inheritance_chain>`][core::borrow::Borrow]
/// - [`BorrowMut<$inheritance_chain>`][core::borrow::BorrowMut]
///
/// The macro allows specifying zero-sized fields like [`PhantomData`] on the
/// struct.
///
/// You can add most attributes to the class, including `#[cfg(...)]`,
/// `#[derive(...)]` and doc comments (but not ABI-modifying attributes like
/// `#[repr(...)]`).
///
/// [rustfmt-macros]: https://github.com/rust-lang/rustfmt/discussions/5437
/// [`PhantomData`]: core::marker::PhantomData
///
///
/// ## `ClassType` implementation
///
/// The syntax of this macro neatly documents that it implements the
/// [`ClassType`] trait for you, though to do so you need to provide it the
/// following:
/// - The [`Super`] class.
///
/// Due to Rust trait limitations, specifying e.g. the superclass `NSData`
/// would not give you the ability to convert via. `AsRef` to `NSObject`.
/// Therefore, you may optionally specify additional parts of the
/// inheritance chain using an `#[inherits(...)]` attribute.
/// - The class' [`Mutability`].
/// - Optionally, the class' [`NAME`] - if not specified, this will default to
/// the struct name.
///
/// You may add `#[cfg(...)]` attributes to the `ClassType` impl, and then it
/// will work as expected. No other attributes are supported.
///
/// [`ClassType`]: crate::ClassType
/// [`Super`]: crate::ClassType::Super
/// [`Mutability`]: crate::ClassType::Mutability
/// [`NAME`]: crate::ClassType::NAME
///
///
/// # Safety
///
/// This macro implements the three unsafe traits [`RefEncode`], [`Message`]
/// and [`ClassType`] for you, and while it can ensure most of the required
/// properties in those, it cannot ensure all of them.
///
/// In particular, when writing `unsafe` on `impl ClassType`, you must ensure
/// that:
/// 1. [`ClassType::Super`] is correct.
/// 2. [`ClassType::Mutability`] is correct.
///
/// See [`ClassType`'s safety section][ClassType#safety] for further
/// details on what this entails.
///
/// [`RefEncode`]: crate::encode::RefEncode
/// [`Message`]: crate::Message
/// [`ClassType::Super`]: crate::ClassType::Super
/// [`ClassType::Mutability`]: crate::ClassType::Mutability
/// [ClassType#safety]: crate::ClassType#safety
///
///
/// # Examples
///
/// Create a new type to represent the `NSFormatter` class (for demonstration,
/// `objc2_foundation::NSFormatter` exist for exactly this purpose).
///
/// ```
/// # #[cfg(not_available)]
/// use objc2_foundation::{NSCoding, NSCopying, NSObjectProtocol};
/// # use objc2::runtime::NSObjectProtocol;
/// use objc2::rc::Retained;
/// use objc2::runtime::NSObject;
/// use objc2::{extern_class, msg_send_id, mutability, ClassType};
///
/// extern_class!(
/// /// An example description.
/// #[derive(PartialEq, Eq, Hash)] // Uses the superclass' implementation
/// // Specify the class and struct name to be used
/// pub struct NSFormatter;
///
/// // Specify the superclass, in this case `NSObject`
/// unsafe impl ClassType for NSFormatter {
/// type Super = NSObject;
/// type Mutability = mutability::InteriorMutable;
/// // Optionally, specify the name of the class, if it differs from
/// // the struct name.
/// // const NAME: &'static str = "NSFormatter";
/// }
/// );
///
/// // Note: We have to specify the protocols for the superclasses as well,
/// // since Rust doesn't do inheritance.
/// unsafe impl NSObjectProtocol for NSFormatter {}
/// # #[cfg(not_available)]
/// unsafe impl NSCopying for NSFormatter {}
/// # #[cfg(not_available)]
/// unsafe impl NSCoding for NSFormatter {}
///
/// fn main() {
/// // Provided by the implementation of `ClassType`
/// let cls = NSFormatter::class();
///
/// // `NSFormatter` implements `Message`:
/// let obj: Retained<NSFormatter> = unsafe { msg_send_id![cls, new] };
/// }
/// ```
///
/// Represent the `NSDateFormatter` class, using the `NSFormatter` type we
/// declared previously to specify as its superclass.
///
/// ```
/// # #[cfg(not_available)]
/// use objc2_foundation::{NSCoding, NSCopying, NSObjectProtocol};
/// # use objc2::runtime::NSObjectProtocol;
/// use objc2::runtime::NSObject;
/// use objc2::{extern_class, mutability, ClassType};
/// #
/// # extern_class!(
/// # #[derive(PartialEq, Eq, Hash)]
/// # pub struct NSFormatter;
/// #
/// # unsafe impl ClassType for NSFormatter {
/// # type Super = NSObject;
/// # type Mutability = mutability::InteriorMutable;
/// # }
/// # );
///
/// extern_class!(
/// #[derive(PartialEq, Eq, Hash)]
/// pub struct NSDateFormatter;
///
/// unsafe impl ClassType for NSDateFormatter {
/// // Specify the correct inheritance chain
/// #[inherits(NSObject)]
/// type Super = NSFormatter;
/// type Mutability = mutability::InteriorMutable;
/// }
/// );
///
/// // Similarly, we can specify the protocols that this implements here:
/// unsafe impl NSObjectProtocol for NSFormatter {}
/// # #[cfg(not_available)]
/// unsafe impl NSCopying for NSDateFormatter {}
/// # #[cfg(not_available)]
/// unsafe impl NSCoding for NSDateFormatter {}
/// ```
///
/// See the source code of `objc2-foundation` for many more examples.
#[doc(alias = "@interface")]
#[macro_export]
macro_rules! extern_class {
// No fields
(
$(#[$m:meta])*
$v:vis struct $name:ident;
$(#[$impl_m:meta])*
unsafe impl ClassType for $for:ty {
$(#[inherits($($inheritance_rest:ty),+)])?
type Super = $superclass:ty;
type Mutability = $mutability:ty;
$(const NAME: &'static str = $name_const:expr;)?
}
) => {
// Shorthand syntax for the following
$crate::extern_class!(
$(#[$m])*
$v struct $name {}
$(#[$impl_m])*
unsafe impl ClassType for $for {
$(#[inherits($($inheritance_rest),+)])?
type Super = $superclass;
type Mutability = $mutability;
$(const NAME: &'static str = $name_const;)?
}
);
};
(
$(#[$m:meta])*
$v:vis struct $name:ident {
$($field_vis:vis $field:ident: $field_ty:ty,)*
}
$(#[$impl_m:meta])*
unsafe impl ClassType for $for:ty {
$(#[inherits($($inheritance_rest:ty),+)])?
type Super = $superclass:ty;
type Mutability = $mutability:ty;
$(const NAME: &'static str = $name_const:expr;)?
}
) => {
$crate::__inner_extern_class!(
$(#[$m])*
$v struct $name<> {
__superclass: $superclass,
$($field_vis $field: $field_ty,)*
}
$(#[$impl_m])*
unsafe impl<> ClassType for $for {
$(#[inherits($($inheritance_rest),+)])?
type Super = $superclass;
type Mutability = $mutability;
fn as_super(&self) -> &Self::Super {
&self.__superclass
}
fn as_super_mut(&mut self) -> &mut Self::Super {
&mut self.__superclass
}
$(const NAME: &'static str = $name_const;)?
}
);
$(#[$impl_m])*
const _: () = {
if $crate::__macro_helpers::size_of::<$name>() != 0 {
$crate::__macro_helpers::panic!($crate::__macro_helpers::concat!(
"the struct ",
$crate::__macro_helpers::stringify!($name),
" is not zero-sized!",
))
}
};
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_as_ref_borrow {
{
impl ($($t:tt)*) for $for:ty {
fn as_ref($($self:tt)*) $ref:block
fn as_mut($($self_mut:tt)*) $mut:block
}
()
} => {};
{
impl ($($t:tt)*) for $for:ty {
fn as_ref($($self:tt)*) $ref:block
fn as_mut($($self_mut:tt)*) $mut:block
}
($item:ty, $($tail:ty,)*)
} => {
impl<$($t)*> $crate::__macro_helpers::AsRef<$item> for $for {
#[inline]
fn as_ref($($self)*) -> &$item $ref
}
impl<$($t)*> $crate::__macro_helpers::AsMut<$item> for $for {
#[inline]
fn as_mut($($self_mut)*) -> &mut $item $mut
}
// Borrow and BorrowMut are correct, since subclasses behaves
// identical to the class they inherit (message sending doesn't care).
//
// In particular, `Eq`, `Ord` and `Hash` all give the same results
// after borrow.
impl<$($t)*> $crate::__macro_helpers::Borrow<$item> for $for {
#[inline]
fn borrow($($self)*) -> &$item $ref
}
impl<$($t)*> $crate::__macro_helpers::BorrowMut<$item> for $for {
#[inline]
fn borrow_mut($($self_mut)*) -> &mut $item $mut
}
$crate::__impl_as_ref_borrow! {
impl ($($t)*) for $for {
fn as_ref($($self)*) $ref
fn as_mut($($self_mut)*) $mut
}
($($tail,)*)
}
};
// TODO: Expose a generic variant of the macro.
}
/// Note: While this is not public, it is still a breaking change to change
/// this, since framework crates rely on it.
#[doc(hidden)]
#[macro_export]
macro_rules! __inner_extern_class {
(
$(#[$m:meta])*
$v:vis struct $name:ident<$($t_struct:ident $(: $(?$b_sized_struct:ident)? $($b_struct:ident)? $(= $default:ty)?)?),* $(,)?> {
$superclass_field:ident: $superclass_field_ty:ty,
$($fields:tt)*
}
$(#[$impl_m:meta])*
unsafe impl<$($t_for:ident $(: $(?$b_sized_for:ident +)? $b_for:ident)?),* $(,)?> ClassType for $for:ty {
$(#[inherits($($inheritance_rest:ty),+ $(,)?)])?
type Super = $superclass:ty;
type Mutability = $mutability:ty;
fn as_super(&$as_super_self:ident) -> &Self::Super $as_super:block
fn as_super_mut(&mut $as_super_mut_self:ident) -> &mut Self::Super $as_super_mut:block
$(const NAME: &'static str = $name_const:expr;)?
}
) => {
$(#[$m])*
#[repr(C)]
$v struct $name<$($t_struct $(: $(?$b_sized_struct)? $($b_struct)? $(= $default)?)?),*> {
$superclass_field: $superclass_field_ty,
$($fields)*
}
$crate::__extern_class_impl_traits! {
$(#[$impl_m])*
unsafe impl ($($t_for $(: $(?$b_sized_for +)? $b_for)?),*) for $for {
INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::AnyObject];
fn as_super(&$as_super_self) $as_super
fn as_super_mut(&mut $as_super_mut_self) $as_super_mut
}
}
$(#[$impl_m])*
unsafe impl<$($t_for $(: $(?$b_sized_for +)? $b_for)?),*> ClassType for $for {
type Super = $superclass;
type Mutability = $mutability;
const NAME: &'static $crate::__macro_helpers::str = $crate::__select_name!($name; $($name_const)?);
#[inline]
fn class() -> &'static $crate::runtime::AnyClass {
$crate::__macro_helpers::assert_mutability_matches_superclass_mutability::<Self>();
$crate::__class_inner!(
$crate::__select_name!($name; $($name_const)?),
$crate::__hash_idents!($name)
)
}
#[inline]
fn as_super(&$as_super_self) -> &Self::Super $as_super
#[inline]
fn as_super_mut(&mut $as_super_mut_self) -> &mut Self::Super $as_super_mut
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_class_impl_traits {
(
$(#[$impl_m:meta])*
unsafe impl ($($t:tt)*) for $for:ty {
INHERITS = [$superclass:ty $(, $inheritance_rest:ty)*];
fn as_super(&$as_super_self:ident) $as_super:block
fn as_super_mut(&mut $as_super_mut_self:ident) $as_super_mut:block
}
) => {
// SAFETY:
// - The item is FFI-safe with `#[repr(C)]`.
// - The encoding is taken from the inner item, and caller verifies
// that it actually inherits said object.
// - The rest of the struct's fields are ZSTs, so they don't influence
// the layout.
//
// Be aware that very rarely, this implementation is wrong because the
// class' instances do not have the encoding `Encoding::Object`.
//
// A known case is that `NSAutoreleasePool` has a different encoding.
// This should be fairly problem-free though, since that is still
// valid in Objective-C to represent that class' instances as
// `NSObject*`.
$(#[$impl_m])*
unsafe impl<$($t)*> $crate::RefEncode for $for {
const ENCODING_REF: $crate::Encoding
= <$superclass as $crate::RefEncode>::ENCODING_REF;
}
// SAFETY: This is a newtype wrapper over `AnyObject` (we even ensure
// that `AnyObject` is always last in our inheritance tree), so it is
// always safe to reinterpret as that.
//
// That the object must work with standard memory management is
// properly upheld by the fact that the superclass is required by
// `assert_mutability_matches_superclass_mutability` to implement
// `ClassType`, and hence must be a subclass of one of `NSObject`,
// `NSProxy` or some other class that ensures this (e.g. the object
// itself is not a root class).
$(#[$impl_m])*
unsafe impl<$($t)*> $crate::Message for $for {}
// SAFETY: An instance can always be _used_ in exactly the same way as
// its superclasses (though not necessarily _constructed_ in the same
// way, but `Deref` doesn't allow this).
//
// Remember; while we (the Rust side) may intentionally be forgetting
// which instance we're holding, the Objective-C side will remember,
// and will always dispatch to the correct method implementations.
//
// Any lifetime information that the object may have been holding is
// safely kept in the returned reference.
//
// Generics are discarded (for example in the case of `&NSArray<T>` to
// `&NSObject`), but if the generic contained a lifetime, that
// lifetime is still included in the returned reference, and is not
// erasable by e.g. `ClassType::retain` since `NSObject` does not
// allow that.
//
// Note that you can have two different variables pointing to the same
// object, `x: &T` and `y: &T::Target`, and this would be perfectly
// safe!
$(#[$impl_m])*
impl<$($t)*> $crate::__macro_helpers::Deref for $for {
type Target = $superclass;
#[inline]
fn deref(&$as_super_self) -> &Self::Target $as_super
}
// SAFETY: Mutability does not change anything in the above
// consideration, the lifetime of `&mut Self::Target` is still tied to
// `&mut self`.
//
// Usually we don't want to allow `&mut` of immutable objects like
// `NSString`, because their `NSCopying` implementation returns the
// same object, and would violate aliasing rules.
//
// But `&mut NSMutableString` -> `&mut NSString` safe, since the
// `NSCopying` implementation of `NSMutableString` is still used on
// the `&mut NSString`, and that is guaranteed to return a different
// object.
$(#[$impl_m])*
impl<$($t)*> $crate::__macro_helpers::DerefMut for $for {
#[inline]
fn deref_mut(&mut $as_super_mut_self) -> &mut Self::Target $as_super_mut
}
$(#[$impl_m])*
impl<$($t)*> $crate::__macro_helpers::AsRef<Self> for $for {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
$(#[$impl_m])*
impl<$($t)*> $crate::__macro_helpers::AsMut<Self> for $for {
#[inline]
fn as_mut(&mut self) -> &mut Self {
self
}
}
// Assume the meta attributes are all `cfg` attributes
$(#[$impl_m])*
$crate::__impl_as_ref_borrow! {
impl ($($t)*) for $for {
fn as_ref(&self) {
// Triggers Deref coercion depending on return type
&*self
}
fn as_mut(&mut self) {
// Triggers Deref coercion depending on return type
&mut *self
}
}
($superclass, $($inheritance_rest,)*)
}
};
}

View File

@@ -0,0 +1,385 @@
/// Define methods on an external class.
///
/// This is a convenience macro to generate associated functions and methods
/// that call [`msg_send!`] or [`msg_send_id!`] appropriately.
///
/// [`msg_send!`]: crate::msg_send
/// [`msg_send_id!`]: crate::msg_send_id
///
///
/// # Specification
///
/// Within the `impl` block you can define two types of functions without
/// bodies; ["associated functions"] and ["methods"]. These are then mapped to
/// the Objective-C equivalents "class methods" and "instance methods", and an
/// appropriate body is created for you. In particular, if you use `self` or
/// the special name `this` (or `_this`), your method will assumed to be an
/// instance method, and if you don't it will be assumed to be a class method.
///
/// The desired selector can be specified using the `#[method(my:selector:)]`
/// or `#[method_id(my:selector:)]` attribute. The `method` attribute maps to
/// a call to [`msg_send!`], while the `method_id` maps to [`msg_send_id!`].
///
/// If the attribute ends with "_", as in `#[method(my:error:_)]` or
/// `#[method_id(my:error:_)]`, the method is assumed to take an
/// implicit `NSError**` parameter, which is automatically converted to a
/// [`Result`]. See the error section in [`msg_send!`] and [`msg_send_id!`]
/// for details.
///
/// If you use `objc2_foundation::MainThreadMarker` as a parameter type, the
/// macro will ignore it, allowing you to neatly specify "this method must be
/// run on the main thread". Note that due to type-system limitations, this is
/// currently a textual match on `MainThreadMarker`; so you must use that
/// exact identifier.
///
/// Putting other attributes on the method such as `cfg`, `allow`, `doc`,
/// `deprecated` and so on is supported. However, note that `cfg_attr` may not
/// work correctly, due to implementation difficulty - if you have a concrete
/// use-case, please [open an issue], then we can discuss it.
///
/// The name of the function will be used for the resulting function that the
/// user will use to access the functionality, but is otherwise not used by
/// the macro.
///
/// If you specify a function/method with a body, the macro will output it
/// unchanged.
///
/// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
/// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods
/// [open an issue]: https://github.com/madsmtm/objc2/issues/new
///
///
/// # Safety
///
/// You must ensure that any methods you declare with the `#[method(...)]`
/// attribute upholds the safety guarantees decribed in the [`msg_send!`]
/// macro, _or_ are marked `unsafe`.
///
/// Likewise, you must ensure that any methods you declare with the
/// `#[method_id(...)]` attribute upholds the safety guarantees decribed in
/// the [`msg_send_id!`] macro, _or_ are marked `unsafe`.
///
///
/// # Examples
///
/// Let's create a quick custom class:
///
/// ```
/// use objc2::encode::{Encode, Encoding};
/// use objc2::ffi::NSUInteger;
/// use objc2::rc::{Allocated, Retained};
/// use objc2::runtime::NSObject;
/// use objc2::{declare_class, extern_methods, mutability, ClassType, DeclaredClass};
///
/// // Shim
/// type NSError = NSObject;
///
/// declare_class!(
/// pub struct MyObject;
///
/// // SAFETY:
/// // - The superclass NSObject does not have any subclassing requirements.
/// // - Interior mutability is a safe default.
/// // - `MyObject` does not implement `Drop`.
/// unsafe impl ClassType for MyObject {
/// type Super = NSObject;
/// type Mutability = mutability::InteriorMutable;
/// const NAME: &'static str = "MyObject";
/// }
///
/// impl DeclaredClass for MyObject {}
///
/// unsafe impl MyObject {
/// // ... Assume we've implemented all the methods used below
/// }
/// );
///
/// extern_methods!(
/// /// Creation methods.
/// unsafe impl MyObject {
/// #[method_id(new)]
/// pub fn new() -> Retained<Self>;
///
/// #[method_id(initWithVal:)]
/// // Arbitary self types are not stable, but we can work around it
/// // with the special name `this`.
/// pub fn init(this: Allocated<Self>, val: usize) -> Retained<Self>;
/// }
///
/// /// Instance accessor methods.
/// unsafe impl MyObject {
/// #[method(foo)]
/// pub fn foo(&self) -> NSUInteger;
///
/// #[method_id(fooObject)]
/// pub fn foo_object(&self) -> Retained<NSObject>;
///
/// #[method(withError:_)]
/// // Since the selector specifies "_", the return type is assumed to
/// // be `Result`.
/// pub fn with_error(&self) -> Result<(), Retained<NSError>>;
/// }
/// );
/// ```
///
/// The `extern_methods!` declaration then becomes:
///
/// ```
/// # use objc2::encode::{Encode, Encoding};
/// # use objc2::ffi::NSUInteger;
/// # use objc2::rc::{Allocated, Retained};
/// # use objc2::runtime::NSObject;
/// # use objc2::{declare_class, extern_methods, mutability, ClassType, DeclaredClass};
/// #
/// # // Shim
/// # type NSError = NSObject;
/// #
/// # declare_class!(
/// # pub struct MyObject;
/// #
/// # unsafe impl ClassType for MyObject {
/// # type Super = NSObject;
/// # type Mutability = mutability::InteriorMutable;
/// # const NAME: &'static str = "MyObject2";
/// # }
/// #
/// # impl DeclaredClass for MyObject {}
/// #
/// # unsafe impl MyObject {
/// # // ... Assume we've implemented all the methods used below
/// # }
/// # );
/// #
/// use objc2::{msg_send, msg_send_id};
///
/// /// Creation methods.
/// impl MyObject {
/// pub fn new() -> Retained<Self> {
/// unsafe { msg_send_id![Self::class(), new] }
/// }
///
/// pub fn init(this: Allocated<Self>, val: usize) -> Retained<Self> {
/// unsafe { msg_send_id![this, initWithVal: val] }
/// }
/// }
///
/// /// Instance accessor methods.
/// impl MyObject {
/// pub fn foo(&self) -> NSUInteger {
/// unsafe { msg_send![self, foo] }
/// }
///
/// pub fn foo_object(&self) -> Retained<NSObject> {
/// unsafe { msg_send_id![self, fooObject] }
/// }
///
/// // Since the selector specifies one more argument than we
/// // have, the return type is assumed to be `Result`.
/// pub fn with_error(&self) -> Result<(), Retained<NSError>> {
/// unsafe { msg_send![self, withError: _] }
/// }
/// }
/// ```
///
/// See the source code of `objc2-foundation` for many more examples.
#[macro_export]
macro_rules! extern_methods {
// Generic impls
(
$(
$(#[$impl_m:meta])*
unsafe impl<$($t:ident $(: $b:ident $(+ $rest:ident)*)?),* $(,)?> $type:ty {
$($methods:tt)*
}
)+
) => {
$(
$(#[$impl_m])*
impl<$($t $(: $b $(+ $rest)*)?),*> $type {
$crate::__extern_methods_rewrite_methods! {
$($methods)*
}
}
)+
};
// Non-generic impls
(
$(
$(#[$impl_m:meta])*
unsafe impl $type:ty {
$($methods:tt)*
}
)+
) => {
$(
$(#[$impl_m])*
impl $type {
$crate::__extern_methods_rewrite_methods! {
$($methods)*
}
}
)+
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_methods_rewrite_methods {
// Base case
{} => {};
// Unsafe variant
{
$(#[$($m:tt)*])*
$v:vis unsafe fn $name:ident($($params:tt)*) $(-> $ret:ty)?
// TODO: Handle where bounds better
$(where $($where:ty : $bound:path),+ $(,)?)?;
$($rest:tt)*
} => {
$crate::__rewrite_self_param! {
($($params)*)
($crate::__extract_custom_attributes)
($(#[$($m)*])*)
($crate::__extern_methods_method_out)
($v unsafe fn $name($($params)*) $(-> $ret)?)
($($($where : $bound ,)+)?)
}
$crate::__extern_methods_rewrite_methods! {
$($rest)*
}
};
// Safe variant
{
$(#[$($m:tt)*])*
$v:vis fn $name:ident($($params:tt)*) $(-> $ret:ty)?
// TODO: Handle where bounds better
$(where $($where:ty : $bound:path),+ $(,)?)?;
$($rest:tt)*
} => {
$crate::__rewrite_self_param! {
($($params)*)
($crate::__extract_custom_attributes)
($(#[$($m)*])*)
($crate::__extern_methods_method_out)
($v fn $name($($params)*) $(-> $ret)?)
($($($where : $bound ,)+)?)
}
$crate::__extern_methods_rewrite_methods! {
$($rest)*
}
};
// Other items that people might want to put here (e.g. functions with a
// body).
{
$associated_item:item
$($rest:tt)*
} => {
$associated_item
$crate::__extern_methods_rewrite_methods! {
$($rest)*
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_methods_method_out {
// #[method(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)
($__builder_method:ident)
($receiver:expr)
($__receiver_ty:ty)
($($__params_prefix:tt)*)
($($params_rest:tt)*)
(#[method($($sel:tt)*)])
()
($($m_optional:tt)*)
($($m_checked:tt)*)
} => {
$($m_checked)*
$($function_start)*
where
$($where : $bound,)*
{
$crate::__extern_methods_no_optional!($($m_optional)*);
#[allow(unused_unsafe)]
unsafe {
$crate::__method_msg_send! {
($receiver)
($($sel)*)
($($params_rest)*)
()
()
}
}
}
};
// #[method_id(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)
($__builder_method:ident)
($receiver:expr)
($__receiver_ty:ty)
($($__params_prefix:tt)*)
($($params_rest:tt)*)
(#[method_id($($sel:tt)*)])
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
} => {
$($m_checked)*
$($function_start)*
where
$($where : $bound,)*
{
$crate::__extern_methods_no_optional!($($m_optional)*);
#[allow(unused_unsafe)]
unsafe {
$crate::__method_msg_send_id! {
($receiver)
($($sel)*)
($($params_rest)*)
()
()
($($retain_semantics)*)
}
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_methods_no_optional {
() => {};
(#[optional]) => {
$crate::__macro_helpers::compile_error!(
"`#[optional]` is only supported in `extern_protocol!`"
)
};
}

View File

@@ -0,0 +1,423 @@
/// Create a new trait to represent a protocol.
///
/// This is similar to a `@protocol` declaration in Objective-C.
///
/// See [Protocols - The Objective-C Programming Language][protocols] and
/// [Working with Protocols - Programming with Objective-C][working-with] for
/// general information about protocols in Objective-C.
///
/// This macro will create an `unsafe` trait with methods which all have
/// default implementations, such that an external class that conforms to the
/// protocol can write `unsafe impl MyProtocol for MyClass {}`, and get access
/// to the functionality exposed by the protocol.
///
/// Note that that conforming to a protocol in a custom object requires
/// putting the implementation inside the [`declare_class!`] invocation.
///
/// Objective-C has a smart feature where you can write `id<MyProtocol>`, and
/// then work with the protocol as-if it was an object; this is very similar
/// to `dyn` traits in Rust, and we implement it in a similar way, see
/// [`ProtocolObject`] for details.
///
/// [protocols]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html
/// [working-with]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithProtocols/WorkingwithProtocols.html
/// [`ProtocolObject`]: crate::runtime::ProtocolObject
///
///
/// # Specification
///
/// The syntax is similar enough to Rust syntax that if you invoke the macro
/// with parentheses (as opposed to curly brackets), `rustfmt` will be able to
/// format the contents.
///
/// This macro creates an `unsafe` trait with the specified methods. A default
/// implementation of the method is generated based on the selector specified
/// with `#[method(a:selector:)]` or `#[method_id(a:selector:)]`.
///
/// Other protocols that this protocol conforms to / inherits can be specified
/// as supertraits.
///
/// The new trait `T` is automatically implemented for
/// [`ProtocolObject<dyn T>`], which also means that [`ProtocolType`] is
/// implemented for `dyn T`.
///
/// Finally, you can use the `#[optional]` attribute to mark optional methods.
/// This currently doesn't have any effect, but probably will have one in the
/// future when implementing protocols in [`declare_class!`].
///
/// This macro otherwise shares similarities with [`extern_class!`] and
/// [`extern_methods!`].
///
/// [`ProtocolObject<dyn T>`]: crate::runtime::ProtocolObject
/// [`ProtocolType`]: crate::ProtocolType
/// [`declare_class!`]: crate::declare_class
/// [`extern_class!`]: crate::extern_class
/// [`extern_methods!`]: crate::extern_methods
///
///
/// # Safety
///
/// The following are required for using the macro itself:
/// - The specified name must be an exisiting Objective-C protocol.
/// - The protocol must actually inherit/conform to the protocols specified
/// as supertraits.
/// - The protocol's methods must be correctly specified.
///
/// While the following are required when implementing the `unsafe` trait for
/// a new type:
/// - The type must represent an object that implements the protocol.
///
///
/// # Examples
///
/// Create a trait to represent the `NSItemProviderWriting` protocol.
///
/// ```
/// use std::ffi::c_void;
/// use objc2::ffi::NSInteger;
/// use objc2::rc::Retained;
/// use objc2::runtime::{NSObject, NSObjectProtocol};
/// use objc2::{extern_protocol, ProtocolType};
///
/// // Assume these were correctly defined, as if they came from
/// // `objc2-foundation`
/// type NSArray<T> = T;
/// type NSString = NSObject;
/// type NSProgress = NSObject;
/// type NSItemProviderRepresentationVisibility = NSInteger;
///
/// extern_protocol!(
/// /// This comment will appear on the trait as expected.
/// pub unsafe trait NSItemProviderWriting: NSObjectProtocol {
/// // ^^^^^^^^^^^^^^^^
/// // This protocol inherits from the `NSObject` protocol
///
/// // This method we mark as `unsafe`, since we aren't using the correct
/// // type for the completion handler
/// #[method_id(loadDataWithTypeIdentifier:forItemProviderCompletionHandler:)]
/// unsafe fn loadData(
/// &self,
/// type_identifier: &NSString,
/// completion_handler: *mut c_void,
/// ) -> Option<Retained<NSProgress>>;
///
/// #[method_id(writableTypeIdentifiersForItemProvider)]
/// fn writableTypeIdentifiersForItemProvider_class()
/// -> Retained<NSArray<NSString>>;
///
/// // The rest of these are optional, which means that a user of
/// // `declare_class!` would not need to implement them.
///
/// #[optional]
/// #[method_id(writableTypeIdentifiersForItemProvider)]
/// fn writableTypeIdentifiersForItemProvider(&self)
/// -> Retained<NSArray<NSString>>;
///
/// #[optional]
/// #[method(itemProviderVisibilityForRepresentationWithTypeIdentifier:)]
/// fn itemProviderVisibilityForRepresentation_class(
/// type_identifier: &NSString,
/// ) -> NSItemProviderRepresentationVisibility;
///
/// #[optional]
/// #[method(itemProviderVisibilityForRepresentationWithTypeIdentifier:)]
/// fn itemProviderVisibilityForRepresentation(
/// &self,
/// type_identifier: &NSString,
/// ) -> NSItemProviderRepresentationVisibility;
/// }
///
/// // SAFETY:
/// // - The name is correct
/// // - The protocol does inherit `NSObject`
/// // - The methods are correctly specified
/// unsafe impl ProtocolType for dyn NSItemProviderWriting {
/// // ^^^ - Remember to add this `dyn`.
///
/// // We could have named the trait something else on the Rust-side,
/// // and then used this to keep it correct from Objective-C.
/// //
/// // const NAME: &'static str = "NSItemProviderWriting";
/// }
/// );
///
/// // Types can now implement `NSItemProviderWriting`, and use the methods
/// // from it as we specified.
/// ```
///
/// See the source code of `objc2-foundation` for many more examples.
#[doc(alias = "@protocol")]
#[macro_export]
macro_rules! extern_protocol {
(
$(#[$m:meta])*
$v:vis unsafe trait $name:ident $(: $conforms_to:ident $(+ $conforms_to_rest:ident)*)? {
$($methods:tt)*
}
$(#[$impl_m:meta])*
unsafe impl ProtocolType for dyn $for:ident {
$(const NAME: &'static str = $name_const:expr;)?
}
) => {
$(#[$m])*
$v unsafe trait $name $(: $conforms_to $(+ $conforms_to_rest)*)? {
$crate::__extern_protocol_rewrite_methods! {
$($methods)*
}
}
$crate::__inner_extern_protocol!(
($(#[$impl_m])*)
($name)
(dyn $for)
($crate::__select_name!($name; $($name_const)?))
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __inner_extern_protocol {
(
($(#[$impl_m:meta])*)
($name:ident)
(dyn $for:ident)
($name_str:expr)
) => {
$(#[$impl_m])*
unsafe impl<T> $name for $crate::runtime::ProtocolObject<T>
where
T: ?$crate::__macro_helpers::Sized + $name
{}
// SAFETY: The specified name is ensured by caller to be a protocol,
// and is correctly defined.
$(#[$impl_m])*
unsafe impl ProtocolType for dyn $for {
const NAME: &'static $crate::__macro_helpers::str = $name_str;
const __INNER: () = ();
}
// SAFETY: Anything that implements the protocol is valid to convert
// to `ProtocolObject<dyn [PROTO]>`.
$(#[$impl_m])*
unsafe impl<T> $crate::runtime::ImplementedBy<T> for dyn $for
where
T: ?$crate::__macro_helpers::Sized + $crate::Message + $name
{
const __INNER: () = ();
}
// TODO: Should we also implement `ImplementedBy` for `Send + Sync`
// types, as is done for `NSObjectProtocol`?
};
}
/// tt-munch each protocol method.
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_rewrite_methods {
// Base case
{} => {};
// Unsafe variant
{
$(#[$($m:tt)*])*
$v:vis unsafe fn $name:ident($($params:tt)*) $(-> $ret:ty)?
// TODO: Handle where bounds better
$(where $($where:ty : $bound:path),+ $(,)?)?;
$($rest:tt)*
} => {
$crate::__rewrite_self_param! {
($($params)*)
($crate::__extract_custom_attributes)
($(#[$($m)*])*)
($crate::__extern_protocol_method_out)
($v unsafe fn $name($($params)*) $(-> $ret)?)
($($($where : $bound ,)+)?)
}
$crate::__extern_protocol_rewrite_methods! {
$($rest)*
}
};
// Safe variant
{
$(#[$($m:tt)*])*
$v:vis fn $name:ident($($params:tt)*) $(-> $ret:ty)?
// TODO: Handle where bounds better
$(where $($where:ty : $bound:path),+ $(,)?)?;
$($rest:tt)*
} => {
$crate::__rewrite_self_param! {
($($params)*)
($crate::__extract_custom_attributes)
($(#[$($m)*])*)
($crate::__extern_protocol_method_out)
($v fn $name($($params)*) $(-> $ret)?)
($($($where : $bound ,)+)?)
}
$crate::__extern_protocol_rewrite_methods! {
$($rest)*
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __extern_protocol_method_out {
// Instance #[method(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)
(add_method)
($receiver:expr)
($__receiver_ty:ty)
($($__params_prefix:tt)*)
($($params_rest:tt)*)
(#[method($($sel:tt)*)])
()
($($m_optional:tt)*)
($($m_checked:tt)*)
} => {
$($m_checked)*
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::Message
$(, $where : $bound)*
{
#[allow(unused_unsafe)]
unsafe {
$crate::__method_msg_send! {
($receiver)
($($sel)*)
($($params_rest)*)
()
()
}
}
}
};
// Instance #[method_id(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)
(add_method)
($receiver:expr)
($__receiver_ty:ty)
($($__params_prefix:tt)*)
($($params_rest:tt)*)
(#[method_id($($sel:tt)*)])
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
} => {
$($m_checked)*
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::Message
$(, $where : $bound)*
{
#[allow(unused_unsafe)]
unsafe {
$crate::__method_msg_send_id! {
($receiver)
($($sel)*)
($($params_rest)*)
()
()
($($retain_semantics)*)
}
}
}
};
// Class #[method(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)
(add_class_method)
($receiver:expr)
($__receiver_ty:ty)
($($__params_prefix:tt)*)
($($params_rest:tt)*)
(#[method($($sel:tt)*)])
()
($($m_optional:tt)*)
($($m_checked:tt)*)
} => {
$($m_checked)*
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::ClassType
$(, $where : $bound)*
{
#[allow(unused_unsafe)]
unsafe {
$crate::__method_msg_send! {
($receiver)
($($sel)*)
($($params_rest)*)
()
()
}
}
}
};
// Class #[method_id(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)
(add_class_method)
($receiver:expr)
($__receiver_ty:ty)
($($__params_prefix:tt)*)
($($params_rest:tt)*)
(#[method_id($($sel:tt)*)])
($($retain_semantics:tt)*)
($($m_optional:tt)*)
($($m_checked:tt)*)
} => {
$($m_checked)*
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::ClassType
$(, $where : $bound)*
{
#[allow(unused_unsafe)]
unsafe {
$crate::__method_msg_send_id! {
($receiver)
($($sel)*)
($($params_rest)*)
()
()
($($retain_semantics)*)
}
}
}
};
}

1389
vendor/objc2/src/macros/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

669
vendor/objc2/src/mutability.rs vendored Normal file
View File

@@ -0,0 +1,669 @@
//! # Marker types for class mutability.
//!
//! Every class must indicate which kind of mutability its instances use:
//! - Is the instance mutable or immutable?
//! - Does it use interior mutability (mutable behind `&self`, like
//! [`UnsafeCell`])?
//! - Does it access global statics in such a way that the type is only safe
//! to use from the main thread?
//!
//! The answer to these facts influence the final capabilities the type has,
//! as encoded in [the traits in this module](#traits).
//!
//! Concretely, you set [`ClassType::Mutability`] to [one of the types in this
//! module](#structs) to indicate the properties of class you're dealing with
//! (can be done inside [`extern_class!`] and [`declare_class!`]).
//!
//! Note that precious little of Objective-C follows Rust's usual shared xor
//! unique ownership model, most often objects assume interior mutability, so
//! a safe default is often [`InteriorMutable`], or of you're working with GUI
//! code, [`MainThreadOnly`].
//!
//! [`UnsafeCell`]: core::cell::UnsafeCell
//! [`ClassType::Mutability`]: crate::ClassType::Mutability
//! [`extern_class!`]: crate::extern_class
//! [`declare_class!`]: crate::declare_class
//!
//!
//! # SemVer
//!
//! It is considered a major change to change the [`ClassType::Mutability`] of
//! an object, though it can be done as a minor change in some cases to fix a
//! bug.
use core::marker::PhantomData;
use crate::runtime::{AnyObject, ProtocolObject};
use crate::{ClassType, Message};
mod private_mutability {
pub trait Sealed {}
}
/// Marker trait for the different types of mutability a class can have.
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
//
// Note: `Sized` is intentionally added to make the trait not object safe.
pub trait Mutability: private_mutability::Sealed + Sized {}
impl private_mutability::Sealed for Root {}
impl Mutability for Root {}
impl private_mutability::Sealed for Immutable {}
impl Mutability for Immutable {}
impl private_mutability::Sealed for Mutable {}
impl Mutability for Mutable {}
impl<MS: ?Sized> private_mutability::Sealed for ImmutableWithMutableSubclass<MS> {}
impl<MS: ?Sized> Mutability for ImmutableWithMutableSubclass<MS> {}
impl<IS: ?Sized> private_mutability::Sealed for MutableWithImmutableSuperclass<IS> {}
impl<IS: ?Sized> Mutability for MutableWithImmutableSuperclass<IS> {}
impl private_mutability::Sealed for InteriorMutable {}
impl Mutability for InteriorMutable {}
impl private_mutability::Sealed for MainThreadOnly {}
impl Mutability for MainThreadOnly {}
/// Helper to make the structs uninhabited, without that being a public fact.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
enum Never {}
/// Marker type for root classes.
///
/// This is used for `objc2_foundation::NSObject` and
/// `objc2_foundation::NSProxy`, which are the two fundamental types that
/// all others inherit from.
///
/// Functionality that is provided with this:
/// - [`IsIdCloneable`] -> [`Retained::clone`][crate::rc::Retained#impl-Clone-for-Retained<T>].
/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`].
/// - [`IsAllowedMutable`].
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Root {
inner: Never,
}
/// Marker type for immutable classes.
///
/// Note that immutable objects are often both [`Send`] and [`Sync`], though
/// such implementations must be provided manually.
///
/// Functionality that is provided with this:
/// - [`IsRetainable`] -> [`ClassType::retain`].
/// - [`IsIdCloneable`] -> [`Retained::clone`][crate::rc::Retained#impl-Clone-for-Retained<T>].
/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`].
/// - You are allowed to hand out pointers / references to an instance's
/// internal data, since you know such data will never be mutated.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Immutable {
inner: Never,
}
/// Marker type for mutable classes.
///
/// Note that mutable objects are often both [`Send`] and [`Sync`], though
/// such implementations must be provided manually (and are usually only safe
/// if all mutation happens behind `&mut self`).
///
/// Functionality that is provided with this:
/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`].
/// - [`IsAllowedMutable`].
/// - [`IsMutable`] -> [`impl DerefMut for Retained`][crate::rc::Retained#impl-DerefMut-for-Retained<T>].
/// - You are allowed to hand out pointers / references to an instance's
/// internal data, since you know such data will never be mutated without
/// the borrowchecker catching it.
///
///
/// # Safety notice
///
/// - (Safe) methods that mutate the object (without synchronization) are
/// required to use `&mut self`.
/// - The `retain` selector is not generally safe to use on classes `T` that
/// specify this, since `Retained<T>` allows having `&mut T` references,
/// which Rust assume are unique.
/// - As a special case of that, `-[NSCopying copy]` and
/// `-[NSMutableCopying mutableCopy]`, if implemented, must return a new
/// instance (e.g. cannot be implemented by just `retain`-ing the instance).
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Mutable {
inner: Never,
}
/// Marker type for immutable classes that have a mutable counterpart.
///
/// This is effectively the same as [`Immutable`], except for the fact that
/// classes that specify this does not implement [`IsRetainable`], meaning
/// that [`ClassType::retain`] does not work (see that for details on why).
///
/// The mutable counterpart must be specified as the type parameter `MS` to
/// allow `NSCopying` and `NSMutableCopying` to return the correct type.
///
/// Functionality that is provided with this:
/// - [`IsIdCloneable`].
/// - [`IsAllocableAnyThread`].
/// - [`IsAllowedMutable`].
/// - You are allowed to hand out pointers / references to an instance's
/// internal data, since you know such data will never be mutated.
///
///
/// # Example
///
/// ```ignore
/// unsafe impl ClassType for NSString {
/// type Super = NSObject;
/// type Mutability = ImmutableWithMutableSubclass<NSMutableString>;
/// // ...
/// }
///
/// unsafe impl ClassType for NSMutableString {
/// type Super = NSString;
/// type Mutability = MutableWithImmutableSubclass<NSString>;
/// // ...
/// }
/// ```
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ImmutableWithMutableSubclass<MS: ?Sized> {
inner: Never,
mutable_subclass: PhantomData<MS>,
}
/// Marker type for mutable classes that have a immutable counterpart.
///
/// This is effectively the same as [`Mutable`], except for the immutable
/// counterpart being be specified as the type parameter `IS` to allow
/// `NSCopying` and `NSMutableCopying` to return the correct type.
///
/// Functionality that is provided with this:
/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`].
/// - [`IsAllowedMutable`].
/// - [`IsMutable`] -> [`impl DerefMut for Retained`][crate::rc::Retained#impl-DerefMut-for-Retained<T>].
/// - You are allowed to hand out pointers / references to an instance's
/// internal data, since you know such data will never be mutated without
/// the borrowchecker catching it.
///
///
/// # Example
///
/// ```ignore
/// unsafe impl ClassType for NSData {
/// type Super = NSObject;
/// type Mutability = ImmutableWithMutableSubclass<NSMutableData>;
/// // ...
/// }
///
/// unsafe impl ClassType for NSMutableData {
/// type Super = NSData;
/// type Mutability = MutableWithImmutableSubclass<NSData>;
/// // ...
/// }
/// ```
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct MutableWithImmutableSuperclass<IS: ?Sized> {
inner: Never,
immutable_superclass: PhantomData<IS>,
}
/// Marker type for classes that use interior mutability.
///
/// This is usually not `Send + Sync`, unless the class is guaranteed to use
/// thread-safe operations.
///
/// Functionality that is provided with this:
/// - [`IsRetainable`] -> [`ClassType::retain`].
/// - [`IsIdCloneable`] -> [`Retained::clone`][crate::rc::Retained#impl-Clone-for-Retained<T>].
/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`].
///
///
/// # Safety notice
///
/// When declaring classes, it is recommended that you wrap your instance
/// variables in [`Cell`], [`RefCell`], atomics or other similar interior
/// mutability abstractions to allow mutating your instance variables through
/// `&self`.
///
/// Declared classes that use this cannot take `&mut self`, except in
/// initializers.
///
/// [`Cell`]: core::cell::Cell
/// [`RefCell`]: core::cell::RefCell
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct InteriorMutable {
inner: Never,
}
/// Marker type for classes that are only safe to use from the main thread.
///
/// This is effectively the same as [`InteriorMutable`], except that classes
/// that specify this are only allowed to be used from the main thread, and
/// hence are not [`IsAllocableAnyThread`].
///
/// This is commonly used in GUI code like `AppKit` and `UIKit`, e.g.
/// `UIWindow` is only usable from the application's main thread.
///
/// It is unsound to implement [`Send`] or [`Sync`] on a type with this
/// mutability.
///
/// Functionality that is provided with this:
/// - [`IsRetainable`] -> [`ClassType::retain`].
/// - [`IsIdCloneable`] -> [`Retained::clone`][crate::rc::Retained#impl-Clone-for-Retained<T>].
/// - [`IsMainThreadOnly`] -> `MainThreadMarker::from`.
//
// While Xcode's Main Thread Checker doesn't report `alloc` and `dealloc` as
// unsafe from other threads, things like `NSView` and `NSWindow` still do a
// non-trivial amount of stuff on `dealloc`, even if the object is freshly
// `alloc`'d - so let's disallow that to be sure.
//
// This also has the nice property that `Allocated<T>` is guaranteed to be
// allowed to initialize on the current thread.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct MainThreadOnly {
inner: Never,
}
mod private_traits {
pub trait Sealed {}
}
impl<T: ?Sized + ClassType> private_traits::Sealed for T {}
impl<P: ?Sized> private_traits::Sealed for ProtocolObject<P> {}
impl private_traits::Sealed for AnyObject {}
/// Marker trait for classes where [`Retained::clone`] / [`Id::clone`] is safe.
///
/// Since the `Foundation` collection types (`NSArray<T>`,
/// `NSDictionary<K, V>`, ...) act as if they store [`Retained`]s, this also
/// makes certain functionality on those types possible.
///
/// This is implemented for classes whose [`ClassType::Mutability`] is one of:
/// - [`Root`].
/// - [`Immutable`].
/// - [`ImmutableWithMutableSubclass`].
/// - [`InteriorMutable`].
/// - [`MainThreadOnly`].
///
/// [`Retained`]: crate::rc::Retained
/// [`Retained::clone`]: crate::rc::Retained#impl-Clone-for-Retained<T>
/// [`Id::clone`]: crate::rc::Retained#impl-Clone-for-Retained<T>
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
pub unsafe trait IsIdCloneable: private_traits::Sealed {}
trait MutabilityIsIdCloneable: Mutability {}
impl MutabilityIsIdCloneable for Root {}
impl MutabilityIsIdCloneable for Immutable {}
impl<MS: ?Sized> MutabilityIsIdCloneable for ImmutableWithMutableSubclass<MS> {}
impl MutabilityIsIdCloneable for InteriorMutable {}
impl MutabilityIsIdCloneable for MainThreadOnly {}
unsafe impl<T: ?Sized + ClassType> IsIdCloneable for T where T::Mutability: MutabilityIsIdCloneable {}
unsafe impl<P: ?Sized + IsIdCloneable> IsIdCloneable for ProtocolObject<P> {}
// SAFETY: Same as for root classes.
unsafe impl IsIdCloneable for AnyObject {}
/// Marker trait for classes where the `retain` selector is always safe.
///
/// [`Retained::clone`] takes `&Retained<T>`, while [`ClassType::retain`] only
/// takes `&T`; the difference between these two is that in the former case,
/// you know that there are no live mutable subclasses.
///
/// This is implemented for classes whose [`ClassType::Mutability`] is one of:
/// - [`Immutable`].
/// - [`InteriorMutable`].
/// - [`MainThreadOnly`].
///
/// This trait inherits [`IsIdCloneable`], so if a function is bound by this,
/// functionality given with that trait is available.
///
/// [`Retained::clone`]: crate::rc::Retained#impl-Clone-for-Retained<T>
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
pub unsafe trait IsRetainable: private_traits::Sealed + IsIdCloneable {}
trait MutabilityIsRetainable: MutabilityIsIdCloneable {}
impl MutabilityIsRetainable for Immutable {}
impl MutabilityIsRetainable for InteriorMutable {}
impl MutabilityIsRetainable for MainThreadOnly {}
unsafe impl<T: ?Sized + ClassType> IsRetainable for T where T::Mutability: MutabilityIsRetainable {}
unsafe impl<P: ?Sized + IsRetainable> IsRetainable for ProtocolObject<P> {}
/// Marker trait for classes that can be allocated from any thread.
///
/// This is implemented for classes whose [`ClassType::Mutability`] is one of:
/// - [`Root`].
/// - [`Immutable`].
/// - [`Mutable`].
/// - [`ImmutableWithMutableSubclass`].
/// - [`MutableWithImmutableSuperclass`].
/// - [`InteriorMutable`].
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
pub unsafe trait IsAllocableAnyThread: private_traits::Sealed {}
trait MutabilityIsAllocableAnyThread: Mutability {}
impl MutabilityIsAllocableAnyThread for Root {}
impl MutabilityIsAllocableAnyThread for Immutable {}
impl MutabilityIsAllocableAnyThread for Mutable {}
impl<MS: ?Sized> MutabilityIsAllocableAnyThread for ImmutableWithMutableSubclass<MS> {}
impl<IS: ?Sized> MutabilityIsAllocableAnyThread for MutableWithImmutableSuperclass<IS> {}
impl MutabilityIsAllocableAnyThread for InteriorMutable {}
unsafe impl<T: ?Sized + ClassType> IsAllocableAnyThread for T where
T::Mutability: MutabilityIsAllocableAnyThread
{
}
unsafe impl<P: ?Sized + IsAllocableAnyThread> IsAllocableAnyThread for ProtocolObject<P> {}
/// Marker trait for classes that may feasibly be used behind a mutable
/// reference.
///
/// This trait exist mostly to disallow using `&mut self` when declaring
/// classes, since that would be a huge footgun.
///
/// This is implemented for classes whose [`ClassType::Mutability`] is one of:
/// - [`Root`]
/// - [`Mutable`]
/// - [`ImmutableWithMutableSubclass`]
/// - [`MutableWithImmutableSuperclass`]
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
pub unsafe trait IsAllowedMutable: private_traits::Sealed {}
trait MutabilityIsAllowedMutable: Mutability {}
impl MutabilityIsAllowedMutable for Root {}
impl MutabilityIsAllowedMutable for Mutable {}
impl<MS: ?Sized> MutabilityIsAllowedMutable for ImmutableWithMutableSubclass<MS> {}
impl<IS: ?Sized> MutabilityIsAllowedMutable for MutableWithImmutableSuperclass<IS> {}
unsafe impl<T: ?Sized + ClassType> IsAllowedMutable for T where
T::Mutability: MutabilityIsAllowedMutable
{
}
unsafe impl<P: ?Sized + IsAllowedMutable> IsAllowedMutable for ProtocolObject<P> {}
// SAFETY: Same as for root classes.
unsafe impl IsAllowedMutable for AnyObject {}
/// Marker trait for classes that are only mutable through `&mut`.
///
/// This is implemented for classes whose [`ClassType::Mutability`] is one of:
/// - [`Mutable`]
/// - [`MutableWithImmutableSuperclass`]
///
/// Notably, [`InteriorMutable`] does not implement this (though it is
/// technically mutable), since it is allowed to mutate through shared
/// references.
///
/// This trait inherits [`IsAllowedMutable`], so if a function is bound by
/// this, functionality given with that trait is available.
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
pub unsafe trait IsMutable: private_traits::Sealed + IsAllowedMutable {}
trait MutabilityIsMutable: MutabilityIsAllowedMutable {}
impl MutabilityIsMutable for Mutable {}
impl<IS: ?Sized> MutabilityIsMutable for MutableWithImmutableSuperclass<IS> {}
unsafe impl<T: ?Sized + ClassType> IsMutable for T where T::Mutability: MutabilityIsMutable {}
unsafe impl<P: ?Sized + IsMutable> IsMutable for ProtocolObject<P> {}
/// Marker trait for classes that are only available on the main thread.
///
/// This is implemented for classes whose [`ClassType::Mutability`] is one of:
/// - [`MainThreadOnly`].
///
/// Since `MainThreadOnly` types must be `!Send` and `!Sync`, if you hold a
/// type that implements this trait, then you're guaranteed to be on the main
/// thread (and can get a `MainThreadMarker` using `MainThreadMarker::from`).
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
pub unsafe trait IsMainThreadOnly: private_traits::Sealed {}
trait MutabilityIsMainThreadOnly: Mutability {}
impl MutabilityIsMainThreadOnly for MainThreadOnly {}
unsafe impl<T: ?Sized + ClassType> IsMainThreadOnly for T where
T::Mutability: MutabilityIsMainThreadOnly
{
}
unsafe impl<P: ?Sized + IsMainThreadOnly> IsMainThreadOnly for ProtocolObject<P> {}
/// Marker trait for classes whose `hash` and `isEqual:` methods are stable.
///
/// This is useful for hashing collection types like `NSDictionary` and
/// `NSSet` which require that their keys never change.
///
/// This is implemented for classes whose [`ClassType::Mutability`] is one of:
/// - [`Immutable`].
/// - [`Mutable`].
/// - [`ImmutableWithMutableSubclass`].
/// - [`MutableWithImmutableSuperclass`].
///
/// Since all of these do not use interior mutability, and since the `hash`
/// and `isEqual:` methods are required to not use external sources like
/// thread locals or randomness to determine their result, we can guarantee
/// that the hash is stable for these types.
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
//
// TODO: Exclude generic types like `NSArray<NSView>` from this!
pub unsafe trait HasStableHash: private_traits::Sealed {}
trait MutabilityHashIsStable: Mutability {}
impl MutabilityHashIsStable for Immutable {}
impl MutabilityHashIsStable for Mutable {}
impl<MS: ?Sized> MutabilityHashIsStable for ImmutableWithMutableSubclass<MS> {}
impl<IS: ?Sized> MutabilityHashIsStable for MutableWithImmutableSuperclass<IS> {}
unsafe impl<T: ?Sized + ClassType> HasStableHash for T where T::Mutability: MutabilityHashIsStable {}
unsafe impl<P: ?Sized + HasStableHash> HasStableHash for ProtocolObject<P> {}
/// Retrieve the immutable/mutable counterpart class, and fall back to `Self`
/// if not applicable.
///
/// This is used for describing the return type of `NSCopying` and
/// `NSMutableCopying`, since due to Rust trait limitations, those two can't
/// have associated types themselves (since we want to use them in
/// `ProtocolObject<dyn NSCopying>`).
///
///
/// # Usage notes
///
/// You may not rely on this being implemented entirely correctly for protocol
/// objects, since we have less type-information available there.
///
/// In particular, the immutable counterpart of a mutable object converted to
/// `ProtocolObject<dyn AProtocol>` may not itself implement the protocol, and
/// invalidly assuming it does is unsound.
///
/// All of this is to say: Do not use this trait in isolation, either require
/// `NSCopying` or `ClassType` along with it.
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
pub unsafe trait CounterpartOrSelf: private_traits::Sealed {
/// The immutable counterpart of the type, or `Self` if the type has no
/// immutable counterpart.
///
/// The implementation for `NSString` has itself (`NSString`) here, while
/// `NSMutableString` instead has `NSString`.
type Immutable: ?Sized + Message;
/// The mutable counterpart of the type, or `Self` if the type has no
/// mutable counterpart.
///
/// The implementation for `NSString` has `NSMutableString` here, while
/// `NSMutableString` has itself (`NSMutableString`).
type Mutable: ?Sized + Message;
}
mod private_counterpart {
use super::*;
pub trait MutabilityCounterpartOrSelf<T: ?Sized>: Mutability {
type Immutable: ?Sized + Message;
type Mutable: ?Sized + Message;
}
impl<T: ClassType<Mutability = Root>> MutabilityCounterpartOrSelf<T> for Root {
type Immutable = T;
type Mutable = T;
}
impl<T: ClassType<Mutability = Immutable>> MutabilityCounterpartOrSelf<T> for Immutable {
type Immutable = T;
type Mutable = T;
}
impl<T: ClassType<Mutability = Mutable>> MutabilityCounterpartOrSelf<T> for Mutable {
type Immutable = T;
type Mutable = T;
}
impl<T, MS> MutabilityCounterpartOrSelf<T> for ImmutableWithMutableSubclass<MS>
where
T: ClassType<Mutability = ImmutableWithMutableSubclass<MS>>,
MS: ClassType<Mutability = MutableWithImmutableSuperclass<T>>,
{
type Immutable = T;
type Mutable = MS;
}
impl<T, IS> MutabilityCounterpartOrSelf<T> for MutableWithImmutableSuperclass<IS>
where
T: ClassType<Mutability = MutableWithImmutableSuperclass<IS>>,
IS: ClassType<Mutability = ImmutableWithMutableSubclass<T>>,
{
type Immutable = IS;
type Mutable = T;
}
impl<T: ClassType<Mutability = InteriorMutable>> MutabilityCounterpartOrSelf<T>
for InteriorMutable
{
type Immutable = T;
type Mutable = T;
}
impl<T: ClassType<Mutability = MainThreadOnly>> MutabilityCounterpartOrSelf<T> for MainThreadOnly {
type Immutable = T;
type Mutable = T;
}
}
unsafe impl<T: ?Sized + ClassType> CounterpartOrSelf for T
where
T::Mutability: private_counterpart::MutabilityCounterpartOrSelf<T>,
{
type Immutable =
<T::Mutability as private_counterpart::MutabilityCounterpartOrSelf<T>>::Immutable;
type Mutable = <T::Mutability as private_counterpart::MutabilityCounterpartOrSelf<T>>::Mutable;
}
unsafe impl<P: ?Sized> CounterpartOrSelf for ProtocolObject<P> {
// SAFETY: The only place where this would differ from `Self` is for
// classes with `MutableWithImmutableSuperclass<IS>`.
//
// Superclasses are not in general required to implement the same traits
// as their subclasses, but we're not dealing with normal classes, we're
// dealing with with immutable/mutable class counterparts!
//
// We could probably get away with requiring that mutable classes
// only implement the same protocols as their immutable counterparts, but
// for now we relax the requirements of `CounterpartOrSelf`.
type Immutable = Self;
// SAFETY: The only place where this would differ from `Self` is for
// classes with `ImmutableWithMutableSubclass<MS>`.
//
// But subclasses are required to always implement the same traits as
// their superclasses, so a mutable subclass is required to implement the
// same traits too.
type Mutable = Self;
}
#[cfg(test)]
mod tests {
use crate::runtime::NSObject;
use super::*;
use core::any::TypeId;
use core::fmt;
use core::hash;
#[test]
fn generic_traits() {
fn assert_traits<T>()
where
T: Sync + Send,
T: Clone + Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash + fmt::Debug,
{
}
assert_traits::<Root>();
assert_traits::<Immutable>();
assert_traits::<Mutable>();
assert_traits::<ImmutableWithMutableSubclass<()>>();
assert_traits::<MutableWithImmutableSuperclass<()>>();
assert_traits::<InteriorMutable>();
assert_traits::<MainThreadOnly>();
#[allow(unused)]
fn test_mutability_implies_sized<M: ?Sized + Mutability>() {
fn assert_sized<T: Sized>() {}
assert_sized::<M>();
}
}
#[test]
fn counterpart_root() {
assert_eq!(
TypeId::of::<NSObject>(),
TypeId::of::<<NSObject as CounterpartOrSelf>::Immutable>(),
);
assert_eq!(
TypeId::of::<NSObject>(),
TypeId::of::<<NSObject as CounterpartOrSelf>::Mutable>(),
);
}
#[allow(unused, clippy::too_many_arguments)]
fn object_safe(
_: &dyn IsIdCloneable,
_: &dyn IsRetainable,
_: &dyn IsAllocableAnyThread,
_: &dyn IsAllowedMutable,
_: &dyn IsMutable,
_: &dyn IsMainThreadOnly,
_: &dyn HasStableHash,
_: &dyn CounterpartOrSelf<Immutable = (), Mutable = ()>,
) {
}
}

View File

@@ -0,0 +1,353 @@
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};
/// An Objective-C object that has been allocated, but not initialized.
///
/// Objective-C splits the allocation and initialization steps up into two, so
/// we need to track it in the type-system whether something has been
/// intialized or not.
///
/// Note that allocation in Objective-C can fail, e.g. in Out Of Memory
/// situations! This is handled by `objc2` automatically, but if you really
/// need to, you can check for this explicitly by inspecting the pointer
/// returned from [`as_ptr`].
///
/// Note also that this represents that the _current_ class's instance
/// variables are not yet initialized; but subclass instance variables may
/// have been so.
///
/// See [Apple's documentation on Object Allocation][object-allocation] for a
/// few more details.
///
/// [`as_ptr`]: Self::as_ptr
/// [object-allocation]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectAllocation/ObjectAllocation.html
///
///
/// # Memory layout
///
/// This is guaranteed to have the same size and alignment as a pointer to the
/// object, `*const T`. The pointer may be NULL.
#[repr(transparent)]
#[derive(Debug)]
pub struct Allocated<T: ?Sized> {
/// The yet-to-be initialized object.
///
/// We don't use `Retained` here, since that has different auto-trait
/// impls, and requires in its safety contract that the object is
/// initialized (which makes it difficult to ensure correctness if such
/// things are split across different files). Additionally, we want to
/// have fine control over NULL-ness.
///
/// Covariance is correct, same as `Retained`.
ptr: *const T, // Intentially not `NonNull`!
/// Necessary for dropck, as with `Retained`.
p: PhantomData<T>,
/// Necessary for restricting auto traits.
///
/// We _could_ probably implement auto traits `Send` and `Sync` here, but to be
/// safe, we won't for now.
p_auto_traits: PhantomData<AnyObject>,
}
// Explicitly don't implement `Deref`, `Message` nor `RefEncode`.
impl<T: ?Sized + Message> Allocated<T> {
/// # Safety
///
/// The caller must ensure the pointer is NULL, or that the given object
/// has +1 retain count, and that the object behind the pointer has been
/// allocated (but not yet initialized).
#[inline]
pub(crate) unsafe fn new(ptr: *mut T) -> Self {
Self {
ptr,
p: PhantomData,
p_auto_traits: PhantomData,
}
}
/// Returns a raw pointer to the object.
///
/// The pointer is valid for at least as long as the `Allocated` is held.
///
/// See [`Allocated::as_mut_ptr`] for the mutable equivalent.
///
/// This is an associated method, and must be called as
/// `Allocated::as_ptr(obj)`.
#[inline]
pub fn as_ptr(this: &Self) -> *const T {
this.ptr
}
/// Returns a raw mutable pointer to the object.
///
/// The pointer is valid for at least as long as the `Allocated` is held.
///
/// See [`Allocated::as_ptr`] for the immutable equivalent.
///
/// This is an associated method, and must be called as
/// `Allocated::as_mut_ptr(obj)`.
///
///
/// # Note about mutable references
///
/// In general, you're not allowed to create a mutable reference from
/// `Allocated`, unless you're defining the object and know that to be
/// safe.
///
/// For example, `+[NSMutableString alloc]` is allowed to return a
/// non-unique object as an optimization, and then only figure out
/// afterwards whether it needs to allocate, or if it can store an
/// `NSString` internally.
///
/// Similarly, while e.g. `+[NSData alloc]` may return a unique object,
/// calling `-[NSData init]` afterwards could return a shared empty
/// `NSData` instance.
#[inline]
#[allow(unknown_lints)] // New lint below
#[allow(clippy::needless_pass_by_ref_mut)]
pub fn as_mut_ptr(this: &mut Self) -> *mut T {
// Note: Not bound by `T: IsMutable`, since mutable pointers _can_ be
// safe for non-mutable classes, especially right when they're being
// allocated / initialized.
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
}
/// Initialize the instance variables for this object.
///
/// This consumes the allocated instance, and returns the now partially
/// initialized instance instead, which can be further used in
/// [`msg_send_id!`] `super` calls.
///
/// This works very similarly to [Swift's two-phase initialization
/// scheme][two-phase-init], see that for details.
///
/// [`msg_send_id!`]: crate::msg_send_id
/// [two-phase-init]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/#Two-Phase-Initialization
///
///
/// # Panics
///
/// If debug assertions are enabled, this function will panic if the
/// allocated instance is `NULL`, which usually only happens in Out of
/// Memory situations.
///
/// If debug assertions are disabled, this will return a `NULL` instance
/// and the ivars will be dropped. The NULL instance cannot cause
/// unsoundness and will likely lead to an initialization failure later on
/// instead, but not panicking here is done as a code-size optimization.
//
// Note: This is intentionally _not_ an associated method, even though
// `Allocated` will become `MethodReceiver` in the future.
#[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) {
// SAFETY: The pointer came from `self`, so it is valid.
unsafe { initialize_ivars::<T>(ptr, ivars) };
// SAFETY:
// - The pointer came from a `ManuallyDrop<Allocated<T>>`, which means
// that we've now transfered ownership over +1 retain count.
// - The instance variables for this class have been intialized above.
unsafe { PartialInit::new(ptr.as_ptr()) }
} else if cfg!(debug_assertions) {
panic!("tried to initialize instance variables on a NULL allocated object")
} else {
// Explicitly drop the ivars in this branch
drop(ivars);
// Create a new NULL PartialInit, which will likely be checked for
// NULL-ness later on, after initialization of it has failed.
//
// SAFETY: The pointer is NULL.
unsafe { PartialInit::new(ptr::null_mut()) }
}
}
}
impl<T: ?Sized> Drop for Allocated<T> {
#[inline]
fn drop(&mut self) {
// SAFETY: Allocated objects can always safely be released, since
// destructors are written to take into account that the object may
// not have been initialized.
//
// This is also safe in the case where the object is NULL,
// since `objc_release` allows NULL pointers.
//
// Rest is same as `Retained`'s `Drop`.
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)
}
}
/// An Objective-C object that has been allocated and initialized in the
/// current class, but not yet initialized in the superclass.
///
/// This is returned by [`Allocated::set_ivars`], and is intended to be used
/// further in [`msg_send_id!`] `super` calls.
///
/// [`msg_send_id!`]: crate::msg_send_id
///
///
/// # Memory layout
///
/// The memory layout of this struct is NOT currently guaranteed, as we may
/// want to be able to move a drop flag to the stack in the future.
//
// Internally, this is very similar to `Allocated`, except that we have
// different guarantees on the validity of the object.
#[repr(transparent)]
#[derive(Debug)]
pub struct PartialInit<T: ?Sized> {
/// The partially initialized object.
///
/// Variance is same as `Retained`.
ptr: *const T, // Intentionally not NonNull<T>
/// Necessary for dropck, as with `Retained`.
p: PhantomData<T>,
/// Restrict auto traits, same as `Allocated<T>`.
p_auto_traits: PhantomData<AnyObject>,
}
impl<T: ?Sized + Message> PartialInit<T> {
/// # Safety
///
/// The caller must ensure the pointer is NULL, or that the given object
/// is allocated, has +1 retain count, and that the class' instance
/// variables have been initialized.
#[inline]
pub(crate) unsafe fn new(ptr: *mut T) -> Self {
Self {
ptr,
p: PhantomData,
p_auto_traits: PhantomData,
}
}
/// Returns a raw pointer to the object.
///
/// The pointer is valid for at least as long as the `PartialInit` is
/// held.
///
/// See [`PartialInit::as_mut_ptr`] for the mutable equivalent.
///
/// This is an associated method, and must be called as
/// `PartialInit::as_ptr(obj)`.
#[inline]
pub fn as_ptr(this: &Self) -> *const T {
this.ptr
}
/// Returns a raw mutable pointer to the object.
///
/// The pointer is valid for at least as long as the `PartialInit` is
/// held.
///
/// See [`PartialInit::as_ptr`] for the immutable equivalent.
///
/// This is an associated method, and must be called as
/// `PartialInit::as_mut_ptr(obj)`.
#[inline]
#[allow(unknown_lints)] // New lint below
#[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) {
// SAFETY: Partially initialized objects can always safely be
// released, since destructors are written to take into account that
// the object may not have been fully initialized.
//
// This is also safe in the case where the object is NULL,
// since `objc_release` allows NULL pointers.
//
// Rest is same as `Retained`.
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>,
}
/// Test that `Allocated<T>` is covariant over `T`.
#[allow(unused)]
fn assert_allocated_variance<'b>(obj: Allocated<MyObject<'static>>) -> Allocated<MyObject<'b>> {
obj
}
/// Test that `PartialInit<T>` is covariant over `T`.
#[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() {
// SAFETY: The pointer is NULL
let obj: Allocated<RcTestObject> = unsafe { Allocated::new(ptr::null_mut()) };
let _ = obj.set_ivars(());
}
}

564
vendor/objc2/src/rc/autorelease.rs vendored Normal file
View File

@@ -0,0 +1,564 @@
use core::ffi::c_void;
#[cfg(not(all(debug_assertions, not(feature = "unstable-autoreleasesafe"))))]
use core::marker::PhantomData;
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
use std::{cell::RefCell, thread_local, vec::Vec};
use crate::ffi;
/// The actual pool object.
///
/// It is drained when dropped.
///
/// This is not [`Send`], since `objc_autoreleasePoolPop` must be called on
/// the same thread as `objc_autoreleasePoolPush`.
///
/// And this is not [`Sync`], since that would make `AutoreleasePool` `Send`.
#[derive(Debug)]
struct Pool {
/// This is an opaque handle, and is not guaranteed to be neither a valid
/// nor an aligned pointer.
context: *mut c_void,
}
impl Pool {
/// Construct a new autorelease pool.
///
///
/// # Safety
///
/// The caller must ensure that when handing out `AutoreleasePool<'p>` to
/// functions that this is the innermost pool.
///
/// Additionally, the pools must be dropped in the same order they were
/// created.
#[inline]
unsafe fn new() -> Self {
let context = unsafe { ffi::objc_autoreleasePoolPush() };
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
POOLS.with(|c| c.borrow_mut().push(context));
Self { context }
}
/// Drains the autoreleasepool.
///
/// The [clang documentation] says that `@autoreleasepool` blocks are not
/// drained when exceptions occur because:
///
/// > Not draining the pool during an unwind is apparently required by the
/// > Objective-C exceptions implementation.
///
/// We _would_ really like to do this anyway whenever possible, since the
/// unwind is probably caused by Rust, and forgetting to pop the pool will
/// likely leak memory.
///
/// The above statement was true in the past, but since [revision `551.1`]
/// of objc4 (ships with MacOS 10.9) the exception is now retained when
/// `@throw` is encountered (on __OBJC2__, so e.g. not on macOS 32bit).
///
/// So in the future, once we drop support for older versions, we should
/// move this to `Drop`.
///
/// [clang documentation]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#autoreleasepool
/// [revision `551.1`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-551.1/runtime/objc-exception.mm#L516
#[inline]
unsafe fn drain(self) {
unsafe { ffi::objc_autoreleasePoolPop(self.context) }
}
}
impl Drop for Pool {
#[inline]
fn drop(&mut self) {
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
POOLS.with(|c| {
assert_eq!(
c.borrow_mut().pop(),
Some(self.context),
"popped pool that was not the innermost pool"
);
});
}
}
/// An Objective-C autorelease pool.
///
/// Autorelease pools are a way to store objects in a certain thread-local
/// scope, such that they are only released at the end of said scope.
///
/// See [`autoreleasepool`] and [`autoreleasepool_leaking`] for how to create
/// this.
///
/// This is not [`Send`] nor [`Sync`], since you can only autorelease a
/// reference to a pool on the current thread.
///
///
/// # Example
///
/// Use the pool as a bound on a function, and release an object to that pool.
///
/// ```
/// use objc2::rc::{autoreleasepool, AutoreleasePool};
/// use objc2::runtime::NSObject;
/// use objc2::msg_send;
///
/// fn needs_lifetime_from_pool<'p>(pool: AutoreleasePool<'p>) -> &'p NSObject {
/// let obj = NSObject::new();
/// // Do action that returns an autoreleased object
/// let description: *mut NSObject = unsafe { msg_send![&obj, description] };
/// // Bound the lifetime of the reference to the pool
/// unsafe { pool.ptr_as_ref(description) }
/// }
///
/// autoreleasepool(|pool| {
/// let obj = needs_lifetime_from_pool(pool);
/// println!("{obj:?}");
/// });
/// ```
#[derive(Debug, Copy, Clone)]
pub struct AutoreleasePool<'pool> {
/// A reference to the pool.
///
/// The lifetime is covariant, since shortening the lifetime is not a
/// problem (the lifetime talks about the pool, and not any data inside
/// the pool).
///
/// To somewhat prove this, consider the following example using
/// `typed-arena` to partially implement the autorelease pool:
///
/// ```ignore
/// struct Pool(typed_arena::Arena<String>);
///
/// pub struct AutoreleasePool<'pool>(&'pool Pool);
///
/// impl<'pool> AutoreleasePool<'pool> {
/// pub fn autorelease(self, s: String) -> &'pool str {
/// &*self.0.0.alloc(s)
/// }
///
/// pub fn autorelease_mut(self, s: String) -> &'pool mut str {
/// &mut *self.0.0.alloc(s)
/// }
/// }
///
/// pub fn autoreleasepool<F, R>(f: F) -> R
/// where
/// F: for<'pool> FnOnce(AutoreleasePool<'pool>) -> R
/// {
/// let pool = Pool(Default::default());
/// f(AutoreleasePool(&pool))
/// }
/// ```
///
/// Hence assuming `typed-arena` is sound, having covariance here should
/// also be sound.
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
inner: Option<&'pool Pool>,
/// We use `PhantomData` here to make `AutoreleasePool` a ZST.
#[cfg(not(all(debug_assertions, not(feature = "unstable-autoreleasesafe"))))]
inner: PhantomData<&'pool Pool>,
}
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
thread_local! {
/// We track the thread's pools to verify that object lifetimes are only
/// taken from the innermost pool.
static POOLS: RefCell<Vec<*mut c_void>> = const { RefCell::new(Vec::new()) };
}
impl<'pool> AutoreleasePool<'pool> {
fn new(_inner: Option<&'pool Pool>) -> Self {
Self {
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
inner: _inner,
#[cfg(not(all(debug_assertions, not(feature = "unstable-autoreleasesafe"))))]
inner: PhantomData,
}
}
/// This will be removed in a future version.
#[inline]
#[doc(hidden)]
pub fn __verify_is_inner(self) {
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
if let Some(pool) = &self.inner {
POOLS.with(|c| {
assert_eq!(
c.borrow().last(),
Some(&pool.context),
"tried to use lifetime from pool that was not innermost"
);
});
}
}
/// Returns a shared reference to the given autoreleased pointer object.
///
/// This is the preferred way to make references from autoreleased
/// objects, since it binds the lifetime of the reference to the pool, and
/// does some extra checks when debug assertions are enabled.
///
/// For the mutable counterpart see [`ptr_as_mut`](#method.ptr_as_mut).
///
///
/// # Safety
///
/// This is equivalent to `&*ptr`, and shares the unsafety of that, except
/// the lifetime is bound to the pool instead of being unbounded.
#[inline]
pub unsafe fn ptr_as_ref<T: ?Sized>(self, ptr: *const T) -> &'pool T {
self.__verify_is_inner();
// SAFETY: Checked by the caller
unsafe { ptr.as_ref().unwrap_unchecked() }
}
/// Returns a unique reference to the given autoreleased pointer object.
///
/// This is the preferred way to make mutable references from autoreleased
/// objects, since it binds the lifetime of the reference to the pool, and
/// does some extra checks when debug assertions are enabled.
///
/// For the shared counterpart see [`ptr_as_ref`](#method.ptr_as_ref).
///
///
/// # Safety
///
/// This is equivalent to `&mut *ptr`, and shares the unsafety of that,
/// except the lifetime is bound to the pool instead of being unbounded.
#[inline]
pub unsafe fn ptr_as_mut<T: ?Sized>(self, ptr: *mut T) -> &'pool mut T {
self.__verify_is_inner();
// SAFETY: Checked by the caller
unsafe { ptr.as_mut().unwrap_unchecked() }
}
}
/// We use a macro here so that the documentation is included whether the
/// feature is enabled or not.
#[cfg(not(feature = "unstable-autoreleasesafe"))]
macro_rules! auto_trait {
{$(#[$fn_meta:meta])* $v:vis unsafe trait AutoreleaseSafe {}} => {
$(#[$fn_meta])*
$v unsafe trait AutoreleaseSafe {}
}
}
#[cfg(feature = "unstable-autoreleasesafe")]
macro_rules! auto_trait {
{$(#[$fn_meta:meta])* $v:vis unsafe trait AutoreleaseSafe {}} => {
$(#[$fn_meta])*
$v unsafe auto trait AutoreleaseSafe {}
}
}
auto_trait! {
/// Marks types that are safe to pass across the closure in an
/// [`autoreleasepool`].
///
/// With the `"unstable-autoreleasesafe"` feature enabled, this is an auto
/// trait that is implemented for all types except [`AutoreleasePool`].
///
/// Otherwise it is a dummy trait that is implemented for all types; the
/// safety invariants are checked with debug assertions instead.
///
/// You should not normally need to implement this trait yourself.
///
///
/// # Safety
///
/// Must not be implemented for types that interract with the autorelease
/// pool. So if you reimplement the [`AutoreleasePool`] struct or
/// likewise, this should be negatively implemented for that.
///
/// This can be accomplished with an `PhantomData<AutoreleasePool<'_>>` if
/// the `"unstable-autoreleasesafe"` feature is enabled.
///
///
/// # Examples
///
/// Most types are [`AutoreleaseSafe`].
///
/// ```
/// use objc2::rc::{AutoreleasePool, AutoreleaseSafe};
/// fn requires_autoreleasesafe<T: AutoreleaseSafe>() {}
/// requires_autoreleasesafe::<()>();
/// requires_autoreleasesafe::<Box<Vec<i32>>>();
/// requires_autoreleasesafe::<fn(AutoreleasePool<'_>)>();
/// ```
///
/// But [`AutoreleasePool`] isn't (if the `"unstable-autoreleasesafe"`
/// feature is enabled).
///
#[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail")]
#[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```")]
/// use objc2::rc::AutoreleasePool;
/// # use objc2::rc::AutoreleaseSafe;
/// # fn requires_autoreleasesafe<T: AutoreleaseSafe>() {}
/// requires_autoreleasesafe::<AutoreleasePool<'static>>();
/// ```
///
/// This also means that trait objects aren't (since they may contain an
/// [`AutoreleasePool`] internally):
///
#[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail")]
#[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```")]
/// # use objc2::rc::AutoreleaseSafe;
/// # fn requires_autoreleasesafe<T: AutoreleaseSafe>() {}
/// requires_autoreleasesafe::<&dyn std::io::Write>();
/// ```
pub unsafe trait AutoreleaseSafe {}
}
#[cfg(not(feature = "unstable-autoreleasesafe"))]
unsafe impl<T: ?Sized> AutoreleaseSafe for T {}
#[cfg(feature = "unstable-autoreleasesafe")]
impl !AutoreleaseSafe for Pool {}
#[cfg(feature = "unstable-autoreleasesafe")]
impl !AutoreleaseSafe for AutoreleasePool<'_> {}
/// Execute `f` in the context of a new autorelease pool. The pool is drained
/// after the execution of `f` completes.
///
/// This corresponds to `@autoreleasepool` blocks in Objective-C and Swift.
///
/// The pool is passed as a parameter to the closure to give you a lifetime
/// parameter that autoreleased objects can refer to.
///
/// Note that this is mostly useful for preventing leaks (as any Objective-C
/// method may autorelease internally - see also [`autoreleasepool_leaking`]).
/// If implementing an interface to an object, you should try to return
/// retained pointers with [`msg_send_id!`] wherever you can instead, since
/// it is usually more efficient, and having to use this function can be quite
/// cumbersome for users.
///
/// [`msg_send_id!`]: crate::msg_send_id
///
///
/// # Restrictions
///
/// The given parameter must not be used in an inner `autoreleasepool` - doing
/// so will panic with debug assertions enabled, and be a compile error in a
/// future release.
///
/// Note that this means that **this function is currently unsound**, since it
/// doesn't disallow wrong usage in all cases. Enabling the assertions in
/// release mode would be prohibitively expensive though, so this is the
/// least-bad solution.
///
/// You can try to compile your crate with the `"unstable-autoreleasesafe"`
/// crate feature enabled on nightly Rust - if your crate compiles with that,
/// its autoreleasepool usage is guaranteed to be correct.
///
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use objc2::rc::{autoreleasepool, Retained};
/// use objc2::runtime::NSObject;
///
/// autoreleasepool(|pool| {
/// // Create `obj` and autorelease it to the pool
/// let obj = Retained::autorelease(NSObject::new(), pool);
/// // We now have a reference that we can freely use
/// println!("{obj:?}");
/// // `obj` is deallocated when the pool ends
/// });
/// // And is no longer usable outside the closure
/// ```
///
/// Fails to compile because `obj` does not live long enough for us to take it
/// out of the pool:
///
/// ```compile_fail
/// use objc2::rc::{autoreleasepool, Retained};
/// use objc2::runtime::NSObject;
///
/// let obj = autoreleasepool(|pool| {
/// Retained::autorelease(NSObject::new(), pool)
/// });
/// ```
///
/// Fails to compile with the `"unstable-autoreleasesafe"` feature enabled, or
/// panics with debug assertions enabled, because we tried to pass an outer
/// pool to an inner pool:
///
#[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail")]
#[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```should_panic")]
/// use objc2::rc::{autoreleasepool, Retained};
/// use objc2::runtime::NSObject;
///
/// autoreleasepool(|outer_pool| {
/// let obj = autoreleasepool(|inner_pool| {
/// Retained::autorelease(NSObject::new(), outer_pool)
/// });
/// // `obj` could wrongly be used here because its lifetime was
/// // assigned to the outer pool, even though it was released by the
/// // inner pool already.
/// });
/// #
/// # panic!("Does not panic in release mode, so for testing we make it!");
/// ```
///
/// It is impossible to extend the lifetime of the pool.
///
/// ```compile_fail
/// use std::cell::RefCell;
/// use objc2::rc::{autoreleasepool, AutoreleasePool};
///
/// thread_local! {
/// static POOL: RefCell<Option<&'static AutoreleasePool<'static>>> = RefCell::new(None);
/// }
///
/// autoreleasepool(|pool| {
/// POOL.with(|p| {
/// *p.borrow_mut() = Some(Box::leak(Box::new(pool)))
/// });
/// });
/// ```
#[doc(alias = "@autoreleasepool")]
#[doc(alias = "objc_autoreleasePoolPush")]
#[doc(alias = "objc_autoreleasePoolPop")]
#[inline]
pub fn autoreleasepool<T, F>(f: F) -> T
where
for<'pool> F: AutoreleaseSafe + FnOnce(AutoreleasePool<'pool>) -> T,
{
// SAFETY:
// - The `AutoreleaseSafe` bound on the closure ensures that no pool from
// a different "level" can be passed down through and used in this one.
// - The pools are guaranteed to be dropped in the reverse order they were
// created (since you can't possibly "interleave" closures).
//
// This would not work if we e.g. allowed users to create pools on the
// stack, since they could then safely control the drop order.
let pool = unsafe { Pool::new() };
let res = f(AutoreleasePool::new(Some(&pool)));
unsafe { pool.drain() };
res
}
/// Execute `f` in the context of a "fake" autorelease pool.
///
/// This is useful to create a context in which to use autoreleased objects,
/// without the overhead of actually creating and draining the pool.
///
/// Any function boundary in Objective-C is an implicit autorelease pool, so
/// there you'd do `id obj2 = [obj autorelease]` and be done with it - but we
/// do this using a closure instead because we need some way to bind the
/// lifetime of any objects released to the pool.
///
///
/// # Examples
///
/// Autorelease an object to an outer pool, from inside an inner, "fake" pool.
///
/// ```
/// use objc2::rc::{autoreleasepool, autoreleasepool_leaking, Retained};
/// use objc2::runtime::NSObject;
///
/// autoreleasepool(|outer_pool| {
/// let obj = autoreleasepool_leaking(|inner_pool| {
/// Retained::autorelease(NSObject::new(), outer_pool)
/// });
/// // `obj` is still usable here, since the leaking pool doesn't actually
/// // do anything.
/// println!("{obj:?}");
/// });
///
/// // But it is not usable here, since the outer pool has been closed
/// ```
///
/// Like [`autoreleasepool`], you can't extend the lifetime of an object to
/// outside the closure.
///
/// ```compile_fail
/// use objc2::rc::{autoreleasepool_leaking, Retained};
/// use objc2::runtime::NSObject;
///
/// let obj = autoreleasepool_leaking(|pool| {
/// Retained::autorelease(NSObject::new(), pool)
/// });
/// ```
///
/// While you can pass an outer pool into this, you still can't pass the pool
/// from this into [`autoreleasepool`]:
///
#[cfg_attr(feature = "unstable-autoreleasesafe", doc = "```compile_fail")]
#[cfg_attr(not(feature = "unstable-autoreleasesafe"), doc = "```should_panic")]
/// use objc2::rc::{autoreleasepool, autoreleasepool_leaking, Retained};
/// use objc2::runtime::NSObject;
///
/// autoreleasepool_leaking(|outer_pool| {
/// let obj = autoreleasepool(|inner_pool| {
/// Retained::autorelease(NSObject::new(), outer_pool)
/// });
/// });
/// #
/// # panic!("Does not panic in release mode, so for testing we make it!");
/// ```
#[inline]
pub fn autoreleasepool_leaking<T, F>(f: F) -> T
where
for<'pool> F: FnOnce(AutoreleasePool<'pool>) -> T,
{
// SAFETY: This is effectively what most Objective-C code does; they
// assume that there's an autorelease pool _somewhere_ in the call stack
// above it, and then use their autoreleased objects for a duration that
// is guaranteed to be shorter than that.
//
// The `AutoreleaseSafe` bound is not required, since we don't actually do
// anything inside this; hence if the user know they have the _actual_
// innermost pool, they may still safely use it to extend the lifetime
// beyond this closure.
f(AutoreleasePool::new(None))
}
#[cfg(test)]
mod tests {
use core::mem;
use core::panic::{RefUnwindSafe, UnwindSafe};
use static_assertions::{assert_impl_all, assert_not_impl_any};
use super::{AutoreleasePool, AutoreleaseSafe};
use crate::runtime::AnyObject;
#[test]
fn auto_traits() {
assert_impl_all!(AutoreleasePool<'static>: Unpin, UnwindSafe, RefUnwindSafe);
assert_not_impl_any!(AutoreleasePool<'static>: Send, Sync);
assert_impl_all!(usize: AutoreleaseSafe);
assert_impl_all!(*mut AnyObject: AutoreleaseSafe);
assert_impl_all!(&mut AnyObject: AutoreleaseSafe);
#[cfg(feature = "unstable-autoreleasesafe")]
assert_not_impl_any!(AutoreleasePool<'static>: AutoreleaseSafe);
}
#[allow(unused)]
fn assert_covariant1<'a>(pool: AutoreleasePool<'static>) -> AutoreleasePool<'a> {
pool
}
#[allow(unused)]
fn assert_covariant2<'long: 'short, 'short>(
pool: AutoreleasePool<'long>,
) -> AutoreleasePool<'short> {
pool
}
#[allow(unused)]
fn assert_object_safe(_: &dyn AutoreleaseSafe) {}
#[cfg_attr(
not(feature = "unstable-autoreleasesafe"),
ignore = "only stably ZST when `unstable-autoreleasesafe` is enabled"
)]
#[test]
fn assert_zst() {
assert_eq!(mem::size_of::<AutoreleasePool<'static>>(), 0);
}
}

1091
vendor/objc2/src/rc/id.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,263 @@
//! Trivial forwarding impls on `Retained`.
//!
//! Kept here to keep `id.rs` free from this boilerplate.
//!
//! `#[inline]` is used where the standard library `Box` uses it.
#![forbid(unsafe_code)]
use alloc::borrow;
use alloc::string::String;
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::fmt;
use core::future::Future;
use core::hash;
use core::pin::Pin;
use core::task::{Context, Poll};
use std::error::Error;
use std::io;
use super::Retained;
use crate::mutability::IsMutable;
#[allow(clippy::unconditional_recursion)]
impl<T: PartialEq + ?Sized> PartialEq for Retained<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
(**self).eq(&**other)
}
#[inline]
#[allow(clippy::partialeq_ne_impl)]
fn ne(&self, other: &Self) -> bool {
(**self).ne(&**other)
}
}
impl<T: Eq + ?Sized> Eq for Retained<T> {}
impl<T: PartialOrd + ?Sized> PartialOrd for Retained<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
#[inline]
fn lt(&self, other: &Self) -> bool {
(**self).lt(&**other)
}
#[inline]
fn le(&self, other: &Self) -> bool {
(**self).le(&**other)
}
#[inline]
fn ge(&self, other: &Self) -> bool {
(**self).ge(&**other)
}
#[inline]
fn gt(&self, other: &Self) -> bool {
(**self).gt(&**other)
}
}
impl<T: Ord + ?Sized> Ord for Retained<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T: hash::Hash + ?Sized> hash::Hash for Retained<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: hash::Hasher + ?Sized + IsMutable> hash::Hasher for Retained<T> {
fn finish(&self) -> u64 {
(**self).finish()
}
fn write(&mut self, bytes: &[u8]) {
(**self).write(bytes);
}
fn write_u8(&mut self, i: u8) {
(**self).write_u8(i);
}
fn write_u16(&mut self, i: u16) {
(**self).write_u16(i);
}
fn write_u32(&mut self, i: u32) {
(**self).write_u32(i);
}
fn write_u64(&mut self, i: u64) {
(**self).write_u64(i);
}
fn write_u128(&mut self, i: u128) {
(**self).write_u128(i);
}
fn write_usize(&mut self, i: usize) {
(**self).write_usize(i);
}
fn write_i8(&mut self, i: i8) {
(**self).write_i8(i);
}
fn write_i16(&mut self, i: i16) {
(**self).write_i16(i);
}
fn write_i32(&mut self, i: i32) {
(**self).write_i32(i);
}
fn write_i64(&mut self, i: i64) {
(**self).write_i64(i);
}
fn write_i128(&mut self, i: i128) {
(**self).write_i128(i);
}
fn write_isize(&mut self, i: isize) {
(**self).write_isize(i);
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for Retained<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for Retained<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: ?Sized> borrow::Borrow<T> for Retained<T> {
fn borrow(&self) -> &T {
// Auto-derefs
self
}
}
impl<T: ?Sized + IsMutable> borrow::BorrowMut<T> for Retained<T> {
fn borrow_mut(&mut self) -> &mut T {
// Auto-derefs
self
}
}
impl<T: ?Sized> AsRef<T> for Retained<T> {
fn as_ref(&self) -> &T {
// Auto-derefs
self
}
}
impl<T: ?Sized + IsMutable> AsMut<T> for Retained<T> {
fn as_mut(&mut self) -> &mut T {
// Auto-derefs
self
}
}
impl<T: Error + ?Sized> Error for Retained<T> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
(**self).source()
}
}
impl<T: io::Read + ?Sized + IsMutable> io::Read for Retained<T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(**self).read(buf)
}
#[inline]
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
(**self).read_vectored(bufs)
}
#[inline]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
(**self).read_to_end(buf)
}
#[inline]
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
(**self).read_to_string(buf)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
(**self).read_exact(buf)
}
}
impl<T: io::Write + ?Sized + IsMutable> io::Write for Retained<T> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(**self).write(buf)
}
#[inline]
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
(**self).write_vectored(bufs)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
(**self).flush()
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
(**self).write_all(buf)
}
#[inline]
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
(**self).write_fmt(fmt)
}
}
impl<T: io::Seek + ?Sized + IsMutable> io::Seek for Retained<T> {
#[inline]
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
(**self).seek(pos)
}
#[inline]
fn stream_position(&mut self) -> io::Result<u64> {
(**self).stream_position()
}
}
impl<T: io::BufRead + ?Sized + IsMutable> io::BufRead for Retained<T> {
#[inline]
fn fill_buf(&mut self) -> io::Result<&[u8]> {
(**self).fill_buf()
}
#[inline]
fn consume(&mut self, amt: usize) {
(**self).consume(amt);
}
#[inline]
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
(**self).read_until(byte, buf)
}
#[inline]
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
(**self).read_line(buf)
}
}
impl<T: Future + Unpin + ?Sized + IsMutable> Future for Retained<T> {
type Output = T::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
T::poll(Pin::new(&mut *self), cx)
}
}
// TODO: impl Fn traits, CoerceUnsized, Stream and so on when stabilized

275
vendor/objc2/src/rc/id_traits.rs vendored Normal file
View File

@@ -0,0 +1,275 @@
//! Helper traits for Retained.
use super::Retained;
use crate::mutability::IsMutable;
/// Helper trait to implement [`Default`] on [`Retained`].
#[doc(alias = "DefaultId")]
pub trait DefaultRetained {
/// The default [`Retained`] for a type.
///
/// Soft-deprecated alias of [`DefaultRetained::default_retained`].
fn default_id() -> Retained<Self>;
/// The default [`Retained`] for a type.
///
/// On most objects the implementation would be sending a message to the
/// `new` selector.
#[inline]
fn default_retained() -> Retained<Self> {
Self::default_id()
}
}
impl<T: ?Sized + DefaultRetained> Default for Retained<T> {
#[inline]
fn default() -> Self {
T::default_retained()
}
}
/// Helper trait to implement [`IntoIterator`] on [`Retained`].
///
/// This should be implemented in exactly the same fashion as if you were
/// implementing `IntoIterator` for your type normally.
//
// Note that [`Box<T>` gets to cheat with regards moves][box-move], so
// `boxed.into_iter()` is possible, while `obj.into_iter()` is not possible
// without this helper trait.
//
// [box-move]: https://doc.rust-lang.org/reference/expressions.html#moved-and-copied-types
#[doc(alias = "IdIntoIterator")]
pub trait RetainedIntoIterator {
/// The type of the elements being iterated over.
type Item;
/// Which kind of iterator are we turning this into?
type IntoIter: Iterator<Item = Self::Item>;
/// Creates an iterator from an [`Retained`].
///
/// Soft-deprecated alias of [`RetainedIntoIterator::retained_into_iter`].
fn id_into_iter(this: Retained<Self>) -> Self::IntoIter;
/// Creates an iterator from an [`Retained`].
///
/// You would normally not call this function directly; instead, you'd
/// call [`into_iter`](IntoIterator::into_iter) on an [`Retained`].
#[inline]
fn retained_into_iter(this: Retained<Self>) -> Self::IntoIter {
Self::id_into_iter(this)
}
}
// Note: These `IntoIterator` implementations conflict with an `Iterator`
// implementation for `Retained`.
//
// For our case however (in contrast with `Box`), that is the better tradeoff,
// which I will show with an example:
//
// ```
// let xs = Box::new(vec![]);
// for x in &xs { // Doesn't compile, `&Box` doesn't implement `IntoIterator`
// // ...
// }
// ```
//
// Here, you're expected to write `xs.iter()` or `&**xs` instead, which is
// fairly acceptable, since usually people don't wrap things in boxes so much;
// but in Objective-C, _everything_ is wrapped in an `Retained`, and hence we should
// attempt to make that common case easier:
//
// ```
// let obj = NSArray::new(); // `Retained<NSArray<_>>`
// for item in &obj { // Should compile
// // ...
// }
// ```
//
// The loss of the `Iterator` impl is a bit unfortunate, but not a big deal,
// since there is only one iterator in Objective-C anyhow, `NSEnumerator`, and
// for that we can make other abstractions instead.
impl<T: ?Sized + RetainedIntoIterator> IntoIterator for Retained<T> {
type Item = <T as RetainedIntoIterator>::Item;
type IntoIter = <T as RetainedIntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
T::retained_into_iter(self)
}
}
impl<'a, T: ?Sized> IntoIterator for &'a Retained<T>
where
&'a T: IntoIterator,
{
type Item = <&'a T as IntoIterator>::Item;
type IntoIter = <&'a T as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&**self).into_iter()
}
}
impl<'a, T: ?Sized + IsMutable> IntoIterator for &'a mut Retained<T>
where
&'a mut T: IntoIterator,
{
type Item = <&'a mut T as IntoIterator>::Item;
type IntoIter = <&'a mut T as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&mut **self).into_iter()
}
}
/// Helper trait to implement [`FromIterator`] on [`Retained`].
///
/// This should be implemented in exactly the same fashion as if you were
/// implementing `FromIterator` for your type normally.
#[doc(alias = "IdFromIterator")]
pub trait RetainedFromIterator<T>: Sized {
/// Creates an `Retained` from an iterator.
///
/// Soft-deprecated alias of [`RetainedFromIterator::retained_from_iter`].
fn id_from_iter<I>(iter: I) -> Retained<Self>
where
I: IntoIterator<Item = T>;
/// Creates an `Retained` from an iterator.
#[inline]
fn retained_from_iter<I>(iter: I) -> Retained<Self>
where
I: IntoIterator<Item = T>,
{
Self::id_from_iter(iter)
}
}
impl<T, U: RetainedFromIterator<T>> FromIterator<T> for Retained<U> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
U::retained_from_iter(iter)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mutability::Mutable;
use crate::runtime::NSObject;
use crate::{declare_class, msg_send_id, ClassType, DeclaredClass};
declare_class!(
#[derive(PartialEq, Eq, Hash, Debug)]
struct Collection;
unsafe impl ClassType for Collection {
type Super = NSObject;
type Mutability = Mutable;
const NAME: &'static str = "MyCustomCollection";
}
impl DeclaredClass for Collection {}
);
impl DefaultRetained for Collection {
fn default_id() -> Retained<Self> {
unsafe { msg_send_id![Collection::class(), new] }
}
}
struct Iter<'a> {
_inner: &'a Collection,
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a NSObject;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
impl<'a> IntoIterator for &'a Collection {
type Item = &'a NSObject;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
Iter { _inner: self }
}
}
struct IterMut<'a> {
_inner: &'a mut Collection,
}
impl<'a> Iterator for IterMut<'a> {
type Item = &'a mut NSObject;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
impl<'a> IntoIterator for &'a mut Collection {
// Usually only valid if a mutable object is stored in the collection.
type Item = &'a mut NSObject;
type IntoIter = IterMut<'a>;
fn into_iter(self) -> Self::IntoIter {
IterMut { _inner: self }
}
}
struct IntoIter {
_inner: Retained<Collection>,
}
impl Iterator for IntoIter {
type Item = Retained<NSObject>;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
impl RetainedIntoIterator for Collection {
type Item = Retained<NSObject>;
type IntoIter = IntoIter;
fn id_into_iter(this: Retained<Self>) -> Self::IntoIter {
IntoIter { _inner: this }
}
}
impl RetainedFromIterator<Retained<NSObject>> for Collection {
fn id_from_iter<I: IntoIterator<Item = Retained<NSObject>>>(_iter: I) -> Retained<Self> {
Collection::default_retained()
}
}
#[test]
fn test_default() {
let obj1: Retained<Collection> = Default::default();
let obj2 = Collection::default_retained();
assert_ne!(obj1, obj2);
}
#[test]
fn test_into_iter() {
let mut obj: Retained<Collection> = Default::default();
for _ in &*obj {}
for _ in &obj {}
for _ in &mut *obj {}
for _ in &mut obj {}
for _ in obj {}
}
#[test]
fn test_from_iter() {
let _: Retained<Collection> = [NSObject::new()].into_iter().collect();
}
}

74
vendor/objc2/src/rc/mod.rs vendored Normal file
View File

@@ -0,0 +1,74 @@
//! # Reference counting utilities.
//!
//! The types in this module provide roughly the same benefits as ARC does to
//! Objective-C.
//!
//! Most importantly, a smart pointer [`Retained`] is provided to ensure that
//! objects are correctly retained and released when created and dropped,
//! respectively. This ties in strongly with the [`msg_send_id!`] macro.
//!
//! Weak references may be created using the [`Weak`] struct; these will not
//! retain the object, but one can attempt to load them and obtain an `Retained`, or
//! safely fail if the object has been deallocated.
//!
//! See [the clang documentation][clang-arc] and [the Apple article on memory
//! management][mem-mgmt] (similar document exists [for Core Foundation][cf])
//! for more information on automatic and manual reference counting.
//!
//! It can also be useful to [enable Malloc Debugging][mem-debug] if you're trying
//! to figure out if/where your application has memory errors and leaks.
//!
//! [`msg_send_id!`]: crate::msg_send_id
//! [clang-arc]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html
//! [mem-mgmt]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
//! [cf]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html
//! [mem-debug]: https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
//!
//!
//! ## Example
//!
//! ```
//! use objc2::rc::{autoreleasepool, Retained, Weak};
//! use objc2::runtime::NSObject;
//!
//! // Retained will release the object when dropped
//! let obj: Retained<NSObject> = NSObject::new();
//!
//! // Cloning retains the object an additional time
//! let cloned = obj.clone();
//! autoreleasepool(|pool| {
//! // Autorelease consumes the Retained, but won't actually
//! // release it until the end of the autoreleasepool
//! let obj_ref: &NSObject = Retained::autorelease(cloned, pool);
//! });
//!
//! // Weak references won't retain the object
//! let weak = Weak::from_retained(&obj);
//! drop(obj);
//! assert!(weak.load().is_none());
//! ```
mod allocated_partial_init;
mod autorelease;
mod id;
mod id_forwarding_impls;
mod id_traits;
#[cfg(test)]
mod test_object;
mod weak_id;
pub use self::allocated_partial_init::{Allocated, PartialInit};
pub use self::autorelease::{
autoreleasepool, autoreleasepool_leaking, AutoreleasePool, AutoreleaseSafe,
};
pub use self::id::{Id, Retained};
pub use self::id_traits::{DefaultRetained, RetainedFromIterator, RetainedIntoIterator};
#[cfg(test)]
pub(crate) use self::test_object::{RcTestObject, ThreadTestData};
pub use self::weak_id::{Weak, WeakId};
// Soft-deprecated aliases
pub use self::id_traits::{
DefaultRetained as DefaultId, RetainedFromIterator as IdFromIterator,
RetainedIntoIterator as IdIntoIterator,
};

315
vendor/objc2/src/rc/test_object.rs vendored Normal file
View File

@@ -0,0 +1,315 @@
//! Note: This file is included in the `tests` crate via. a symlink as well.
use core::cell::RefCell;
use core::ptr;
use crate::mutability::Immutable;
use crate::rc::{Allocated, DefaultRetained, Retained};
use crate::runtime::{NSObject, NSZone};
use crate::{declare_class, msg_send, msg_send_id, ClassType, DeclaredClass};
// TODO: Put tests that use this in another crate
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[allow(missing_copy_implementations)]
#[doc(hidden)]
pub(crate) struct ThreadTestData {
pub(crate) alloc: usize,
pub(crate) drop: usize,
pub(crate) init: usize,
pub(crate) retain: usize,
pub(crate) copy: usize,
pub(crate) mutable_copy: usize,
pub(crate) release: usize,
pub(crate) autorelease: usize,
pub(crate) try_retain: usize,
pub(crate) try_retain_fail: usize,
// TODO: Is there some way we can test weak pointers? Or is that implemented entirely in Foundation?
// Maybe `_setWeaklyReferenced` can be useful?
}
impl ThreadTestData {
/// Get the amount of method calls performed on the current thread.
pub(crate) fn current() -> Self {
TEST_DATA.with(|data| data.borrow().clone())
}
#[track_caller]
#[allow(clippy::missing_panics_doc)]
#[allow(dead_code)]
pub(crate) fn assert_current(&self) {
let current = Self::current();
let mut expected = self.clone();
if cfg!(feature = "gnustep-1-7") {
// GNUStep doesn't have `tryRetain`, it uses `retain` directly
let retain_diff = expected.try_retain - current.try_retain;
expected.retain += retain_diff;
expected.try_retain -= retain_diff;
// GNUStep doesn't call `autorelease` if it's overridden
expected.autorelease = 0;
}
if current != expected {
panic!(
"got differing amounts of calls:
current: `{current:?}`,
expected: `{expected:?}`"
)
}
}
}
std::thread_local! {
static TEST_DATA: RefCell<ThreadTestData> = RefCell::default();
}
declare_class!(
/// A helper object that counts how many times various reference-counting
/// primitives are called.
#[derive(Debug, PartialEq, Eq, Hash)]
#[doc(hidden)]
pub(crate) struct RcTestObject;
unsafe impl ClassType for RcTestObject {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "__RcTestObject";
}
impl DeclaredClass for RcTestObject {}
unsafe impl RcTestObject {
#[method_id(newReturningNull)]
fn new_returning_null() -> Option<Retained<Self>> {
None
}
#[method_id(newMethodOnInstance)]
fn new_method_on_instance(&self) -> Retained<Self> {
Self::new()
}
#[method_id(newMethodOnInstanceNull)]
fn new_method_on_instance_null(&self) -> Option<Retained<Self>> {
None
}
#[method(alloc)]
fn alloc_() -> *mut Self {
TEST_DATA.with(|data| data.borrow_mut().alloc += 1);
let superclass = NSObject::class().metaclass();
let zone: *const NSZone = ptr::null();
unsafe { msg_send![super(Self::class(), superclass), allocWithZone: zone] }
}
#[method(allocWithZone:)]
fn alloc_with_zone(zone: *const NSZone) -> *mut Self {
TEST_DATA.with(|data| data.borrow_mut().alloc += 1);
let superclass = NSObject::class().metaclass();
unsafe { msg_send![super(Self::class(), superclass), allocWithZone: zone] }
}
#[method(allocReturningNull)]
fn alloc_returning_null() -> *mut Self {
ptr::null_mut()
}
#[method_id(init)]
unsafe fn init(this: Allocated<Self>) -> Retained<Self> {
TEST_DATA.with(|data| data.borrow_mut().init += 1);
let this = this.set_ivars(());
unsafe { msg_send_id![super(this), init] }
}
#[method_id(initReturningNull)]
fn init_returning_null(_this: Allocated<Self>) -> Option<Retained<Self>> {
None
}
#[method(retain)]
fn retain(&self) -> *mut Self {
TEST_DATA.with(|data| data.borrow_mut().retain += 1);
unsafe { msg_send![super(self), retain] }
}
#[method(release)]
fn release(&self) {
TEST_DATA.with(|data| data.borrow_mut().release += 1);
unsafe { msg_send![super(self), release] }
}
#[method(autorelease)]
fn autorelease(&self) -> *mut Self {
TEST_DATA.with(|data| data.borrow_mut().autorelease += 1);
unsafe { msg_send![super(self), autorelease] }
}
#[method(_tryRetain)]
unsafe fn try_retain(&self) -> bool {
TEST_DATA.with(|data| data.borrow_mut().try_retain += 1);
let res: bool = unsafe { msg_send![super(self), _tryRetain] };
if !res {
TEST_DATA.with(|data| data.borrow_mut().try_retain -= 1);
TEST_DATA.with(|data| data.borrow_mut().try_retain_fail += 1);
}
res
}
#[method_id(copyWithZone:)]
fn copy_with_zone(&self, _zone: *const NSZone) -> Retained<Self> {
TEST_DATA.with(|data| data.borrow_mut().copy += 1);
Self::new()
}
#[method_id(mutableCopyWithZone:)]
fn mutable_copy_with_zone(&self, _zone: *const NSZone) -> Retained<Self> {
TEST_DATA.with(|data| data.borrow_mut().mutable_copy += 1);
Self::new()
}
#[method_id(copyReturningNull)]
fn copy_returning_null(_this: &Self) -> Option<Retained<Self>> {
None
}
#[method_id(methodReturningNull)]
fn method_returning_null(self: &Self) -> Option<Retained<Self>> {
None
}
#[method_id(aMethod:)]
fn a_method(&self, param: bool) -> Option<Retained<Self>> {
param.then(Self::new)
}
#[method(boolAndShouldError:error:)]
fn class_error_bool(should_error: bool, err: Option<&mut *mut RcTestObject>) -> bool {
if should_error {
if let Some(err) = err {
*err = Retained::autorelease_ptr(RcTestObject::new());
}
false
} else {
true
}
}
#[method(boolAndShouldError:error:)]
fn instance_error_bool(
&self,
should_error: bool,
err: Option<&mut *mut RcTestObject>,
) -> bool {
if should_error {
if let Some(err) = err {
*err = Retained::autorelease_ptr(RcTestObject::new());
}
false
} else {
true
}
}
#[method_id(idAndShouldError:error:)]
fn class_error_id(
should_error: bool,
err: Option<&mut *mut RcTestObject>,
) -> Option<Retained<Self>> {
if should_error {
if let Some(err) = err {
*err = Retained::autorelease_ptr(RcTestObject::new());
}
None
} else {
Some(Self::new())
}
}
#[method_id(idAndShouldError:error:)]
fn instance_error_id(
self: &Self,
should_error: bool,
err: Option<&mut *mut RcTestObject>,
) -> Option<Retained<Self>> {
if should_error {
if let Some(err) = err {
*err = Retained::autorelease_ptr(RcTestObject::new());
}
None
} else {
Some(Self::new())
}
}
#[method_id(newAndShouldError:error:)]
fn new_error(
should_error: bool,
err: Option<&mut *mut RcTestObject>,
) -> Option<Retained<Self>> {
if should_error {
if let Some(err) = err {
*err = Retained::autorelease_ptr(RcTestObject::new());
}
None
} else {
unsafe { msg_send_id![Self::class(), new] }
}
}
#[method(allocAndShouldError:error:)]
fn alloc_error(should_error: bool, err: Option<&mut *mut RcTestObject>) -> *mut Self {
if should_error {
if let Some(err) = err {
*err = Retained::autorelease_ptr(RcTestObject::new());
}
ptr::null_mut()
} else {
unsafe { msg_send![Self::class(), alloc] }
}
}
#[method_id(initAndShouldError:error:)]
fn init_error(
this: Allocated<Self>,
should_error: bool,
err: Option<&mut *mut RcTestObject>,
) -> Option<Retained<Self>> {
if should_error {
if let Some(err) = err {
*err = Retained::autorelease_ptr(RcTestObject::new());
}
None
} else {
unsafe { msg_send_id![this, init] }
}
}
#[method(outParamNull:)]
fn out_param_null(param: Option<&mut *mut RcTestObject>) {
if let Some(param) = param {
*param = ptr::null_mut();
}
}
}
);
impl Drop for RcTestObject {
fn drop(&mut self) {
TEST_DATA.with(|data| data.borrow_mut().drop += 1);
}
}
impl DefaultRetained for RcTestObject {
fn default_id() -> Retained<Self> {
Self::new()
}
}
unsafe impl Send for RcTestObject {}
unsafe impl Sync for RcTestObject {}
impl RcTestObject {
#[doc(hidden)]
pub(crate) fn new() -> Retained<Self> {
// Use msg_send! - msg_send_id! is tested elsewhere!
unsafe { Retained::from_raw(msg_send![Self::class(), new]) }.unwrap()
}
}

298
vendor/objc2/src/rc/weak_id.rs vendored Normal file
View File

@@ -0,0 +1,298 @@
use alloc::boxed::Box;
use core::cell::UnsafeCell;
use core::fmt;
use core::marker::PhantomData;
use core::ptr;
use std::panic::{RefUnwindSafe, UnwindSafe};
use super::Retained;
use crate::mutability::{IsIdCloneable, IsRetainable};
use crate::{ffi, Message};
/// A weak pointer to an Objective-C reference counted object.
///
/// The object is allowed to be deallocated while the weak pointer is alive,
/// though the backing allocation for the object can only be released once all
/// weak pointers are gone.
///
/// Useful for breaking reference cycles and safely checking whether an
/// object has been deallocated.
///
///
/// # Comparison to `std` types
///
/// This is the Objective-C equivalent of [`sync::Weak`] from the standard
/// library, and hence is only usable on types where `Retained<T>` acts like
/// [`sync::Arc`], a.k.a. on non-mutable types.
///
/// [`sync::Weak`]: std::sync::Weak
/// [`sync::Arc`]: std::sync::Arc
#[repr(transparent)] // This is not a public guarantee
#[doc(alias = "WeakId")]
pub struct Weak<T: ?Sized> {
/// We give the runtime the address to this box, so that it can modify it
/// even if the `Weak` is moved.
///
/// Loading may modify the pointer through a shared reference, so we use
/// an UnsafeCell to get a *mut without self being mutable.
///
/// Remember that any thread may actually modify the inner value
/// concurrently, but as long as we only use it through the `objc_XXXWeak`
/// methods, all access is behind a lock.
///
/// TODO: Verify the need for UnsafeCell?
/// TODO: Investigate if we can avoid some allocations using `Pin`.
inner: Box<UnsafeCell<*mut ffi::objc_object>>,
/// Weak inherits variance, dropck and various marker traits from
/// `Retained<T>`.
item: PhantomData<Retained<T>>,
}
/// Soft-deprecated type-alias to [`Weak`].
pub type WeakId<T> = Weak<T>;
impl<T: Message> Weak<T> {
/// Construct a new weak pointer that references the given object.
#[doc(alias = "objc_initWeak")]
#[inline]
pub fn new(obj: &T) -> Self
where
T: IsRetainable,
{
// SAFETY: `obj` is retainable
unsafe { Self::new_inner(obj) }
}
/// Construct a new weak pointer that references the given [`Retained`].
///
/// Soft-deprecated alias of [`Weak::from_retained`].
#[doc(alias = "objc_initWeak")]
#[inline]
pub fn from_id(obj: &Retained<T>) -> Self
where
T: IsIdCloneable,
{
Self::from_retained(obj)
}
/// Construct a new weak pointer that references the given [`Retained`].
///
/// You should prefer [`Weak::new`] whenever the object is retainable.
#[doc(alias = "objc_initWeak")]
#[inline]
pub fn from_retained(obj: &Retained<T>) -> Self
where
T: IsIdCloneable,
{
// SAFETY: `obj` is cloneable, and is known to have come from `Retained`.
unsafe { Self::new_inner(Retained::as_ptr(obj)) }
}
/// Raw constructor.
///
///
/// # Safety
///
/// The object must be valid or null.
unsafe fn new_inner(obj: *const T) -> Self {
let inner = Box::new(UnsafeCell::new(ptr::null_mut()));
// SAFETY: `ptr` will never move, and the caller verifies `obj`
let _ = unsafe { ffi::objc_initWeak(inner.get(), (obj as *mut T).cast()) };
Self {
inner,
item: PhantomData,
}
}
/// Load the object into an [`Retained`] if it still exists.
///
/// Returns [`None`] if the object has been deallocated, or the `Weak`
/// was created with [`Default::default`].
#[doc(alias = "retain")]
#[doc(alias = "objc_loadWeak")]
#[doc(alias = "objc_loadWeakRetained")]
#[inline]
pub fn load(&self) -> Option<Retained<T>> {
let ptr = self.inner.get();
let obj = unsafe { ffi::objc_loadWeakRetained(ptr) }.cast();
// SAFETY: The object has +1 retain count
unsafe { Retained::from_raw(obj) }
}
// TODO: Add `autorelease(&self, pool) -> Option<&T>` using `objc_loadWeak`?
}
impl<T: ?Sized> Drop for Weak<T> {
/// Destroys the weak pointer.
#[doc(alias = "objc_destroyWeak")]
#[inline]
fn drop(&mut self) {
unsafe { ffi::objc_destroyWeak(self.inner.get()) }
}
}
// TODO: Add ?Sized
impl<T: Message + IsRetainable> Clone for Weak<T> {
/// Make a clone of the weak pointer that points to the same object.
#[doc(alias = "objc_copyWeak")]
fn clone(&self) -> Self {
let ptr = Box::new(UnsafeCell::new(ptr::null_mut()));
unsafe { ffi::objc_copyWeak(ptr.get(), self.inner.get()) };
Self {
inner: ptr,
item: PhantomData,
}
}
}
// TODO: Add ?Sized
impl<T: Message + IsRetainable> Default for Weak<T> {
/// Constructs a new weak pointer that doesn't reference any object.
///
/// Calling [`Self::load`] on the return value always gives [`None`].
#[inline]
fn default() -> Self {
// SAFETY: The pointer is null
unsafe { Self::new_inner(ptr::null()) }
}
}
impl<T: ?Sized> fmt::Debug for Weak<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Note: We intentionally don't try to debug-print the value, since
// that could lead to cycles. See:
// https://github.com/rust-lang/rust/pull/90291
write!(f, "(Weak)")
}
}
// Same as `std::sync::Weak<T>`.
unsafe impl<T: ?Sized + Sync + Send + IsIdCloneable> Sync for Weak<T> {}
// Same as `std::sync::Weak<T>`.
unsafe impl<T: ?Sized + Sync + Send + IsIdCloneable> Send for Weak<T> {}
// Same as `std::sync::Weak<T>`.
impl<T: ?Sized> Unpin for Weak<T> {}
// Same as `std::sync::Weak<T>`.
impl<T: ?Sized + RefUnwindSafe + IsIdCloneable> RefUnwindSafe for Weak<T> {}
// Same as `std::sync::Weak<T>`.
impl<T: ?Sized + RefUnwindSafe + IsIdCloneable> UnwindSafe for Weak<T> {}
impl<T: Message + IsRetainable> From<&T> for Weak<T> {
#[inline]
fn from(obj: &T) -> Self {
Weak::new(obj)
}
}
impl<T: Message + IsIdCloneable> From<&Retained<T>> for Weak<T> {
#[inline]
fn from(obj: &Retained<T>) -> Self {
Weak::from_retained(obj)
}
}
impl<T: Message + IsIdCloneable> From<Retained<T>> for Weak<T> {
#[inline]
fn from(obj: Retained<T>) -> Self {
Weak::from_retained(&obj)
}
}
#[cfg(test)]
mod tests {
use core::mem;
use super::*;
use crate::rc::{RcTestObject, ThreadTestData};
use crate::runtime::NSObject;
#[test]
fn test_weak() {
let obj = RcTestObject::new();
let mut expected = ThreadTestData::current();
let weak = Weak::from(&obj);
expected.assert_current();
let strong = weak.load().unwrap();
expected.try_retain += 1;
expected.assert_current();
assert!(ptr::eq(&*strong, &*obj));
drop(obj);
drop(strong);
expected.release += 2;
expected.drop += 1;
expected.assert_current();
if cfg!(not(feature = "gnustep-1-7")) {
// This loads the object on GNUStep for some reason??
assert!(weak.load().is_none());
expected.assert_current();
}
drop(weak);
expected.assert_current();
}
#[test]
fn test_weak_clone() {
let obj = RcTestObject::new();
let mut expected = ThreadTestData::current();
let weak = Weak::from(&obj);
expected.assert_current();
let weak2 = weak.clone();
if cfg!(target_vendor = "apple") {
expected.try_retain += 1;
expected.release += 1;
}
expected.assert_current();
let strong = weak.load().unwrap();
expected.try_retain += 1;
expected.assert_current();
assert!(ptr::eq(&*strong, &*obj));
let strong2 = weak2.load().unwrap();
expected.try_retain += 1;
expected.assert_current();
assert!(ptr::eq(&*strong, &*strong2));
drop(weak);
drop(weak2);
expected.assert_current();
}
#[test]
fn test_weak_default() {
let weak: Weak<RcTestObject> = Weak::default();
assert!(weak.load().is_none());
drop(weak);
}
#[repr(C)]
struct MyObject<'a> {
inner: NSObject,
p: PhantomData<&'a str>,
}
/// Test that `Weak<T>` is covariant over `T`.
#[allow(unused)]
fn assert_variance<'a, 'b>(obj: &'a Weak<MyObject<'static>>) -> &'a Weak<MyObject<'b>> {
obj
}
#[test]
fn test_size_of() {
assert_eq!(
mem::size_of::<Option<Weak<NSObject>>>(),
mem::size_of::<*const ()>()
);
}
}

77
vendor/objc2/src/runtime/__nsstring.rs vendored Normal file
View File

@@ -0,0 +1,77 @@
use core::slice;
use core::str;
use std::os::raw::c_char;
use crate::ffi::NSUInteger;
use crate::msg_send;
use crate::rc::AutoreleasePool;
use crate::runtime::NSObject;
// Note: While this is not public, it is still a breaking change to modify,
// since `objc2-foundation` relies on it.
#[cfg(not(feature = "gnustep-1-7"))]
pub const UTF8_ENCODING: usize = 4;
#[cfg(feature = "gnustep-1-7")]
pub const UTF8_ENCODING: i32 = 4;
/// The number of UTF-8 code units in the given string.
///
/// # Safety
///
/// The object must be an instance of `NSString`.
//
// Note: While this is not public, it is still a breaking change to modify,
// since `objc2-foundation` relies on it.
pub unsafe fn nsstring_len(obj: &NSObject) -> NSUInteger {
unsafe { msg_send![obj, lengthOfBytesUsingEncoding: UTF8_ENCODING] }
}
/// Extract a [`str`](`prim@str`) representation out of the given NSString.
///
/// # Safety
///
/// The object must be an instance of `NSString`.
//
// Note: While this is not public, it is still a breaking change to modify,
// since `objc2-foundation` relies on it.
pub unsafe fn nsstring_to_str<'r, 's: 'r, 'p: 'r>(
obj: &'s NSObject,
pool: AutoreleasePool<'p>,
) -> &'r str {
// This is necessary until `auto` types stabilizes.
pool.__verify_is_inner();
// The documentation on `UTF8String` is a bit sparse, but with
// educated guesses and testing I've determined that NSString stores
// a pointer to the string data, sometimes with an UTF-8 encoding,
// (usual for ascii data), sometimes in other encodings (UTF-16?).
//
// `UTF8String` then checks the internal encoding:
// - If the data is UTF-8 encoded, it returns the internal pointer.
// - If the data is in another encoding, it creates a new allocation,
// writes the UTF-8 representation of the string into it,
// autoreleases the allocation and returns a pointer to it.
//
// So the lifetime of the returned pointer is either the same as the
// NSString OR the lifetime of the innermost @autoreleasepool.
//
// https://developer.apple.com/documentation/foundation/nsstring/1411189-utf8string?language=objc
let bytes: *const c_char = unsafe { msg_send![obj, UTF8String] };
let bytes: *const u8 = bytes.cast();
// SAFETY: Caller ensures that the object is an instance of `NSString`.
let len = unsafe { nsstring_len(obj) };
// SAFETY:
// The held AutoreleasePool is the innermost, and the reference is
// constrained both by the pool and the NSString.
//
// `len` is the length of the string in the UTF-8 encoding.
//
// `bytes` is a null-terminated C string (with length = len + 1), so
// it is never a NULL pointer.
let bytes: &'r [u8] = unsafe { slice::from_raw_parts(bytes, len) };
// TODO: Always UTF-8, so should we use `from_utf8_unchecked`?
str::from_utf8(bytes).unwrap()
}

218
vendor/objc2/src/runtime/bool.rs vendored Normal file
View File

@@ -0,0 +1,218 @@
use core::fmt;
use crate::encode::{Encode, Encoding, RefEncode};
use crate::ffi;
/// The Objective-C `BOOL` type.
///
/// This is a thin wrapper-type over [`objc_sys::BOOL`]. It is intended that
/// you convert this into a Rust [`bool`] with the [`Bool::as_bool`] method as
/// soon as possible.
///
/// This is FFI-safe and can be used directly with `msg_send!` and `extern`
/// functions as a substitute for `BOOL` in Objective-C. If your Objective-C
/// code uses C99 `_Bool`, you should use a `#[repr(transparent)]` wrapper
/// around `bool` instead.
///
/// Note that this is able to contain more states than `bool` on some
/// platforms, but these cases should not be relied on!
#[repr(transparent)]
// We don't implement comparison traits because they could be implemented with
// two slightly different semantics:
// - `self.as_bool().cmp(other.as_bool())`
// - `self.value.cmp(other.value)`
// And it is not immediately clear for users which one was chosen.
#[derive(Copy, Clone, Default)]
pub struct Bool {
value: ffi::BOOL,
}
impl Bool {
/// The equivalent of [`true`] for Objective-C's `BOOL` type.
pub const YES: Self = Self::from_raw(ffi::YES);
/// The equivalent of [`false`] for Objective-C's `BOOL` type.
pub const NO: Self = Self::from_raw(ffi::NO);
/// Creates an Objective-C boolean from a Rust boolean.
#[inline]
pub const fn new(value: bool) -> Self {
// true as BOOL => 1 (YES)
// false as BOOL => 0 (NO)
let value = value as ffi::BOOL;
Self { value }
}
/// Creates this from a boolean value received from a raw Objective-C API.
#[inline]
pub const fn from_raw(value: ffi::BOOL) -> Self {
Self { value }
}
/// Retrieves the inner [`ffi::BOOL`] boolean type, to be used in raw
/// Objective-C APIs.
#[inline]
pub const fn as_raw(self) -> ffi::BOOL {
self.value
}
/// Returns `true` if `self` is [`NO`][Self::NO].
///
/// You should prefer using [`as_bool`][Self::as_bool].
#[inline]
pub const fn is_false(self) -> bool {
!self.as_bool()
}
/// Returns `true` if `self` is not [`NO`][Self::NO].
///
/// You should prefer using [`as_bool`][Self::as_bool].
#[inline]
pub const fn is_true(self) -> bool {
self.as_bool()
}
/// Converts this into the [`bool`] equivalent.
#[inline]
pub const fn as_bool(self) -> bool {
// Always compare with 0 (NO)
// This is what happens with the `!` operator / when using `if` in C.
self.value != ffi::NO
}
}
impl From<bool> for Bool {
#[inline]
fn from(b: bool) -> Bool {
Bool::new(b)
}
}
impl From<Bool> for bool {
#[inline]
fn from(b: Bool) -> bool {
b.as_bool()
}
}
impl fmt::Debug for Bool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.as_bool() { "YES" } else { "NO" })
}
}
trait Helper {
const __ENCODING: Encoding;
}
impl<T: Encode> Helper for T {
const __ENCODING: Encoding = T::ENCODING;
}
impl Helper for bool {
const __ENCODING: Encoding = Encoding::Bool;
}
// SAFETY: `Bool` is `repr(transparent)`.
unsafe impl Encode for Bool {
// i8::__ENCODING == Encoding::Char
// u8::__ENCODING == Encoding::UChar
// bool::__ENCODING == Encoding::Bool
// i32::__ENCODING == Encoding::Int
const ENCODING: Encoding = ffi::BOOL::__ENCODING;
}
// Note that we shouldn't delegate to `BOOL`'s `ENCODING_REF` since `BOOL` is
// sometimes `i8`/`u8`, and their `ENCODING_REF`s are `Encoding::String`,
// which is incorrect for `BOOL`:
//
// ```objc
// @encode(BOOL); // -> "c", "C" or "B"
// @encode(BOOL*); // -> "^c", "^C" or "^B"
// @encode(char); // -> "c" or "C"
// @encode(char*); // -> "*"
// ```
unsafe impl RefEncode for Bool {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::__macro_helpers::{ConvertArgument, ConvertReturn};
use alloc::format;
#[test]
fn test_basic() {
let b = Bool::new(true);
assert!(b.as_bool());
assert!(b.is_true());
assert!(!b.is_false());
assert!(bool::from(b));
assert_eq!(b.as_raw() as usize, 1);
let b = Bool::new(false);
assert!(!b.as_bool());
assert!(!b.is_true());
assert!(b.is_false());
assert!(!bool::from(b));
assert_eq!(b.as_raw() as usize, 0);
}
#[test]
fn test_associated_constants() {
let b = Bool::YES;
assert!(b.as_bool());
assert!(b.is_true());
assert_eq!(b.as_raw() as usize, 1);
let b = Bool::NO;
assert!(!b.as_bool());
assert!(b.is_false());
assert_eq!(b.as_raw() as usize, 0);
}
#[test]
fn test_encode() {
assert_eq!(bool::__ENCODING, Encoding::Bool);
assert_eq!(
<bool as ConvertArgument>::__Inner::__ENCODING,
<bool as ConvertArgument>::__Inner::ENCODING
);
assert_eq!(
<bool as ConvertReturn>::__Inner::__ENCODING,
<bool as ConvertReturn>::__Inner::ENCODING
);
}
#[test]
fn test_impls() {
let b: Bool = Default::default();
assert!(!b.as_bool());
assert!(b.is_false());
assert!(Bool::from(true).as_bool());
assert!(Bool::from(true).is_true());
assert!(Bool::from(false).is_false());
assert!(Bool::from(true).is_true());
assert!(Bool::from(false).is_false());
}
#[test]
fn test_debug() {
assert_eq!(format!("{:?}", Bool::from(true)), "YES");
assert_eq!(format!("{:?}", Bool::from(false)), "NO");
}
#[test]
// Test on platform where we know the type of BOOL
#[cfg(all(target_vendor = "apple", target_os = "macos", target_arch = "x86_64"))]
fn test_outside_normal() {
let b = Bool::from_raw(42);
assert!(b.is_true());
assert!(!b.is_false());
assert_eq!(b.as_raw(), 42);
}
}

1057
vendor/objc2/src/runtime/declare.rs vendored Normal file

File diff suppressed because it is too large Load Diff

151
vendor/objc2/src/runtime/malloc.rs vendored Normal file
View File

@@ -0,0 +1,151 @@
//! A minimal alternative to crates like `malloc_buf`, `mbox` and `malloced`.
use core::fmt;
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr::{self, NonNull};
use core::str;
use core::str::Utf8Error;
use std::ffi::CStr;
use std::os::raw::c_char;
use crate::ffi;
#[repr(transparent)]
pub(crate) struct MallocSlice<T> {
ptr: NonNull<[T]>,
// Necessary for dropck
_p: PhantomData<[T]>,
}
impl<T> MallocSlice<T> {
// Currently has to have the same API as `malloc_buf::Malloc`
pub(crate) unsafe fn from_array(mut ptr: *mut T, len: usize) -> Self {
// If the length is 0, the pointer is usually NULL, and as such we
// need to conjure some other pointer (slices are always non-null).
if len == 0 {
ptr = NonNull::dangling().as_ptr();
}
let ptr = ptr::slice_from_raw_parts_mut(ptr, len);
let ptr = NonNull::new(ptr).expect("tried to construct MallocSlice from a NULL pointer");
Self {
ptr,
_p: PhantomData,
}
}
fn len(&self) -> usize {
// TODO: Use `self.ptr.len()` once in MSRV
(**self).len()
}
}
impl<T> Drop for MallocSlice<T> {
fn drop(&mut self) {
// If the length is 0, then the pointer is dangling from `from_array`
// (since the length is immutable), and we can skip calling `free`.
if self.len() != 0 {
// SAFETY: We take ownership over the slice elements in
// `from_array`.
unsafe { ptr::drop_in_place(self.ptr.as_ptr()) };
// SAFETY: We take ownership over the pointer in `from_array`,
// and the pointer is valid if the length is non-zero.
unsafe { ffi::free(self.ptr.cast().as_ptr()) };
}
}
}
impl<T> Deref for MallocSlice<T> {
type Target = [T];
#[inline]
fn deref(&self) -> &[T] {
// SAFETY:
// - That the pointer is aligned, dereferenceable and initialized is
// ensured by the caller of `from_array` (which usually get it from
// some external API that will do this for you).
// - The lifetime is bound to the `MallocSlice`, which in turn ensures
// the pointer is valid until it is dropped.
unsafe { self.ptr.as_ref() }
}
}
impl<T: fmt::Debug> fmt::Debug for MallocSlice<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T> AsRef<[T]> for MallocSlice<T> {
#[inline]
fn as_ref(&self) -> &[T] {
self
}
}
// TODO: Change this to `MallocCStr` once we get rid of `malloc_buf` support.
#[repr(transparent)]
pub(crate) struct MallocStr {
ptr: NonNull<str>,
}
impl MallocStr {
// Currently has to have the same API as `malloc_buf::Malloc`
pub(crate) unsafe fn from_c_str(ptr: *mut c_char) -> Result<Self, Utf8Error> {
if ptr.is_null() {
panic!("tried to construct MallocStr from a NULL pointer");
}
// SAFETY: We just checked that the pointer is not NULL.
//
// Further validity of the pointer is ensured by the caller.
let cstr = unsafe { CStr::from_ptr(ptr) };
// Note that we construct this `NonNull` from an immutable reference
// (there is not yet a `CStr::from_mut_ptr`).
//
// This means that we're (probably) no longer allowed to mutate the
// value, if that is desired for `MallocStr` in the future, then we'll
// have to implement this method a bit differently.
let ptr = NonNull::from(cstr.to_str()?);
Ok(Self { ptr })
}
}
impl Drop for MallocStr {
#[inline]
fn drop(&mut self) {
// SAFETY: We take ownership in `from_c_str`.
unsafe { ffi::free(self.ptr.cast().as_ptr()) };
}
}
impl Deref for MallocStr {
type Target = str;
#[inline]
fn deref(&self) -> &str {
// SAFETY: Same as `MallocSlice::deref`
unsafe { self.ptr.as_ref() }
}
}
impl fmt::Debug for MallocStr {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl fmt::Display for MallocStr {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl AsRef<str> for MallocStr {
#[inline]
fn as_ref(&self) -> &str {
self
}
}

View File

@@ -0,0 +1,687 @@
use core::ptr::NonNull;
use crate::encode::{EncodeArguments, EncodeReturn, RefEncode};
use crate::mutability::IsAllowedMutable;
use crate::runtime::{AnyClass, AnyObject, Sel};
use crate::Message;
/// Wrap the given closure in `exception::catch` if the `catch-all` feature is
/// enabled.
///
/// This is a macro to help with monomorphization when the feature is
/// disabled, as well as improving the final stack trace (`#[track_caller]`
/// doesn't really work on closures).
#[cfg(not(feature = "catch-all"))]
macro_rules! conditional_try {
(|| $expr:expr) => {
$expr
};
}
#[cfg(feature = "catch-all")]
macro_rules! conditional_try {
(|| $expr:expr) => {{
let f = core::panic::AssertUnwindSafe(|| $expr);
match crate::exception::catch(f) {
Ok(r) => r,
Err(exception) => {
if let Some(exception) = exception {
panic!("uncaught {exception:?}")
} else {
panic!("uncaught exception nil")
}
}
}
}};
}
// More information on how objc_msgSend works:
// <https://web.archive.org/web/20200118080513/http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/>
// <https://www.mikeash.com/pyblog/objc_msgsends-new-prototype.html>
// <https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html>
#[cfg(all(target_vendor = "apple", not(feature = "gnustep-1-7")))]
mod msg_send_primitive {
#[allow(unused_imports)]
use core::mem;
#[allow(unused_imports)]
use crate::encode::Encoding;
use crate::encode::{EncodeArguments, EncodeReturn};
use crate::ffi;
use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
/// On the below architectures we can statically find the correct method to
/// call from the return type, by looking at its `EncodeReturn` impl.
#[allow(clippy::missing_safety_doc)]
unsafe trait MsgSendFn: EncodeReturn {
const MSG_SEND: Imp;
const MSG_SEND_SUPER: Imp;
}
#[cfg(target_arch = "aarch64")]
/// `objc_msgSend_stret` is not even available in arm64.
///
/// <https://twitter.com/gparker/status/378079715824660480>
unsafe impl<T: EncodeReturn> MsgSendFn for T {
const MSG_SEND: Imp = ffi::objc_msgSend;
const MSG_SEND_SUPER: Imp = ffi::objc_msgSendSuper;
}
#[cfg(target_arch = "arm")]
/// Double-word sized fundamental data types don't use stret, but any
/// composite type larger than 4 bytes does.
///
/// <https://web.archive.org/web/20191016000656/http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf>
/// <https://developer.arm.com/documentation/ihi0042/latest>
/// <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/CodeGen/Targets/ARM.cpp#L531>
unsafe impl<T: EncodeReturn> MsgSendFn for T {
const MSG_SEND: Imp = {
if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN
{
ffi::objc_msgSend
} else if mem::size_of::<T>() <= 4 {
ffi::objc_msgSend
} else {
ffi::objc_msgSend_stret
}
};
const MSG_SEND_SUPER: Imp = {
if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN
{
ffi::objc_msgSendSuper
} else if mem::size_of::<T>() <= 4 {
ffi::objc_msgSendSuper
} else {
ffi::objc_msgSendSuper_stret
}
};
}
#[cfg(target_arch = "x86")]
/// Structures 1 or 2 bytes in size are placed in EAX.
/// Structures 4 or 8 bytes in size are placed in: EAX and EDX.
/// Structures of other sizes are placed at the address supplied by the caller.
///
/// <https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html>
/// <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/CodeGen/Targets/X86.cpp#L472>
unsafe impl<T: EncodeReturn> MsgSendFn for T {
const MSG_SEND: Imp = {
// See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172
if let Encoding::Float | Encoding::Double | Encoding::LongDouble = T::ENCODING_RETURN {
ffi::objc_msgSend_fpret
} else if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
ffi::objc_msgSend
} else {
ffi::objc_msgSend_stret
}
};
const MSG_SEND_SUPER: Imp = {
if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
ffi::objc_msgSendSuper
} else {
ffi::objc_msgSendSuper_stret
}
};
}
#[cfg(target_arch = "x86_64")]
/// If the size of an object is larger than two eightbytes, it has class
/// MEMORY. If the type has class MEMORY, then the caller provides space for
/// the return value and passes the address of this storage.
///
/// <https://www.uclibc.org/docs/psABI-x86_64.pdf>
/// <https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/CodeGen/Targets/X86.cpp#L2532>
unsafe impl<T: EncodeReturn> MsgSendFn for T {
const MSG_SEND: Imp = {
// See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172
if let Encoding::LongDouble = T::ENCODING_RETURN {
ffi::objc_msgSend_fpret
} else if let Encoding::LongDoubleComplex = T::ENCODING_RETURN {
ffi::objc_msgSend_fp2ret
} else if mem::size_of::<T>() <= 16 {
ffi::objc_msgSend
} else {
ffi::objc_msgSend_stret
}
};
const MSG_SEND_SUPER: Imp = {
if mem::size_of::<T>() <= 16 {
ffi::objc_msgSendSuper
} else {
ffi::objc_msgSendSuper_stret
}
};
}
#[inline]
#[track_caller]
pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
receiver: *mut AnyObject,
sel: Sel,
args: A,
) -> R {
let msg_send_fn = R::MSG_SEND;
// Note: Modern Objective-C compilers have a workaround to ensure that
// messages to `nil` with a struct return produces `mem::zeroed()`,
// see:
// <https://www.sealiesoftware.com/blog/archive/2012/2/29/objc_explain_return_value_of_message_to_nil.html>
//
// We _could_ technically do something similar, but since we're
// disallowing messages to `nil` with `debug_assertions` enabled
// anyhow, and since Rust has a much stronger type-system that
// disallows NULL/nil in most cases, we won't bother supporting it.
unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
}
#[inline]
#[track_caller]
pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
receiver: *mut AnyObject,
superclass: &AnyClass,
sel: Sel,
args: A,
) -> R {
let superclass: *const AnyClass = superclass;
let mut sup = ffi::objc_super {
receiver: receiver.cast(),
super_class: superclass.cast(),
};
let receiver: *mut ffi::objc_super = &mut sup;
let receiver = receiver.cast();
let msg_send_fn = R::MSG_SEND_SUPER;
unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
}
}
#[cfg(feature = "gnustep-1-7")]
mod msg_send_primitive {
use core::mem;
use crate::encode::{EncodeArguments, EncodeReturn};
use crate::ffi;
use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
#[inline]
fn unwrap_msg_send_fn(msg_send_fn: Option<Imp>) -> Imp {
match msg_send_fn {
Some(msg_send_fn) => msg_send_fn,
None => {
// SAFETY: This will never be NULL, even if the selector is not
// found a callable function pointer will still be returned!
//
// `clang` doesn't insert a NULL check here either.
unsafe { core::hint::unreachable_unchecked() }
}
}
}
#[track_caller]
pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
receiver: *mut AnyObject,
sel: Sel,
args: A,
) -> R {
// If `receiver` is NULL, objc_msg_lookup will return a standard
// C-method taking two arguments, the receiver and the selector.
//
// Transmuting and calling such a function with multiple parameters is
// safe as long as the return value is a primitive (and e.g. not a big
// struct or array).
//
// However, when the return value is a floating point value, the float
// will end up as some undefined value, usually NaN, which is
// incompatible with Apple's platforms. As such, we insert this extra
// NULL check here.
if receiver.is_null() {
// SAFETY: Caller guarantees that messages to NULL-receivers only
// return pointers or primitive values, and a mem::zeroed pointer
// / primitive is just a NULL-pointer or a zeroed primitive.
return unsafe { mem::zeroed() };
}
let msg_send_fn = unsafe { ffi::objc_msg_lookup(receiver.cast(), sel.as_ptr()) };
let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
}
#[track_caller]
pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
receiver: *mut AnyObject,
superclass: &AnyClass,
sel: Sel,
args: A,
) -> R {
if receiver.is_null() {
// SAFETY: Same as in `send`.
return unsafe { mem::zeroed() };
}
let superclass: *const AnyClass = superclass;
let sup = ffi::objc_super {
receiver: receiver.cast(),
super_class: superclass.cast(),
};
let msg_send_fn = unsafe { ffi::objc_msg_lookup_super(&sup, sel.as_ptr()) };
let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
unsafe { A::__invoke(msg_send_fn, receiver, sel, args) }
}
}
#[cfg(all(not(target_vendor = "apple"), not(feature = "gnustep-1-7")))]
mod msg_send_primitive {
use crate::encode::{EncodeArguments, EncodeReturn};
use crate::runtime::{AnyClass, AnyObject, Sel};
#[track_caller]
pub(crate) unsafe fn send<A: EncodeArguments, R: EncodeReturn>(
_receiver: *mut AnyObject,
_sel: Sel,
_args: A,
) -> R {
unimplemented!("no runtime chosen")
}
#[track_caller]
pub(crate) unsafe fn send_super<A: EncodeArguments, R: EncodeReturn>(
_receiver: *mut AnyObject,
_superclass: &AnyClass,
_sel: Sel,
_args: A,
) -> R {
unimplemented!("no runtime chosen")
}
}
/// Help with monomorphizing in framework crates
#[cfg(debug_assertions)]
#[track_caller]
fn msg_send_check(
obj: Option<&AnyObject>,
sel: Sel,
args: &[crate::encode::Encoding],
ret: &crate::encode::Encoding,
) {
let cls = if let Some(obj) = obj {
obj.class()
} else {
panic_null(sel)
};
msg_send_check_class(cls, sel, args, ret);
}
#[cfg(debug_assertions)]
#[track_caller]
fn msg_send_check_class(
cls: &AnyClass,
sel: Sel,
args: &[crate::encode::Encoding],
ret: &crate::encode::Encoding,
) {
use crate::verify::{verify_method_signature, Inner, VerificationError};
let err = if let Some(method) = cls.instance_method(sel) {
if let Err(err) = verify_method_signature(method, args, ret) {
err
} else {
return;
}
} else {
VerificationError::from(Inner::MethodNotFound)
};
panic_verify(cls, sel, &err);
}
#[cfg(debug_assertions)]
#[track_caller]
fn panic_null(sel: Sel) -> ! {
panic!("messsaging {sel} to nil")
}
#[cfg(debug_assertions)]
#[track_caller]
fn panic_verify(cls: &AnyClass, sel: Sel, err: &crate::runtime::VerificationError) -> ! {
panic!(
"invalid message send to {}[{cls} {sel}]: {err}",
if cls.is_metaclass() { "+" } else { "-" },
)
}
mod private {
pub trait Sealed {}
}
/// Types that can directly be used as the receiver of Objective-C messages.
///
/// Examples include objects pointers, class pointers, and block pointers.
///
///
/// # Safety
///
/// This is a sealed trait, and should not need to be implemented. Open an
/// issue if you know a use-case where this restrition should be lifted!
pub unsafe trait MessageReceiver: private::Sealed + Sized {
#[doc(hidden)]
type __Inner: ?Sized + RefEncode;
#[doc(hidden)]
fn __as_raw_receiver(self) -> *mut AnyObject;
/// Sends a message to the receiver with the given selector and arguments.
///
/// This should be used instead of the [`performSelector:`] family of
/// methods, as this is both more performant and flexible than that.
///
/// The correct version of `objc_msgSend` will be chosen based on the
/// return type. For more information, see [the Messaging section in
/// Apple's Objective-C Runtime Programming Guide][guide-messaging].
///
/// If the selector is known at compile-time, it is recommended to use the
/// [`msg_send!`] macro rather than this method.
///
/// [`performSelector:`]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418867-performselector?language=objc
/// [guide-messaging]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html
///
///
/// # Safety
///
/// This shares the same safety requirements as [`msg_send!`].
///
/// The added invariant is that the selector must take the same number of
/// arguments as is given.
///
/// [`msg_send!`]: crate::msg_send
///
///
/// # Example
///
/// Call the `copy` method, but using a dynamic selector instead.
///
/// ```no_run
/// use objc2::rc::Retained;
/// use objc2::runtime::MessageReceiver;
/// use objc2::sel;
/// # use objc2::runtime::NSObject as MyObject;
///
/// let obj = MyObject::new();
/// // SAFETY: The `copy` method takes no arguments, and returns an object
/// let copy: *mut MyObject = unsafe { obj.send_message(sel!(copy), ()) };
/// // SAFETY: The `copy` method returns an object with +1 retain count
/// let copy = unsafe { Retained::from_raw(copy) }.unwrap();
/// ```
#[inline]
#[track_caller]
#[doc(alias = "performSelector")]
#[doc(alias = "performSelector:")]
#[doc(alias = "performSelector:withObject:")]
#[doc(alias = "performSelector:withObject:withObject:")]
unsafe fn send_message<A: EncodeArguments, R: EncodeReturn>(self, sel: Sel, args: A) -> R {
let receiver = self.__as_raw_receiver();
#[cfg(debug_assertions)]
{
// SAFETY: Caller ensures only valid or NULL pointers.
let obj = unsafe { receiver.as_ref() };
msg_send_check(obj, sel, A::ENCODINGS, &R::ENCODING_RETURN);
}
// SAFETY: Upheld by caller
//
// The @catch is safe since message sending primitives are guaranteed
// to do Objective-C compatible unwinding.
unsafe { conditional_try!(|| msg_send_primitive::send(receiver, sel, args)) }
}
/// Sends a message to a specific superclass with the given selector and
/// arguments.
///
/// The correct version of `objc_msgSend_super` will be chosen based on the
/// return type. For more information, see the section on "Sending
/// Messages" in Apple's [documentation][runtime].
///
/// If the selector is known at compile-time, it is recommended to use the
/// [`msg_send!(super(...), ...)`] macro rather than this method.
///
/// [runtime]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc
///
///
/// # Safety
///
/// This shares the same safety requirements as
/// [`msg_send!(super(...), ...)`].
///
/// The added invariant is that the selector must take the same number of
/// arguments as is given.
///
/// [`msg_send!(super(...), ...)`]: crate::msg_send
#[inline]
#[track_caller]
unsafe fn send_super_message<A: EncodeArguments, R: EncodeReturn>(
self,
superclass: &AnyClass,
sel: Sel,
args: A,
) -> R {
let receiver = self.__as_raw_receiver();
#[cfg(debug_assertions)]
{
if receiver.is_null() {
panic_null(sel);
}
msg_send_check_class(superclass, sel, A::ENCODINGS, &R::ENCODING_RETURN);
}
// SAFETY: Same as in `send_message`
unsafe {
conditional_try!(|| msg_send_primitive::send_super(receiver, superclass, sel, args))
}
}
}
// Note that we implement MessageReceiver for unsized types as well, this is
// to support `extern type`s in the future, not because we want to allow DSTs.
impl<T: ?Sized + Message> private::Sealed for *const T {}
unsafe impl<T: ?Sized + Message> MessageReceiver for *const T {
type __Inner = T;
#[inline]
fn __as_raw_receiver(self) -> *mut AnyObject {
(self as *mut T).cast()
}
}
impl<T: ?Sized + Message> private::Sealed for *mut T {}
unsafe impl<T: ?Sized + Message> MessageReceiver for *mut T {
type __Inner = T;
#[inline]
fn __as_raw_receiver(self) -> *mut AnyObject {
self.cast()
}
}
impl<T: ?Sized + Message> private::Sealed for NonNull<T> {}
unsafe impl<T: ?Sized + Message> MessageReceiver for NonNull<T> {
type __Inner = T;
#[inline]
fn __as_raw_receiver(self) -> *mut AnyObject {
self.as_ptr().cast()
}
}
impl<'a, T: ?Sized + Message> private::Sealed for &'a T {}
unsafe impl<'a, T: ?Sized + Message> MessageReceiver for &'a T {
type __Inner = T;
#[inline]
fn __as_raw_receiver(self) -> *mut AnyObject {
let ptr: *const T = self;
(ptr as *mut T).cast()
}
}
impl<'a, T: ?Sized + Message + IsAllowedMutable> private::Sealed for &'a mut T {}
unsafe impl<'a, T: ?Sized + Message + IsAllowedMutable> MessageReceiver for &'a mut T {
type __Inner = T;
#[inline]
fn __as_raw_receiver(self) -> *mut AnyObject {
let ptr: *mut T = self;
ptr.cast()
}
}
impl private::Sealed for *const AnyClass {}
unsafe impl MessageReceiver for *const AnyClass {
type __Inner = AnyClass;
#[inline]
fn __as_raw_receiver(self) -> *mut AnyObject {
(self as *mut AnyClass).cast()
}
}
impl<'a> private::Sealed for &'a AnyClass {}
unsafe impl<'a> MessageReceiver for &'a AnyClass {
type __Inner = AnyClass;
#[inline]
fn __as_raw_receiver(self) -> *mut AnyObject {
let ptr: *const AnyClass = self;
(ptr as *mut AnyClass).cast()
}
}
#[cfg(test)]
mod tests {
use core::ptr;
use super::*;
use crate::mutability;
use crate::rc::{Allocated, Retained};
use crate::runtime::NSObject;
use crate::test_utils;
use crate::{declare_class, msg_send, msg_send_id, ClassType, DeclaredClass};
declare_class!(
struct MutableObject;
unsafe impl ClassType for MutableObject {
type Super = NSObject;
type Mutability = mutability::Mutable;
const NAME: &'static str = "TestMutableObject";
}
impl DeclaredClass for MutableObject {}
);
#[allow(unused)]
fn test_different_receivers(mut obj: Retained<MutableObject>) {
unsafe {
let x = &mut obj;
let _: () = msg_send![x, mutable1];
// let _: () = msg_send![x, mutable2];
let _: () = msg_send![&mut *obj, mutable1];
let _: () = msg_send![&mut *obj, mutable2];
#[allow(clippy::needless_borrow)]
let obj: NonNull<MutableObject> = (&mut *obj).into();
let _: () = msg_send![obj, mutable1];
let _: () = msg_send![obj, mutable2];
let obj: *mut MutableObject = obj.as_ptr();
let _: () = msg_send![obj, mutable1];
let _: () = msg_send![obj, mutable2];
}
}
#[test]
fn test_send_message() {
let mut obj = test_utils::custom_object();
let result: u32 = unsafe {
let _: () = msg_send![&mut obj, setFoo: 4u32];
msg_send![&obj, foo]
};
assert_eq!(result, 4);
}
#[test]
fn test_send_message_stret() {
let obj = test_utils::custom_object();
let result: test_utils::CustomStruct = unsafe { msg_send![&obj, customStruct] };
let expected = test_utils::CustomStruct {
a: 1,
b: 2,
c: 3,
d: 4,
};
assert_eq!(result, expected);
}
#[test]
#[cfg_attr(debug_assertions, should_panic = "messsaging description to nil")]
fn test_send_message_nil() {
let nil: *mut NSObject = ::core::ptr::null_mut();
// This result should not be relied on
let result: Option<Retained<NSObject>> = unsafe { msg_send_id![nil, description] };
assert!(result.is_none());
// This result should not be relied on
let result: usize = unsafe { msg_send![nil, hash] };
assert_eq!(result, 0);
// This result should not be relied on
#[cfg(target_pointer_width = "16")]
let result: f32 = 0.0;
#[cfg(target_pointer_width = "32")]
let result: f32 = unsafe { msg_send![nil, floatValue] };
#[cfg(target_pointer_width = "64")]
let result: f64 = unsafe { msg_send![nil, doubleValue] };
assert_eq!(result, 0.0);
// This result should not be relied on
let result: Option<Retained<NSObject>> =
unsafe { msg_send_id![nil, multiple: 1u32, arguments: 2i8] };
assert!(result.is_none());
// This result should not be relied on
let obj = unsafe { Allocated::new(ptr::null_mut()) };
let result: Option<Retained<NSObject>> = unsafe { msg_send_id![obj, init] };
assert!(result.is_none());
}
#[test]
fn test_send_message_super() {
let mut obj = test_utils::custom_subclass_object();
let superclass = test_utils::custom_class();
unsafe {
let _: () = msg_send![&mut obj, setFoo: 4u32];
let foo: u32 = msg_send![super(&obj, superclass), foo];
assert_eq!(foo, 4);
// The subclass is overriden to return foo + 2
let foo: u32 = msg_send![&obj, foo];
assert_eq!(foo, 6);
}
}
#[test]
#[cfg_attr(
feature = "gnustep-1-7",
ignore = "GNUStep deadlocks here for some reason"
)]
fn test_send_message_class_super() {
let cls = test_utils::custom_subclass();
let superclass = test_utils::custom_class();
unsafe {
let foo: u32 = msg_send![super(cls, superclass.metaclass()), classFoo];
assert_eq!(foo, 7);
// The subclass is overriden to return + 2
let foo: u32 = msg_send![cls, classFoo];
assert_eq!(foo, 9);
}
}
}

View File

@@ -0,0 +1,213 @@
//! Utility for parsing an Objective-C method type encoding.
//!
//! TODO: Move this to `objc2-encode` when more stable.
use core::fmt;
use core::num::ParseIntError;
use core::str;
use std::error::Error;
use crate::encode::{Encoding, EncodingBox, ParseError};
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct MethodEncodingIter<'a> {
s: &'a str,
}
impl<'a> MethodEncodingIter<'a> {
pub(crate) fn new(s: &'a str) -> Self {
Self { s }
}
pub(crate) fn extract_return(
&mut self,
) -> Result<(EncodingBox, Option<isize>), EncodingParseError> {
// TODO: Verify stack layout
self.next().ok_or(EncodingParseError::MissingReturn)?
}
pub(crate) fn verify_receiver(&mut self) -> Result<(), EncodingParseError> {
// TODO: Verify stack layout
let (enc, _stack_layout) = self.next().ok_or(EncodingParseError::MissingReceiver)??;
if !Encoding::Object.equivalent_to_box(&enc) {
return Err(EncodingParseError::InvalidReceiver(enc));
}
Ok(())
}
pub(crate) fn verify_sel(&mut self) -> Result<(), EncodingParseError> {
let (enc, _stack_layout) = self.next().ok_or(EncodingParseError::MissingSel)??;
if !Encoding::Sel.equivalent_to_box(&enc) {
return Err(EncodingParseError::InvalidSel(enc));
}
Ok(())
}
fn extract_encoding(&mut self) -> Result<(EncodingBox, Option<isize>), EncodingParseError> {
// See also the following other approaches:
// objrs: https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/src/test.rs
// libobjc2: https://github.com/gnustep/libobjc2/blob/v2.1/encoding2.c
// objc4: https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-typeencoding.mm
let encoding = EncodingBox::from_start_of_str(&mut self.s)?;
let stack_layout = parse_stack_layout(&mut self.s)?;
Ok((encoding, stack_layout))
}
}
impl<'a> Iterator for MethodEncodingIter<'a> {
type Item = Result<(EncodingBox, Option<isize>), EncodingParseError>;
fn next(&mut self) -> Option<Self::Item> {
if self.s.is_empty() {
return None;
}
Some(self.extract_encoding())
}
}
// TODO: Is `isize` correct here?
fn parse_stack_layout(s: &mut &str) -> Result<Option<isize>, ParseIntError> {
let rest = s.trim_start_matches(|c: char| c.is_ascii_digit() || c == '-' || c == '+');
let stack_layout = &s[..s.len() - rest.len()];
*s = rest;
if stack_layout.is_empty() {
return Ok(None);
}
stack_layout.parse().map(Some)
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) enum EncodingParseError {
ParseError(ParseError),
InvalidStackLayoutInteger,
MissingReturn,
MissingReceiver,
MissingSel,
InvalidReceiver(EncodingBox),
InvalidSel(EncodingBox),
}
impl From<ParseError> for EncodingParseError {
fn from(e: ParseError) -> Self {
Self::ParseError(e)
}
}
impl From<ParseIntError> for EncodingParseError {
fn from(_: ParseIntError) -> Self {
Self::InvalidStackLayoutInteger
}
}
impl fmt::Display for EncodingParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !matches!(self, Self::ParseError(_)) {
write!(f, "failed parsing encoding: ")?;
}
match self {
Self::ParseError(e) => write!(f, "{e}")?,
Self::InvalidStackLayoutInteger => write!(f, "invalid integer for stack layout")?,
Self::MissingReturn => write!(f, "return type must be present")?,
Self::MissingReceiver => write!(f, "receiver type must be present")?,
Self::MissingSel => write!(f, "selector type must be present")?,
Self::InvalidReceiver(enc) => {
write!(f, "receiver encoding must be '@', but it was '{enc}'")?;
}
Self::InvalidSel(enc) => {
write!(f, "selector encoding must be '@', but it was '{enc}'")?;
}
}
write!(f, ". This is likely a bug, please report it!")
}
}
impl Error for EncodingParseError {}
#[cfg(test)]
mod tests {
use super::*;
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
fn assert_stack_layout(mut types: &str, expected: Option<isize>, rest: &str) {
let sl = parse_stack_layout(&mut types).unwrap();
assert_eq!(sl, expected);
assert_eq!(types, rest);
}
#[test]
fn stack_layout_extract() {
assert_stack_layout("", None, "");
assert_stack_layout("abc", None, "abc");
assert_stack_layout("abc12abc", None, "abc12abc");
assert_stack_layout("0", Some(0), "");
assert_stack_layout("1abc", Some(1), "abc");
assert_stack_layout("42def24", Some(42), "def24");
assert_stack_layout("1234567890xyz", Some(1234567890), "xyz");
assert_stack_layout("-1a", Some(-1), "a");
assert_stack_layout("-1a", Some(-1), "a");
// GNU runtime's register parameter hint??
assert_stack_layout("+1a", Some(1), "a");
}
fn assert_encoding_extract(s: &str, expected: &[(EncodingBox, Option<isize>)]) {
let actual: Vec<_> = MethodEncodingIter::new(s)
.collect::<Result<_, _>>()
.unwrap_or_else(|e| panic!("{}", e));
assert_eq!(&actual, expected);
}
#[test]
fn parse_bitfield() {
assert_encoding_extract(
"@48@0:8Ad16r^*24{bitfield=b64b1}32i48",
&[
(EncodingBox::Object, Some(48)),
(EncodingBox::Object, Some(0)),
(EncodingBox::Sel, Some(8)),
(EncodingBox::Atomic(Box::new(EncodingBox::Double)), Some(16)),
(
EncodingBox::Pointer(Box::new(EncodingBox::String)),
Some(24),
),
(
EncodingBox::Struct(
"bitfield".into(),
vec![
EncodingBox::BitField(64, None),
EncodingBox::BitField(1, None),
],
),
Some(32),
),
(EncodingBox::Int, Some(48)),
],
);
}
#[test]
fn parse_complex() {
assert_encoding_extract(
"jf16@0:8",
&[
(EncodingBox::FloatComplex, Some(16)),
(EncodingBox::Object, Some(0)),
(EncodingBox::Sel, Some(8)),
],
);
assert_encoding_extract(
"jf@:",
&[
(EncodingBox::FloatComplex, None),
(EncodingBox::Object, None),
(EncodingBox::Sel, None),
],
);
}
}

View File

@@ -0,0 +1,116 @@
use core::mem;
use crate::__macro_helpers::IdReturnValue;
use crate::encode::{EncodeArgument, EncodeArguments, EncodeReturn, RefEncode};
use crate::rc::Allocated;
use crate::runtime::{Imp, MessageReceiver, Sel};
use crate::Message;
mod private {
pub trait Sealed {}
}
/// Types that can be used as the implementation of an Objective-C method.
///
/// This is a sealed trait that is implemented for a lot of `extern "C"`
/// function pointer types.
//
// Note: `Sized` is intentionally added to make the trait not object safe.
pub trait MethodImplementation: private::Sealed + Sized {
/// The callee type of the method.
type Callee: ?Sized + RefEncode;
/// The argument types of the method.
type Arguments: EncodeArguments;
/// The return type of the method.
type Return: EncodeReturn;
#[doc(hidden)]
fn __imp(self) -> Imp;
}
macro_rules! method_impl_inner {
($(($unsafe:ident))? $abi:literal; $($t:ident),*) => {
impl<T, R, $($t),*> private::Sealed for $($unsafe)? extern $abi fn(T, Sel $(, $t)*) -> R
where
T: ?Sized + MessageReceiver,
R: EncodeReturn,
$($t: EncodeArgument,)*
{}
impl<T, R, $($t),*> MethodImplementation for $($unsafe)? extern $abi fn(T, Sel $(, $t)*) -> R
where
T: ?Sized + MessageReceiver,
R: EncodeReturn,
$($t: EncodeArgument,)*
{
type Callee = T::__Inner;
type Arguments = ($($t,)*);
type Return = R;
fn __imp(self) -> Imp {
// SAFETY: Transmuting to an `unsafe` function pointer
unsafe { mem::transmute(self) }
}
}
impl<T, $($t),*> private::Sealed for $($unsafe)? extern $abi fn(Allocated<T>, Sel $(, $t)*) -> IdReturnValue
where
T: ?Sized + Message,
$($t: EncodeArgument,)*
{}
#[doc(hidden)]
impl<T, $($t),*> MethodImplementation for $($unsafe)? extern $abi fn(Allocated<T>, Sel $(, $t)*) -> IdReturnValue
where
T: ?Sized + Message,
$($t: EncodeArgument,)*
{
type Callee = T;
type Arguments = ($($t,)*);
type Return = IdReturnValue;
fn __imp(self) -> Imp {
// SAFETY: `Allocated<T>` is the same as `NonNull<T>`, except
// with the assumption of a +1 calling convention.
//
// The calling convention is ensured to be upheld by having
// `IdReturnValue` in the type, since that type is private
// and hence only internal macros like `#[method_id]` will be
// able to produce it (and that, in turn, only allows it if
// the selector is `init` as checked by `MessageRecieveId`).
unsafe { mem::transmute(self) }
}
}
};
}
macro_rules! method_impl {
($($t:ident),*) => {
method_impl_inner!((unsafe) "C"; $($t),*);
method_impl_inner!("C"; $($t),*);
#[cfg(feature = "unstable-c-unwind")]
method_impl_inner!((unsafe) "C-unwind"; $($t),*);
#[cfg(feature = "unstable-c-unwind")]
method_impl_inner!("C-unwind"; $($t),*);
};
}
method_impl!();
method_impl!(A);
method_impl!(A, B);
method_impl!(A, B, C);
method_impl!(A, B, C, D);
method_impl!(A, B, C, D, E);
method_impl!(A, B, C, D, E, F);
method_impl!(A, B, C, D, E, F, G);
method_impl!(A, B, C, D, E, F, G, H);
method_impl!(A, B, C, D, E, F, G, H, I);
method_impl!(A, B, C, D, E, F, G, H, I, J);
method_impl!(A, B, C, D, E, F, G, H, I, J, K);
method_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
method_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M);
method_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
method_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
method_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);

1684
vendor/objc2/src/runtime/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

609
vendor/objc2/src/runtime/nsobject.rs vendored Normal file
View File

@@ -0,0 +1,609 @@
use core::fmt;
use core::hash;
use crate::ffi::NSUInteger;
use crate::mutability::Root;
use crate::rc::{Allocated, DefaultRetained, Retained};
use crate::runtime::{AnyClass, AnyObject, AnyProtocol, ImplementedBy, ProtocolObject, Sel};
use crate::{extern_methods, msg_send, msg_send_id, Message};
use crate::{ClassType, ProtocolType};
/// The root class of most Objective-C class hierarchies.
///
/// This represents the [`NSObject` class][cls]. The name "NSObject" also
/// refers to a protocol, see [`NSObjectProtocol`] for that.
///
/// Since this class is only available with the `Foundation` framework,
/// `objc2` links to it for you.
///
/// This is exported under `objc2_foundation::NSObject`, you probably
/// want to use that path instead.
///
/// [cls]: https://developer.apple.com/documentation/objectivec/nsobject?language=objc
#[repr(C)]
pub struct NSObject {
__inner: AnyObject,
}
crate::__extern_class_impl_traits! {
unsafe impl () for NSObject {
INHERITS = [AnyObject];
fn as_super(&self) {
&self.__inner
}
fn as_super_mut(&mut self) {
&mut self.__inner
}
}
}
unsafe impl ClassType for NSObject {
type Super = AnyObject;
type Mutability = Root;
const NAME: &'static str = "NSObject";
#[inline]
fn class() -> &'static AnyClass {
crate::__class_inner!("NSObject", "NSObject")
}
#[inline]
fn as_super(&self) -> &Self::Super {
&self.__inner
}
#[inline]
fn as_super_mut(&mut self) -> &mut Self::Super {
&mut self.__inner
}
}
/// The methods that are fundamental to most Objective-C objects.
///
/// This represents the [`NSObject` protocol][proto].
///
/// You should rarely need to use this for anything other than as a trait
/// bound in [`extern_protocol!`], to allow your protocol to implement `Debug`
/// `Hash`, `PartialEq` and `Eq`.
///
/// This trait is exported under `objc2_foundation::NSObjectProtocol`, you
/// probably want to use that path instead.
///
/// [proto]: https://developer.apple.com/documentation/objectivec/1418956-nsobject?language=objc
/// [`extern_protocol!`]: crate::extern_protocol!
///
///
/// # Safety
///
/// Like with [other protocols](ProtocolType), the type must represent a class
/// that implements the `NSObject` protocol.
//
// Note: Most of the methods on this must remain `unsafe` to override,
// including `isEqual` and `hash`, since hashing collections like
// `NSDictionary` and `NSSet` rely on it being stable.
#[allow(non_snake_case)] // Follow the naming scheme in framework crates
pub unsafe trait NSObjectProtocol {
/// Check whether the object is equal to an arbitrary other object.
///
/// Most objects that implement `NSObjectProtocol` also implements the
/// [`PartialEq`] trait. If the objects you are comparing are of the same
/// type, you likely want to use that instead.
///
/// See [Apple's documentation][apple-doc] for details.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418795-isequal?language=objc
#[doc(alias = "isEqual:")]
fn isEqual(&self, other: &AnyObject) -> bool
where
Self: Sized + Message,
{
unsafe { msg_send![self, isEqual: other] }
}
/// An integer that can be used as a table address in a hash table
/// structure.
///
/// Most objects that implement `NSObjectProtocol` also implements the
/// [`Hash`][std::hash::Hash] trait, you likely want to use that instead.
///
/// See [Apple's documentation][apple-doc] for details.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418859-hash?language=objc
fn hash(&self) -> NSUInteger
where
Self: Sized + Message,
{
unsafe { msg_send![self, hash] }
}
/// Check if the object is an instance of the class, or one of its
/// subclasses.
///
/// See [Apple's documentation][apple-doc] for more details on what you
/// may (and what you may not) do with this information.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass?language=objc
#[doc(alias = "isKindOfClass:")]
fn isKindOfClass(&self, cls: &AnyClass) -> bool
where
Self: Sized + Message,
{
unsafe { msg_send![self, isKindOfClass: cls] }
}
/// Check if the object is an instance of the class type, or one of its
/// subclasses.
///
/// See [`isKindOfClass`][Self::isKindOfClass] for details.
#[doc(alias = "isKindOfClass")]
#[doc(alias = "isKindOfClass:")]
// TODO: Consider deprecating this
fn is_kind_of<T: ClassType>(&self) -> bool
where
Self: Sized + Message,
{
self.isKindOfClass(T::class())
}
// Note: We don't provide a method to convert `NSObject` to `T` based on
// `is_kind_of`, since that is not possible to do in general!
//
// For example, something may have a return type of `NSString`, while
// behind the scenes they really return `NSMutableString` and expect it to
// not be modified.
/// Check if the object is an instance of a specific class, without
/// checking subclasses.
///
/// Note that this is rarely what you want, the specific class of an
/// object is considered a private implementation detail. Use
/// [`isKindOfClass`][Self::isKindOfClass] instead to check whether an
/// object is an instance of a given class.
///
/// See [Apple's documentation][apple-doc] for more details.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418766-ismemberofclass?language=objc
#[doc(alias = "isMemberOfClass:")]
fn isMemberOfClass(&self, cls: &AnyClass) -> bool
where
Self: Sized + Message,
{
unsafe { msg_send![self, isMemberOfClass: cls] }
}
/// Check whether the object implements or inherits a method with the
/// given selector.
///
/// See [Apple's documentation][apple-doc] for more details.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector?language=objc
///
///
/// # Example
///
/// Check whether `NSApplication` has the [`effectiveAppearance`] method
/// before calling it, to support systems older than macOS 10.14 where the
/// method was added.
///
/// ```
/// # #[cfg(available_in_frameworks)]
/// use objc2_app_kit::{NSApplication, NSAppearance, NSAppearanceNameAqua};
/// use objc2::runtime::NSObjectProtocol;
/// use objc2::sel;
///
/// # let obj = objc2::runtime::NSObject::new();
/// # assert!(!obj.respondsToSelector(sel!(effectiveAppearance)));
/// #
/// # #[cfg(available_in_frameworks)] {
/// let appearance = if obj.respondsToSelector(sel!(effectiveAppearance)) {
/// NSApplication::sharedApplication(mtm).effectiveAppearance()
/// } else {
/// unsafe { NSAppearance::appearanceNamed(NSAppearanceNameAqua).unwrap() }
/// };
/// # }
/// ```
///
/// [`effectiveAppearance`]: https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc
#[doc(alias = "respondsToSelector:")]
fn respondsToSelector(&self, aSelector: Sel) -> bool
where
Self: Sized + Message,
{
unsafe { msg_send![self, respondsToSelector: aSelector] }
}
/// Check whether the object conforms to a given protocol.
///
/// See [Apple's documentation][apple-doc] for details.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418893-conformstoprotocol?language=objc
#[doc(alias = "conformsToProtocol:")]
fn conformsToProtocol(&self, aProtocol: &AnyProtocol) -> bool
where
Self: Sized + Message,
{
unsafe { msg_send![self, conformsToProtocol: aProtocol] }
}
/// A textual representation of the object.
///
/// The returned class is `NSString`, but since that is defined in
/// `objc2-foundation`, and `NSObjectProtocol` is defined in `objc2`, the
/// declared return type is unfortunately restricted to be [`NSObject`].
/// It is always safe to cast the return value of this to `NSString`.
///
/// You might want to use the [`Debug`][fmt::Debug] impl of the object
/// instead, or if the object implements [`Display`][fmt::Display], the
/// [`to_string`][std::string::ToString::to_string] method.
///
///
/// # Example
///
/// ```
/// use objc2::rc::Retained;
/// # use objc2::runtime::{NSObjectProtocol, NSObject, NSObject as NSString};
/// # #[cfg(available_in_foundation)]
/// use objc2_foundation::{NSObject, NSObjectProtocol, NSString};
///
/// # let obj = NSObject::new();
/// // SAFETY: Descriptions are always `NSString`.
/// let desc: Retained<NSString> = unsafe { Retained::cast(obj.description()) };
/// println!("{desc:?}");
/// ```
fn description(&self) -> Retained<NSObject>
where
Self: Sized + Message,
{
unsafe { msg_send_id![self, description] }
}
/// A textual representation of the object to use when debugging.
///
/// Like with [`description`][Self::description], the return type of this
/// is always `NSString`.
///
/// LLVM's po command uses this property to create a textual
/// representation of the object. The default implemention returns the
/// same value as `description`. Override either to provide custom object
/// descriptions.
// optional, introduced in macOS 10.8
fn debugDescription(&self) -> Retained<NSObject>
where
Self: Sized + Message,
{
unsafe { msg_send_id![self, debugDescription] }
}
/// Check whether the receiver is a subclass of the `NSProxy` root class
/// instead of the usual [`NSObject`].
///
/// See [Apple's documentation][apple-doc] for details.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418528-isproxy?language=objc
///
///
/// # Example
///
/// ```
/// use objc2::runtime::{NSObject, NSObjectProtocol};
///
/// let obj = NSObject::new();
/// assert!(!obj.isProxy());
/// ```
fn isProxy(&self) -> bool
where
Self: Sized + Message,
{
unsafe { msg_send![self, isProxy] }
}
/// The reference count of the object.
///
/// This can rarely be useful when debugging memory management issues,
/// though beware that in most real-world scenarios, your object may be
/// retained by several autorelease pools, especially when debug
/// assertions are enabled, so this value may not represent what you'd
/// expect.
///
///
/// # Example
///
/// ```
/// use objc2::ClassType;
/// use objc2::runtime::{NSObject, NSObjectProtocol};
///
/// let obj = NSObject::new();
/// assert_eq!(obj.retainCount(), 1);
/// let obj2 = obj.clone();
/// assert_eq!(obj.retainCount(), 2);
/// drop(obj2);
/// assert_eq!(obj.retainCount(), 1);
/// ```
fn retainCount(&self) -> NSUInteger
where
Self: Sized + Message,
{
unsafe { msg_send![self, retainCount] }
}
}
crate::__inner_extern_protocol!(
()
(NSObjectProtocol)
(dyn NSObjectProtocol)
("NSObject")
);
// SAFETY: Anything that implements `NSObjectProtocol` and is `Send` is valid
// to convert to `ProtocolObject<dyn NSObjectProtocol + Send>`.
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send
where
T: ?Sized + Message + NSObjectProtocol + Send,
{
const __INNER: () = ();
}
// SAFETY: Anything that implements `NSObjectProtocol` and is `Sync` is valid
// to convert to `ProtocolObject<dyn NSObjectProtocol + Sync>`.
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Sync
where
T: ?Sized + Message + NSObjectProtocol + Sync,
{
const __INNER: () = ();
}
// SAFETY: Anything that implements `NSObjectProtocol` and is `Send + Sync` is
// valid to convert to `ProtocolObject<dyn NSObjectProtocol + Send + Sync>`.
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send + Sync
where
T: ?Sized + Message + NSObjectProtocol + Send + Sync,
{
const __INNER: () = ();
}
unsafe impl NSObjectProtocol for NSObject {}
extern_methods!(
#[allow(non_snake_case)] // Follow the naming scheme in framework crates
unsafe impl NSObject {
/// Create a new empty `NSObject`.
///
/// This method is a shorthand for calling [`alloc`][ClassType::alloc]
/// and then [`init`][Self::init].
#[method_id(new)]
pub fn new() -> Retained<Self>;
/// Initialize an already allocated object.
///
/// See [Apple's documentation][apple-doc] for details.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418641-init?language=objc
///
///
/// # Example
///
/// ```
/// use objc2::runtime::NSObject;
/// use objc2::ClassType;
///
/// let obj = NSObject::init(NSObject::alloc());
/// ```
#[method_id(init)]
pub fn init(this: Allocated<Self>) -> Retained<Self>;
#[method(doesNotRecognizeSelector:)]
fn doesNotRecognizeSelector_inner(&self, sel: Sel);
/// Handle messages the object doesnt recognize.
///
/// See [Apple's documentation][apple-doc] for details.
///
/// [apple-doc]: https://developer.apple.com/documentation/objectivec/nsobject/1418637-doesnotrecognizeselector?language=objc
pub fn doesNotRecognizeSelector(&self, sel: Sel) -> ! {
self.doesNotRecognizeSelector_inner(sel);
unreachable!("doesNotRecognizeSelector: should not return")
}
// TODO: `methodForSelector:`, but deprecated, showing how you should do without?
}
);
/// Objective-C equality has approximately the same semantics as Rust
/// equality (although less aptly specified).
///
/// At the very least, equality is _expected_ to be symmetric and
/// transitive, and that's about the best we can do.
///
/// See also <https://nshipster.com/equality/>
impl PartialEq for NSObject {
#[inline]
#[doc(alias = "isEqual:")]
fn eq(&self, other: &Self) -> bool {
self.isEqual(other)
}
}
/// Most types' equality is reflexive.
impl Eq for NSObject {}
/// Hashing in Objective-C has the exact same requirement as in Rust:
///
/// > If two objects are equal (as determined by the isEqual: method),
/// > they must have the same hash value.
///
/// See <https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418859-hash>
impl hash::Hash for NSObject {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
<Self as NSObjectProtocol>::hash(self).hash(state);
}
}
impl fmt::Debug for NSObject {
#[inline]
#[doc(alias = "description")]
#[doc(alias = "debugDescription")]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let obj: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(self);
obj.fmt(f)
}
}
impl DefaultRetained for NSObject {
#[inline]
fn default_id() -> Retained<Self> {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
use crate::extern_class;
use crate::mutability::Mutable;
use crate::rc::RcTestObject;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
struct NSObjectMutable;
unsafe impl ClassType for NSObjectMutable {
type Super = NSObject;
type Mutability = Mutable;
const NAME: &'static str = "NSObject";
}
);
impl NSObjectMutable {
fn new() -> Retained<Self> {
unsafe { Retained::cast(NSObject::new()) }
}
}
#[test]
fn test_deref() {
let obj: Retained<NSObject> = NSObject::new();
let _: &NSObject = &obj;
let _: &AnyObject = &obj;
}
#[test]
fn test_deref_mut() {
let mut obj: Retained<NSObjectMutable> = NSObjectMutable::new();
let _: &NSObjectMutable = &obj;
let _: &mut NSObjectMutable = &mut obj;
let _: &NSObject = &obj;
let _: &mut NSObject = &mut obj;
let _: &AnyObject = &obj;
let _: &mut AnyObject = &mut obj;
}
#[test]
fn test_as_ref_borrow() {
use core::borrow::{Borrow, BorrowMut};
fn impls_as_ref<T: AsRef<U> + Borrow<U> + ?Sized, U: ?Sized>(_: &T) {}
fn impls_as_mut<T: AsMut<U> + BorrowMut<U> + ?Sized, U: ?Sized>(_: &mut T) {}
let mut obj = NSObjectMutable::new();
impls_as_ref::<Retained<NSObjectMutable>, NSObjectMutable>(&obj);
impls_as_mut::<Retained<NSObjectMutable>, NSObjectMutable>(&mut obj);
impls_as_ref::<NSObjectMutable, NSObjectMutable>(&obj);
impls_as_mut::<NSObjectMutable, NSObjectMutable>(&mut obj);
impls_as_ref::<NSObject, NSObject>(&obj);
impls_as_mut::<NSObject, NSObject>(&mut obj);
impls_as_ref::<NSObject, AnyObject>(&obj);
impls_as_mut::<NSObject, AnyObject>(&mut obj);
let obj = NSObject::new();
impls_as_ref::<Retained<NSObject>, NSObject>(&obj);
impls_as_ref::<NSObject, NSObject>(&obj);
impls_as_ref::<NSObject, AnyObject>(&obj);
}
#[test]
fn test_equality() {
let obj1 = NSObject::new();
assert_eq!(obj1, obj1);
let obj2 = NSObject::new();
assert_ne!(obj1, obj2);
}
#[test]
fn test_hash() {
use core::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
let obj1 = NSObject::new();
let mut hashstate1 = DefaultHasher::new();
let mut hashstate2 = DefaultHasher::new();
obj1.hash(&mut hashstate1);
obj1.hash(&mut hashstate2);
assert_eq!(hashstate1.finish(), hashstate2.finish());
let obj2 = NSObject::new();
let mut hashstate2 = DefaultHasher::new();
obj2.hash(&mut hashstate2);
assert_ne!(hashstate1.finish(), hashstate2.finish());
}
#[test]
fn test_debug() {
let obj = NSObject::new();
let expected = format!("<NSObject: {:p}>", &*obj);
assert_eq!(format!("{obj:?}"), expected);
}
#[test]
fn test_is_kind_of() {
let obj = NSObject::new();
assert!(obj.is_kind_of::<NSObject>());
assert!(!obj.is_kind_of::<RcTestObject>());
let obj = RcTestObject::new();
assert!(obj.is_kind_of::<NSObject>());
assert!(obj.is_kind_of::<RcTestObject>());
}
#[test]
fn test_retain_same() {
let obj1 = NSObject::new();
let ptr1 = Retained::as_ptr(&obj1);
let obj2 = obj1.clone();
let ptr2 = Retained::as_ptr(&obj2);
assert_eq!(ptr1, ptr2);
}
#[test]
fn conforms_to_nsobjectprotocol() {
let protocol = <dyn NSObjectProtocol>::protocol().unwrap();
assert!(NSObject::class().conforms_to(protocol));
}
// Ensure that importing `NSObjectProtocol::hash` does not cause conflicts
// when using `Hash::hash` on normal types.
mod hash_does_not_overlap_with_normal_hash_method {
#[allow(unused_imports)]
use crate::runtime::NSObjectProtocol;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
#[test]
fn inner() {
let integer = 5;
let mut hasher = DefaultHasher::new();
integer.hash(&mut hasher);
}
}
}

81
vendor/objc2/src/runtime/nsproxy.rs vendored Normal file
View File

@@ -0,0 +1,81 @@
use core::fmt;
use core::hash;
use crate::mutability::Root;
use crate::runtime::{AnyClass, AnyObject, NSObjectProtocol, ProtocolObject};
use crate::ClassType;
/// An abstract superclass defining an API for objects that act as
/// stand-ins for other objects or for objects that dont exist yet.
///
/// See [Apple's documentation][apple-doc] for more information.
///
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsproxy?language=objc
#[repr(C)]
pub struct NSProxy {
__inner: AnyObject,
}
crate::__extern_class_impl_traits! {
unsafe impl () for NSProxy {
INHERITS = [AnyObject];
fn as_super(&self) {
&self.__inner
}
fn as_super_mut(&mut self) {
&mut self.__inner
}
}
}
unsafe impl ClassType for NSProxy {
type Super = AnyObject;
type Mutability = Root;
const NAME: &'static str = "NSProxy";
#[inline]
fn class() -> &'static AnyClass {
crate::__class_inner!("NSProxy", "NSProxy")
}
#[inline]
fn as_super(&self) -> &Self::Super {
&self.__inner
}
#[inline]
fn as_super_mut(&mut self) -> &mut Self::Super {
&mut self.__inner
}
}
unsafe impl NSObjectProtocol for NSProxy {}
impl PartialEq for NSProxy {
#[inline]
#[doc(alias = "isEqual:")]
fn eq(&self, other: &Self) -> bool {
self.isEqual(other)
}
}
impl Eq for NSProxy {}
impl hash::Hash for NSProxy {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
<NSProxy as NSObjectProtocol>::hash(self).hash(state);
}
}
impl fmt::Debug for NSProxy {
#[inline]
#[doc(alias = "description")]
#[doc(alias = "debugDescription")]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let obj: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(self);
obj.fmt(f)
}
}

96
vendor/objc2/src/runtime/nszone.rs vendored Normal file
View File

@@ -0,0 +1,96 @@
use core::fmt;
use core::panic::{RefUnwindSafe, UnwindSafe};
#[cfg(feature = "gnustep-1-7")]
use crate::encode::Encode;
use crate::encode::{Encoding, RefEncode};
use crate::ffi;
/// A type used to identify and manage memory zones.
///
/// Zones are ignored on all newer platforms, you should very rarely need to
/// use this, but may be useful if you need to implement `copyWithZone:` or
/// `allocWithZone:`.
///
/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nszone?language=objc).
#[repr(C)]
pub struct NSZone {
// Use `objc_object` to mark the types as !Send, !Sync and UnsafeCell.
//
// This works since `objc_object` is a ZST
_inner: ffi::objc_object,
}
impl fmt::Debug for NSZone {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<NSZone {self:p}>")
}
}
// Note: We don't know anything about the internals of `NSZone`, so best not
// to make it `Send` and `Sync` for now.
impl UnwindSafe for NSZone {}
impl RefUnwindSafe for NSZone {}
unsafe impl RefEncode for NSZone {
#[cfg(not(feature = "gnustep-1-7"))]
const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("_NSZone", &[]));
#[cfg(feature = "gnustep-1-7")]
const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct(
"_NSZone",
&[
// Functions
Encoding::Pointer(&Encoding::Unknown),
Encoding::Pointer(&Encoding::Unknown),
Encoding::Pointer(&Encoding::Unknown),
Encoding::Pointer(&Encoding::Unknown),
Encoding::Pointer(&Encoding::Unknown),
Encoding::Pointer(&Encoding::Unknown),
// Stats
Encoding::Pointer(&Encoding::Unknown),
// Zone granularity
usize::ENCODING,
// Name of zone
Encoding::Object,
// Next zone - note that the contents of this doesn't matter,
// since this is nested far enough that the encoding string ends
// up ignoring it.
Encoding::Pointer(&Encoding::Struct("_NSZone", &[])),
],
));
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use core::ptr;
use super::*;
use crate::msg_send_id;
use crate::rc::Allocated;
use crate::runtime::NSObject;
use crate::ClassType;
#[test]
fn alloc_with_zone() {
let zone: *const NSZone = ptr::null();
let _obj: Allocated<NSObject> =
unsafe { msg_send_id![NSObject::class(), allocWithZone: zone] };
}
#[test]
fn verify_encoding() {
let expected = if cfg!(all(feature = "gnustep-1-7", target_pointer_width = "64")) {
"^{_NSZone=^?^?^?^?^?^?^?Q@^{_NSZone}}"
} else if cfg!(all(
feature = "gnustep-1-7",
not(target_pointer_width = "64")
)) {
"^{_NSZone=^?^?^?^?^?^?^?I@^{_NSZone}}"
} else {
"^{_NSZone=}"
};
assert_eq!(NSZone::ENCODING_REF.to_string(), expected);
}
}

View File

@@ -0,0 +1,416 @@
use core::fmt;
use core::hash;
use core::marker::PhantomData;
use core::ptr::NonNull;
use crate::encode::{Encoding, RefEncode};
use crate::rc::{autoreleasepool_leaking, Retained};
use crate::runtime::__nsstring::nsstring_to_str;
use crate::runtime::{AnyObject, NSObjectProtocol};
use crate::Message;
/// An internal helper trait for [`ProtocolObject`].
///
///
/// # Safety
///
/// This is meant to be a sealed trait, and should not be implemented outside
/// of the [`extern_protocol!`] macro.
///
/// [`extern_protocol!`]: crate::extern_protocol
pub unsafe trait ImplementedBy<T: ?Sized + Message> {
#[doc(hidden)]
const __INNER: ();
}
/// An object representing any object that implements a specified protocol.
///
/// Objective-C has [a feature][protocol-type-checking] where you can write
/// `id<MyProtocol>`, and then work with the protocol as-if it was an object;
/// this is very similar to `dyn` traits in Rust!
///
/// If we could customize how `dyn Trait` works, then this struct would not
/// have been necessary; however, `dyn Trait` is a wide pointer with overhead,
/// which this struct helps avoid.
///
/// If the trait `T` inherits [`NSObjectProtocol`], this will implement common
/// traits like `Debug`, `PartialEq`, `Eq` and `Hash`.
///
/// [protocol-type-checking]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html#//apple_ref/doc/uid/TP30001163-CH15-TPXREF151
///
///
/// # Example
///
/// Convert an object `MyObject` that implements the a protocol `MyProtocol`
/// into a [`ProtocolObject`] for working with the protocol in a type-erased
/// way.
///
/// ```
/// use objc2::runtime::ProtocolObject;
/// use objc2::rc::Retained;
/// # use objc2::runtime::NSObject as MyObject;
/// # use objc2::runtime::NSObjectProtocol as MyProtocol;
///
/// let obj: Retained<MyObject> = MyObject::new();
/// let proto: &ProtocolObject<dyn MyProtocol> = ProtocolObject::from_ref(&*obj);
/// let proto: Retained<ProtocolObject<dyn MyProtocol>> = ProtocolObject::from_retained(obj);
/// ```
#[doc(alias = "id")]
#[repr(C)]
pub struct ProtocolObject<P: ?Sized> {
inner: AnyObject,
p: PhantomData<P>,
}
// SAFETY: `Send` if the underlying trait promises `Send`.
//
// E.g. `ProtocolObject<dyn NSObjectProtocol + Send>` is naturally `Send`.
unsafe impl<P: ?Sized + Send> Send for ProtocolObject<P> {}
// SAFETY: `Sync` if the underlying trait promises `Sync`.
//
// E.g. `ProtocolObject<dyn NSObjectProtocol + Sync>` is naturally `Sync`.
unsafe impl<P: ?Sized + Sync> Sync for ProtocolObject<P> {}
// SAFETY: The type is `#[repr(C)]` and `AnyObject` internally
unsafe impl<P: ?Sized> RefEncode for ProtocolObject<P> {
const ENCODING_REF: Encoding = Encoding::Object;
}
// SAFETY: The type is `AnyObject` internally, and is mean to be messaged
// as-if it's an object.
unsafe impl<P: ?Sized> Message for ProtocolObject<P> {}
impl<P: ?Sized> ProtocolObject<P> {
/// Get an immutable type-erased reference from a type implementing a
/// protocol.
#[inline]
pub fn from_ref<T: ?Sized + Message>(obj: &T) -> &Self
where
P: ImplementedBy<T>,
{
let ptr: NonNull<T> = NonNull::from(obj);
let ptr: NonNull<Self> = ptr.cast();
// SAFETY: Implementer ensures that the object conforms to the
// protocol; so converting the reference here is safe.
unsafe { ptr.as_ref() }
}
/// Get a mutable type-erased reference from a type implementing a
/// protocol.
#[inline]
pub fn from_mut<T: ?Sized + Message>(obj: &mut T) -> &mut Self
where
P: ImplementedBy<T>,
{
let ptr: NonNull<T> = NonNull::from(obj);
let mut ptr: NonNull<Self> = ptr.cast();
// SAFETY: Same as `as_protocol`.
//
// Since the reference came from a mutable reference to start with,
// returning a mutable reference here is safe (the lifetime of the
// returned reference is bound to the input).
unsafe { ptr.as_mut() }
}
/// Get a type-erased object from a type implementing a protocol.
///
/// Soft-deprecated alias of [`ProtocolObject::from_retained`].
#[inline]
pub fn from_id<T>(obj: Retained<T>) -> Retained<Self>
where
P: ImplementedBy<T> + 'static,
T: Message + 'static,
{
Self::from_retained(obj)
}
/// Get a type-erased object from a type implementing a protocol.
#[inline]
pub fn from_retained<T>(obj: Retained<T>) -> Retained<Self>
where
P: ImplementedBy<T> + 'static,
T: Message + 'static,
{
// SAFETY:
// - The type can be represented as the casted-to type.
// - Both types are `'static` (this could maybe be relaxed a bit, but
// let's be on the safe side)!
unsafe { Retained::cast::<Self>(obj) }
}
}
impl<P: ?Sized + NSObjectProtocol> PartialEq for ProtocolObject<P> {
#[inline]
#[doc(alias = "isEqual:")]
fn eq(&self, other: &Self) -> bool {
self.isEqual(&other.inner)
}
}
impl<P: ?Sized + NSObjectProtocol> Eq for ProtocolObject<P> {}
impl<P: ?Sized + NSObjectProtocol> hash::Hash for ProtocolObject<P> {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
<Self as NSObjectProtocol>::hash(self).hash(state);
}
}
impl<P: ?Sized + NSObjectProtocol> fmt::Debug for ProtocolObject<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let description = self.description();
// We use a leaking autorelease pool since often the string
// will be UTF-8, and in that case the pool will be
// irrelevant. Also, it allows us to pass the formatter into
// the pool (since it may contain a pool internally that it
// assumes is current when writing).
autoreleasepool_leaking(|pool| {
// SAFETY: `description` selector is guaranteed to always
// return an instance of `NSString`.
let s = unsafe { nsstring_to_str(&description, pool) };
fmt::Display::fmt(s, f)
})
}
}
impl<P: ?Sized, T> AsRef<ProtocolObject<T>> for ProtocolObject<P>
where
T: ?Sized + ImplementedBy<ProtocolObject<P>>,
{
#[inline]
fn as_ref(&self) -> &ProtocolObject<T> {
ProtocolObject::from_ref(self)
}
}
impl<P: ?Sized, T> AsMut<ProtocolObject<T>> for ProtocolObject<P>
where
T: ?Sized + ImplementedBy<ProtocolObject<P>>,
{
#[inline]
fn as_mut(&mut self) -> &mut ProtocolObject<T> {
ProtocolObject::from_mut(self)
}
}
// TODO: Maybe implement Borrow + BorrowMut?
#[cfg(test)]
#[allow(clippy::missing_safety_doc)]
#[allow(dead_code)]
mod tests {
use alloc::format;
use core::mem::ManuallyDrop;
use static_assertions::{assert_impl_all, assert_not_impl_any};
use super::*;
use crate::mutability::Mutable;
use crate::runtime::NSObject;
use crate::{
declare_class, extern_methods, extern_protocol, ClassType, DeclaredClass, ProtocolType,
};
extern_protocol!(
unsafe trait Foo {
#[method(foo)]
fn foo_class();
#[method(foo)]
fn foo_instance(&self);
}
unsafe impl ProtocolType for dyn Foo {}
);
extern_protocol!(
unsafe trait Bar: NSObjectProtocol {
#[method(bar)]
fn bar_class();
#[method(bar)]
fn bar_instance(&self);
}
unsafe impl ProtocolType for dyn Bar {}
);
extern_protocol!(
unsafe trait FooBar: Foo + Bar {
#[method(foobar)]
fn foobar_class();
#[method(foobar)]
fn foobar_instance(&self);
}
unsafe impl ProtocolType for dyn FooBar {}
);
extern_protocol!(
unsafe trait FooFooBar: Foo + FooBar {
#[method(foofoobar)]
fn foofoobar_class();
#[method(foofoobar)]
fn foofoobar_instance(&self);
}
unsafe impl ProtocolType for dyn FooFooBar {}
);
declare_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
struct DummyClass;
unsafe impl ClassType for DummyClass {
type Super = NSObject;
type Mutability = Mutable;
const NAME: &'static str = "ProtocolTestsDummyClass";
}
impl DeclaredClass for DummyClass {}
unsafe impl NSObjectProtocol for DummyClass {}
);
unsafe impl Foo for DummyClass {}
unsafe impl Bar for DummyClass {}
unsafe impl FooBar for DummyClass {}
// unsafe impl FooFooBar for DummyClass {}
unsafe impl Send for DummyClass {}
unsafe impl Sync for DummyClass {}
extern_methods!(
unsafe impl DummyClass {
#[method_id(new)]
fn new() -> Retained<Self>;
}
);
#[test]
fn impl_traits() {
assert_impl_all!(NSObject: NSObjectProtocol);
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol>: NSObjectProtocol);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Send, Sync);
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send>: NSObjectProtocol, Send);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Send>: Sync);
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Sync>: NSObjectProtocol, Sync);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Sync>: Send);
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send + Sync>: NSObjectProtocol, Send, Sync);
assert_not_impl_any!(ProtocolObject<dyn Foo>: NSObjectProtocol);
assert_impl_all!(ProtocolObject<dyn Bar>: NSObjectProtocol);
assert_impl_all!(ProtocolObject<dyn FooBar>: NSObjectProtocol);
assert_impl_all!(ProtocolObject<dyn FooFooBar>: NSObjectProtocol);
assert_impl_all!(DummyClass: NSObjectProtocol);
assert_not_impl_any!(NSObject: Foo);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Foo);
assert_impl_all!(ProtocolObject<dyn Foo>: Foo);
assert_not_impl_any!(ProtocolObject<dyn Bar>: Foo);
assert_impl_all!(ProtocolObject<dyn FooBar>: Foo);
assert_impl_all!(ProtocolObject<dyn FooFooBar>: Foo);
assert_impl_all!(DummyClass: Foo);
assert_not_impl_any!(NSObject: Bar);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Bar);
assert_not_impl_any!(ProtocolObject<dyn Foo>: Bar);
assert_impl_all!(ProtocolObject<dyn Bar>: Bar);
assert_impl_all!(ProtocolObject<dyn FooBar>: Bar);
assert_impl_all!(ProtocolObject<dyn FooFooBar>: Bar);
assert_impl_all!(DummyClass: Bar);
assert_not_impl_any!(NSObject: FooBar);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: FooBar);
assert_not_impl_any!(ProtocolObject<dyn Foo>: FooBar);
assert_not_impl_any!(ProtocolObject<dyn Bar>: FooBar);
assert_impl_all!(ProtocolObject<dyn FooBar>: FooBar);
assert_impl_all!(ProtocolObject<dyn FooFooBar>: FooBar);
assert_impl_all!(DummyClass: FooBar);
assert_not_impl_any!(NSObject: FooFooBar);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: FooFooBar);
assert_not_impl_any!(ProtocolObject<dyn Foo>: FooFooBar);
assert_not_impl_any!(ProtocolObject<dyn Bar>: FooFooBar);
assert_not_impl_any!(ProtocolObject<dyn FooBar>: FooFooBar);
assert_impl_all!(ProtocolObject<dyn FooFooBar>: FooFooBar);
assert_not_impl_any!(DummyClass: FooFooBar);
}
#[test]
fn convertible() {
let mut obj = DummyClass::new();
let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj);
let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(foobar);
let _bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(foobar);
let bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(&*obj);
let bar: &ProtocolObject<dyn Bar> = ProtocolObject::from_ref(bar);
let _foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(foobar);
let foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(&*obj);
let _foo: &ProtocolObject<dyn Foo> = ProtocolObject::from_ref(foo);
let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(foobar);
let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(bar);
let nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(&*obj);
let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(nsobject);
let _: &ProtocolObject<dyn NSObjectProtocol + Send> = ProtocolObject::from_ref(&*obj);
let _: &ProtocolObject<dyn NSObjectProtocol + Sync> = ProtocolObject::from_ref(&*obj);
let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> =
ProtocolObject::from_ref(&*obj);
let _foobar: &mut ProtocolObject<dyn FooBar> = ProtocolObject::from_mut(&mut *obj);
let _foobar: Retained<ProtocolObject<dyn FooBar>> = ProtocolObject::from_retained(obj);
}
#[test]
fn test_traits() {
use core::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
let obj = DummyClass::new();
let obj2 = DummyClass::new();
let foobar: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj);
let foobar2: &ProtocolObject<dyn FooBar> = ProtocolObject::from_ref(&*obj2);
assert_eq!(
format!("{obj:?}"),
format!(
"DummyClass {{ __superclass: {:?}, __ivars: PhantomData<()> }}",
ManuallyDrop::new(foobar)
),
);
assert_eq!(obj == obj2, foobar == foobar2);
let mut hashstate_a = DefaultHasher::new();
let mut hashstate_b = DefaultHasher::new();
obj.hash(&mut hashstate_a);
<_ as Hash>::hash(foobar, &mut hashstate_b);
assert_eq!(hashstate_a.finish(), hashstate_b.finish());
}
// We use `debug_assertions` here just because it's something that we know
// our CI already tests.
extern_protocol!(
#[cfg(debug_assertions)]
unsafe trait CfgTest {}
#[cfg(debug_assertions)]
unsafe impl ProtocolType for dyn CfgTest {}
);
#[test]
#[cfg(debug_assertions)]
fn test_meta() {
if false {
let _protocol = <dyn CfgTest>::protocol();
}
}
}

View File

@@ -0,0 +1,104 @@
//! Optimized versions of `objc_retain` and `objc_release`.
//!
//! On macOS 13.0 / iOS 16.0 / tvOS 16.0 / watchOS 9.0, on ARM64, optimized
//! versions of these two functions that use a different calling convention
//! than the usual C calling convention, are available.
//!
//! Specifically, the expected input register is changed. The output register
//! is unchanged.
//!
//! As an example, if the object is stored in the `x19` register and we need
//! to release it, we usually end up emitting an extra `mov` to get the object
//! into the `x0` register first, as expected by the C calling convention:
//!
//! ```asm
//! mov x0, x19
//! bl _objc_release
//! ```
//!
//! With this optimization though, since the expected register is encoded in
//! the name of the function instead, we can avoid the move altogether.
//!
//! ```asm
//! bl _objc_release_x19
//! ```
//!
//!
//!
//! Safety of our two uses of the `asm!` macro:
//!
//! 1. We use the register class `reg`, with the modifier `x`, which on
//! Aarch64 is defined as `x[0-30]`, see [this][asm-reg-cls].
//!
//! The functions are only available in the variants `x[0-15]` and
//! `x[19-28]` though, see [this][objc4-source], so if the register
//! allocator ends up using `x16`, `x17`, `x18`, `x29` or `x30`, we will
//! emit a call to e.g. `objc_retain_x29`, which will fail at link time.
//!
//! TODO: Before this option can be stable, we need a way to prevent that!
//!
//! 2. We use the `clobber_abi("C")` since we're effectively calling a C
//! C function.
//!
//! [asm-reg-cls]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html#register-operands
//! [objc4-source]: https://github.com/apple-oss-distributions/objc4/blob/objc4-866.9/runtime/objc-abi.h#L442-L498
use crate::ffi;
/// A potentially faster version of `ffi::objc_retain`.
///
///
/// # Safety
///
/// Same as `ffi::objc_retain`.
#[inline]
pub(crate) unsafe fn objc_retain_fast(obj: *mut ffi::objc_object) -> *mut ffi::objc_object {
#[cfg(all(feature = "unstable-apple-new", target_arch = "aarch64"))]
// SAFETY: See the file header.
//
// As per the ARM64 calling convention, the return value is put in `x0`.
//
// That the function itself is safe to call is upheld by the caller.
unsafe {
let result;
core::arch::asm!(
"bl _objc_retain_{obj:x}",
obj = in(reg) obj,
lateout("x0") result,
clobber_abi("C"),
);
result
}
#[cfg(not(all(feature = "unstable-apple-new", target_arch = "aarch64")))]
// SAFETY: Upheld by caller.
unsafe {
ffi::objc_retain(obj)
}
}
/// A potentially faster version of `ffi::objc_release`.
///
///
/// # Safety
///
/// Same as `ffi::objc_release`.
#[inline]
pub(crate) unsafe fn objc_release_fast(obj: *mut ffi::objc_object) {
#[cfg(all(feature = "unstable-apple-new", target_arch = "aarch64"))]
// SAFETY: See the file header.
//
// That the function itself is safe to call is upheld by the caller.
unsafe {
core::arch::asm!(
"bl _objc_release_{obj:x}",
obj = in(reg) obj,
clobber_abi("C"),
)
}
#[cfg(not(all(feature = "unstable-apple-new", target_arch = "aarch64")))]
// SAFETY: Upheld by caller.
unsafe {
ffi::objc_release(obj)
}
}

281
vendor/objc2/src/test_utils.rs vendored Normal file
View File

@@ -0,0 +1,281 @@
use core::ops::{Deref, DerefMut};
use std::os::raw::c_char;
use std::sync::Once;
use crate::encode::{Encode, Encoding, RefEncode};
use crate::rc::Retained;
use crate::runtime::{AnyClass, AnyObject, AnyProtocol, ClassBuilder, ProtocolBuilder, Sel};
use crate::{ffi, msg_send, mutability, sel, ClassType, Message};
#[derive(Debug)]
#[repr(C)]
pub(crate) struct CustomObject(AnyObject);
unsafe impl RefEncode for CustomObject {
const ENCODING_REF: Encoding = Encoding::Object;
}
unsafe impl Message for CustomObject {}
unsafe impl ClassType for CustomObject {
type Super = AnyObject;
type Mutability = mutability::Mutable;
const NAME: &'static str = "CustomObject";
fn class() -> &'static AnyClass {
custom_class()
}
fn as_super(&self) -> &Self::Super {
&self.0
}
fn as_super_mut(&mut self) -> &mut Self::Super {
&mut self.0
}
}
impl Deref for CustomObject {
type Target = AnyObject;
fn deref(&self) -> &AnyObject {
&self.0
}
}
impl DerefMut for CustomObject {
fn deref_mut(&mut self) -> &mut AnyObject {
&mut self.0
}
}
#[derive(Debug, Eq, PartialEq)]
#[repr(C)]
pub(crate) struct CustomStruct {
pub(crate) a: u64,
pub(crate) b: u64,
pub(crate) c: u64,
pub(crate) d: u64,
}
unsafe impl Encode for CustomStruct {
const ENCODING: Encoding = Encoding::Struct(
"CustomStruct",
&[u64::ENCODING, u64::ENCODING, u64::ENCODING, u64::ENCODING],
);
}
pub(crate) fn custom_class() -> &'static AnyClass {
static REGISTER_CUSTOM_CLASS: Once = Once::new();
REGISTER_CUSTOM_CLASS.call_once(|| {
// The runtime will call this method, so it has to be implemented
extern "C" fn custom_obj_class_initialize(_this: &AnyClass, _cmd: Sel) {}
let mut builder = ClassBuilder::root(
"CustomObject",
custom_obj_class_initialize as extern "C" fn(_, _),
)
.unwrap();
let proto = custom_protocol();
builder.add_protocol(proto);
builder.add_ivar::<u32>("_foo");
unsafe extern "C" fn custom_obj_release(this: *mut AnyObject, _cmd: Sel) {
unsafe {
#[allow(deprecated)]
ffi::object_dispose(this.cast());
}
}
extern "C" fn custom_obj_set_foo(this: &mut AnyObject, _cmd: Sel, foo: u32) {
let ivar = this.class().instance_variable("_foo").unwrap();
unsafe { *ivar.load_mut::<u32>(this) = foo }
}
extern "C" fn custom_obj_get_foo(this: &AnyObject, _cmd: Sel) -> u32 {
let ivar = this.class().instance_variable("_foo").unwrap();
unsafe { *ivar.load::<u32>(this) }
}
extern "C" fn custom_obj_get_foo_reference(this: &AnyObject, _cmd: Sel) -> &u32 {
let ivar = this.class().instance_variable("_foo").unwrap();
unsafe { ivar.load::<u32>(this) }
}
extern "C" fn custom_obj_get_struct(_this: &AnyObject, _cmd: Sel) -> CustomStruct {
CustomStruct {
a: 1,
b: 2,
c: 3,
d: 4,
}
}
extern "C" fn custom_obj_class_method(_this: &AnyClass, _cmd: Sel) -> u32 {
7
}
extern "C" fn get_nsinteger(_this: &AnyObject, _cmd: Sel) -> ffi::NSInteger {
5
}
extern "C" fn custom_obj_set_bar(this: &mut AnyObject, _cmd: Sel, bar: u32) {
let ivar = this.class().instance_variable("_bar").unwrap();
unsafe { *ivar.load_mut::<u32>(this) = bar }
}
extern "C" fn custom_obj_add_number_to_number(
_this: &AnyClass,
_cmd: Sel,
fst: i32,
snd: i32,
) -> i32 {
fst + snd
}
extern "C" fn custom_obj_multiple_colon(
_obj: &AnyObject,
_cmd: Sel,
arg1: i32,
arg2: i32,
arg3: i32,
arg4: i32,
) -> i32 {
arg1 * arg2 * arg3 * arg4
}
extern "C" fn custom_obj_multiple_colon_class(
_cls: &AnyClass,
_cmd: Sel,
arg1: i32,
arg2: i32,
arg3: i32,
arg4: i32,
) -> i32 {
arg1 + arg2 + arg3 + arg4
}
unsafe {
// On GNUStep 2.0, it is required to have `dealloc` methods for some reason
if cfg!(all(feature = "gnustep-2-0", not(feature = "gnustep-2-1"))) {
unsafe extern "C" fn forward_to_dealloc(this: *mut AnyObject, _cmd: Sel) {
unsafe { msg_send![this, dealloc] }
}
let release: unsafe extern "C" fn(_, _) = forward_to_dealloc;
builder.add_method(sel!(release), release);
let release: unsafe extern "C" fn(_, _) = custom_obj_release;
builder.add_method(sel!(dealloc), release);
} else {
let release: unsafe extern "C" fn(_, _) = custom_obj_release;
builder.add_method(sel!(release), release);
}
let set_foo: extern "C" fn(_, _, _) = custom_obj_set_foo;
builder.add_method(sel!(setFoo:), set_foo);
let get_foo: extern "C" fn(_, _) -> _ = custom_obj_get_foo;
builder.add_method(sel!(foo), get_foo);
let get_foo_reference: extern "C" fn(_, _) -> _ = custom_obj_get_foo_reference;
builder.add_method(sel!(fooReference), get_foo_reference);
let get_struct: extern "C" fn(_, _) -> CustomStruct = custom_obj_get_struct;
builder.add_method(sel!(customStruct), get_struct);
let class_method: extern "C" fn(_, _) -> _ = custom_obj_class_method;
builder.add_class_method(sel!(classFoo), class_method);
let get_nsinteger: extern "C" fn(_, _) -> _ = get_nsinteger;
builder.add_method(sel!(getNSInteger), get_nsinteger);
let protocol_instance_method: extern "C" fn(_, _, _) = custom_obj_set_bar;
builder.add_method(sel!(setBar:), protocol_instance_method);
let protocol_class_method: extern "C" fn(_, _, _, _) -> _ =
custom_obj_add_number_to_number;
builder.add_class_method(sel!(addNumber:toNumber:), protocol_class_method);
let f: extern "C" fn(_, _, _, _, _, _) -> _ = custom_obj_multiple_colon;
builder.add_method(sel!(test::test::), f);
let f: extern "C" fn(_, _, _, _, _, _) -> _ = custom_obj_multiple_colon_class;
builder.add_class_method(sel!(test::test::), f);
}
builder.register();
});
// Can't use `class!` here since `CustomObject` is dynamically created.
AnyClass::get("CustomObject").unwrap()
}
pub(crate) fn custom_protocol() -> &'static AnyProtocol {
static REGISTER_CUSTOM_PROTOCOL: Once = Once::new();
REGISTER_CUSTOM_PROTOCOL.call_once(|| {
let mut builder = ProtocolBuilder::new("CustomProtocol").unwrap();
builder.add_method_description::<(i32,), ()>(sel!(setBar:), true);
builder.add_method_description::<(), *const c_char>(sel!(getName), false);
builder.add_class_method_description::<(i32, i32), i32>(sel!(addNumber:toNumber:), true);
builder.register();
});
AnyProtocol::get("CustomProtocol").unwrap()
}
pub(crate) fn custom_subprotocol() -> &'static AnyProtocol {
static REGISTER_CUSTOM_SUBPROTOCOL: Once = Once::new();
REGISTER_CUSTOM_SUBPROTOCOL.call_once(|| {
let super_proto = custom_protocol();
let mut builder = ProtocolBuilder::new("CustomSubProtocol").unwrap();
builder.add_protocol(super_proto);
builder.add_method_description::<(u32,), u32>(sel!(calculateFoo:), true);
builder.register();
});
AnyProtocol::get("CustomSubProtocol").unwrap()
}
pub(crate) fn custom_object() -> Retained<CustomObject> {
let ptr: *const AnyClass = custom_class();
unsafe { Retained::from_raw(ffi::class_createInstance(ptr.cast(), 0).cast()) }.unwrap()
}
pub(crate) fn custom_subclass() -> &'static AnyClass {
static REGISTER_CUSTOM_SUBCLASS: Once = Once::new();
REGISTER_CUSTOM_SUBCLASS.call_once(|| {
let superclass = custom_class();
let mut builder = ClassBuilder::new("CustomSubclassObject", superclass).unwrap();
extern "C" fn custom_subclass_get_foo(this: &AnyObject, _cmd: Sel) -> u32 {
let foo: u32 = unsafe { msg_send![super(this, custom_class()), foo] };
foo + 2
}
extern "C" fn custom_subclass_class_method(_cls: &AnyClass, _cmd: Sel) -> u32 {
9
}
unsafe {
let get_foo: extern "C" fn(_, _) -> _ = custom_subclass_get_foo;
builder.add_method(sel!(foo), get_foo);
let class_method: extern "C" fn(_, _) -> _ = custom_subclass_class_method;
builder.add_class_method(sel!(classFoo), class_method);
}
builder.register();
});
AnyClass::get("CustomSubclassObject").unwrap()
}
pub(crate) fn custom_subclass_object() -> Retained<CustomObject> {
let ptr: *const AnyClass = custom_subclass();
unsafe { Retained::from_raw(ffi::class_createInstance(ptr.cast(), 0).cast()) }.unwrap()
}

447
vendor/objc2/src/top_level_traits.rs vendored Normal file
View File

@@ -0,0 +1,447 @@
use core::ptr::NonNull;
use crate::__macro_helpers::declared_ivars::get_initialized_ivar_ptr;
use crate::encode::RefEncode;
use crate::msg_send_id;
use crate::mutability::{IsAllocableAnyThread, IsRetainable, Mutability};
use crate::rc::{Allocated, Retained};
use crate::runtime::{AnyClass, AnyProtocol};
/// Types that can be sent Objective-C messages.
///
/// Implementing this provides [`MessageReceiver`] implementations for common
/// pointer types and references to the type, which allows using them as the
/// receiver (first argument) in the [`msg_send!`][`crate::msg_send`] macro.
///
/// This trait also allows the object to be used in [`Retained`].
///
/// This is a subtrait of [`RefEncode`], meaning the type must also implement
/// that, almost always with [`RefEncode::ENCODING_REF`] being
/// [`Encoding::Object`].
///
/// This can be implemented for unsized (`!Sized`) types, but the intention is
/// not to support dynamically sized types like slices, only `extern type`s
/// (which is currently unstable).
///
/// [`MessageReceiver`]: crate::runtime::MessageReceiver
/// [`Encoding::Object`]: crate::Encoding::Object
///
///
/// # `Drop` interaction
///
/// If the inner type implements [`Drop`], that implementation will very
/// likely not be called, since there is no way to ensure that the Objective-C
/// runtime will do so. If you need to run some code when the object is
/// destroyed, implement the `dealloc` method instead.
///
/// The [`declare_class!`] macro does this for you, but the [`extern_class!`]
/// macro fundamentally cannot.
///
/// [`declare_class!`]: crate::declare_class
/// [`extern_class!`]: crate::extern_class
///
///
/// # Safety
///
/// The type must represent an Objective-C object, meaning it:
/// - Must be valid to reinterpret as [`AnyObject`].
/// - Must be able to be the receiver of an Objective-C message sent with
/// [`objc_msgSend`] or similar.
/// - Must respond to the standard memory management `retain`, `release` and
/// `autorelease` messages.
/// - Must support weak references. (In the future we should probably make a
/// new trait for this, for example `NSTextView` only supports weak
/// references on macOS 10.12 or above).
///
/// [`AnyObject`]: crate::runtime::AnyObject
/// [`objc_msgSend`]: https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend
///
///
/// # Example
///
/// ```
/// use objc2::runtime::NSObject;
/// use objc2::{Encoding, Message, RefEncode};
///
/// #[repr(C)]
/// struct MyObject {
/// // This has the exact same layout as `NSObject`
/// inner: NSObject
/// }
///
/// unsafe impl RefEncode for MyObject {
/// const ENCODING_REF: Encoding = Encoding::Object;
/// }
///
/// unsafe impl Message for MyObject {}
///
/// // `*mut MyObject` and other pointer/reference types to the object can
/// // now be used in `msg_send!`
/// //
/// // And `Retained<MyObject>` can now be constructed.
/// ```
pub unsafe trait Message: RefEncode {}
/// Marks types that represent specific classes.
///
/// Sometimes it is enough to generically know that a type is messageable,
/// e.g. [`Retained`] works with any type that implements the [`Message`]
/// trait. But often, you have an object that you know represents a specific
/// Objective-C class - this trait allows you to communicate that, as well as
/// a few properties of the class to the rest of the type-system.
///
/// This is implemented automatically for your type by the
/// [`declare_class!`][crate::declare_class] and
/// [`extern_class!`][crate::extern_class] macros.
///
///
/// # Safety
///
/// 1. The type must represent a specific class.
/// 2. [`Self::Super`] must be a superclass of the class (or something that
/// represents any object, like [`AnyObject`][crate::runtime::AnyObject]).
/// 3. [`Self::Mutability`] must be specified correctly.
///
/// Note that very little Objective-C code follows Rust's usual ownership
/// model. If you think your type's mutability should be [`Mutable`], think
/// again, it _very_ rarely should!
///
/// If you're unsure of what to do, [`InteriorMutable`] + avoiding `&mut`
/// is usually a good starting point.
/// 4. [`Self::NAME`] must be correct.
/// 5. The class returned by [`Self::class`] must be the class that this type
/// represents.
///
/// [`Mutable`]: crate::mutability::Mutable
/// [`InteriorMutable`]: crate::mutability::InteriorMutable
///
///
/// # Examples
///
/// Use the trait to access the [`AnyClass`] of an object.
///
/// ```
/// use objc2::{ClassType, msg_send_id};
/// use objc2::rc::Retained;
/// # use objc2::runtime::{NSObject as MyObject};
///
/// // Get the class of the object.
/// let cls = <MyObject as ClassType>::class();
/// // Or, since the trait is in scope.
/// let cls = MyObject::class();
///
/// // We can now access properties of the class.
/// assert_eq!(cls.name(), MyObject::NAME);
///
/// // And we can send messages to the class.
/// //
/// // SAFETY:
/// // - The class is `MyObject`, which can safely be initialized with `new`.
/// // - The return type is correctly specified.
/// let obj: Retained<MyObject> = unsafe { msg_send_id![cls, new] };
/// ```
///
/// Use the trait to allocate a new instance of an object.
///
/// ```
/// use objc2::{ClassType, msg_send_id};
/// use objc2::rc::Retained;
/// # use objc2::runtime::{NSObject as MyObject};
///
/// let obj = MyObject::alloc();
///
/// // Now we can call initializers on this newly allocated object.
/// //
/// // SAFETY: `MyObject` can safely be initialized with `init`.
/// let obj: Retained<MyObject> = unsafe { msg_send_id![obj, init] };
/// ```
///
/// Use the [`extern_class!`][crate::extern_class] macro to implement this
/// trait for a type.
///
/// ```
/// use objc2::runtime::NSObject;
/// use objc2::{extern_class, mutability, ClassType};
///
/// extern_class!(
/// struct MyClass;
///
/// // SAFETY: The superclass and the mutability is correctly specified.
/// unsafe impl ClassType for MyClass {
/// type Super = NSObject;
/// type Mutability = mutability::InteriorMutable;
/// # // For testing purposes
/// # const NAME: &'static str = "NSObject";
/// }
/// );
///
/// let cls = MyClass::class();
/// let obj = MyClass::alloc();
/// ```
///
/// Implement the trait manually for a class with a lifetime parameter.
///
/// ```
#[doc = include_str!("../examples/class_with_lifetime.rs")]
/// ```
pub unsafe trait ClassType: Message {
/// The superclass of this class.
///
/// If you have implemented [`Deref`] for your type, it is highly
/// recommended that this is equal to [`Deref::Target`].
///
/// This may be [`AnyObject`] if the class is a root class.
///
/// [`Deref`]: std::ops::Deref
/// [`Deref::Target`]: std::ops::Deref::Target
/// [`AnyObject`]: crate::runtime::AnyObject
type Super: Message;
/// Whether the type is mutable or immutable.
///
/// See the [`mutability`][crate::mutability] module for further details
/// about class mutability.
type Mutability: Mutability;
/// The name of the Objective-C class that this type represents.
///
/// `T::NAME` is the `const` version of `T::class().name()`.
const NAME: &'static str;
/// Get a reference to the Objective-C class that this type represents.
///
/// May register the class with the runtime if it wasn't already.
///
///
/// # Panics
///
/// This may panic if something went wrong with getting or declaring the
/// class, e.g. if the program is not properly linked to the framework
/// that defines the class.
fn class() -> &'static AnyClass;
/// Get an immutable reference to the superclass.
// Note: It'd be safe to provide a default impl using transmute here if
// we wanted to!
fn as_super(&self) -> &Self::Super;
/// Get a mutable reference to the superclass.
// Note: No `Self: IsMutable` bound required here, since there is no way
// to get `&mut self` in the first place.
//
// Or at least, if we have `&mut NSMutableString`, we're allowed to get
// `&mut NSString`, and from that it will also make sense to allow getting
// `&mut NSObject`.
fn as_super_mut(&mut self) -> &mut Self::Super;
/// Increment the reference count of the receiver.
///
/// This extends the duration in which the receiver is alive by detaching
/// it from the lifetime information carried by the reference.
///
/// This is similar to using [`Clone` on `Retained<Self>`][clone-id], with
/// the addition that it can be used on a plain reference. Note however
/// that this is not possible to use on certain types like `NSString`,
/// since if you only hold `&NSString`, that may have come from
/// `&mut NSMutableString`, in which case it would be unsound to erase the
/// lifetime information carried by the reference.
///
/// In cases like that, you should rather use `NSCopying::copy` (since
/// that gives you a `NSString` whether the string was originally a
/// `NSString` or a `NSMutableString`).
///
/// [clone-id]: crate::rc::Retained#impl-Clone-for-Retained<T>
//
// Note: We could have placed this on `mutability::IsRetainable`, but
// `ClassType` is more often already in scope, allowing easier access to
// `obj.retain()`.
#[inline]
#[doc(alias = "objc_retain")]
fn retain(&self) -> Retained<Self>
where
Self: IsRetainable,
Self: Sized, // Temporary
{
let ptr: *const Self = self;
let ptr: *mut Self = ptr as _;
// SAFETY:
// - The object is known to not be mutable (or have a mutable
// subclass) due to the `IsRetainable` bound.
// - The pointer is valid since it came from `&self`.
// - The lifetime of the pointer itself is extended, but any lifetime
// that the object may carry is still kept within the type itself.
let obj = unsafe { Retained::retain(ptr) };
// SAFETY: The pointer came from `&self`, which is always non-null
// (and objc_retain always returns the same value).
unsafe { obj.unwrap_unchecked() }
}
/// Allocate a new instance of the class.
///
/// The return value can be used directly inside [`msg_send_id!`] to
/// initialize the object.
///
/// For classes that are only usable on the main thread, you can use
/// `MainThreadMarker::alloc` instead.
///
/// [`msg_send_id!`]: crate::msg_send_id
//
// Note: We could have placed this on `mutability::IsAllocableAnyThread`,
// but `ClassType` is more often already in scope, allowing easier access
// to `T::alloc()`.
#[inline]
fn alloc() -> Allocated<Self>
where
Self: IsAllocableAnyThread + Sized,
{
// SAFETY:
// - It is always safe to (attempt to) allocate an object.
// - The object is of the correct type, since we've used the class
// from `Self::class`.
// - The object is safe to `dealloc` on the current thread (due to the
// `IsAllocableAnyThread` bound which guarantees it is not
// `MainThreadOnly`).
//
// See also `MainThreadMarker::alloc`.
unsafe { msg_send_id![Self::class(), alloc] }
}
// TODO: `fn alloc_on_main(mtm: MainThreadMarker)`
// TODO: `fn mtm(&self) -> MainThreadMarker where T::Mutability: MainThreadOnly`
}
/// Marks types whose implementation is defined in Rust.
///
/// This is used in [`declare_class!`], and allows access to the instance
/// variables that a given type declares, see that macro for details.
///
/// [`declare_class!`]: crate::declare_class
//
// Note: We mark this trait as not `unsafe` for better documentation, since
// implementing it inside `declare_class!` is not `unsafe`.
//
// Safety is ensured by `__UNSAFE_OFFSETS_CORRECT`.
pub trait DeclaredClass: ClassType {
/// A type representing the instance variables that this class carries.
type Ivars: Sized;
// TODO: Add `ivars_ptr(this: NonNull<Self>) -> NonNull<Self::Ivars>`?
/// Get a reference to the instance variable data that this object
/// carries.
#[inline]
#[track_caller]
fn ivars(&self) -> &Self::Ivars
where
Self: Sized, // Required because of MSRV
{
let ptr: NonNull<Self> = NonNull::from(self);
// SAFETY: The pointer is valid and initialized.
let ivars = unsafe { get_initialized_ivar_ptr(ptr) };
// SAFETY: The lifetime of the instance variable is tied to the object.
unsafe { ivars.as_ref() }
}
/// Get a mutable reference to the instance variable data that this object
/// carries.
#[inline]
#[track_caller]
fn ivars_mut(&mut self) -> &mut Self::Ivars
where
Self: Sized, // Required because of MSRV
{
let ptr: NonNull<Self> = NonNull::from(self);
// SAFETY: The pointer is valid and initialized.
let mut ivars = unsafe { get_initialized_ivar_ptr(ptr) };
// SAFETY: The lifetime of the instance variable is tied to the object.
//
// Mutability is safe since the object itself is mutable. See
// `ClassType::as_super_mut` for why this is safe without
// `Self: IsMutable`.
unsafe { ivars.as_mut() }
}
#[doc(hidden)]
fn __ivars_offset() -> isize;
#[doc(hidden)]
fn __drop_flag_offset() -> isize;
/// # Safety
///
/// The ivar offset and drop flag offsets must be implemented correctly.
#[doc(hidden)]
const __UNSAFE_OFFSETS_CORRECT: ();
}
/// Marks types that represent specific protocols.
///
/// This is the protocol equivalent of [`ClassType`].
///
/// This is implemented automatically by the [`extern_protocol!`] macro for
/// `dyn T`, where `T` is the protocol.
///
/// [`ClassType`]: crate::ClassType
/// [`extern_protocol!`]: crate::extern_protocol
///
///
/// # Safety
///
/// This is meant to be a sealed trait, and should not be implemented outside
/// of the [`extern_protocol!`] macro.
///
///
/// # Examples
///
/// Use the trait to access the [`AnyProtocol`] of different objects.
///
/// ```
/// use objc2::ProtocolType;
/// use objc2::runtime::NSObjectProtocol;
/// // Get a protocol object representing the `NSObject` protocol
/// let protocol = <dyn NSObjectProtocol>::protocol().expect("NSObject to have a protocol");
/// assert_eq!(<dyn NSObjectProtocol>::NAME, protocol.name());
/// # // Ensure Foundation links on GNUStep
/// # let _cls = objc2::class!(NSObject);
/// ```
///
/// Use the [`extern_protocol!`] macro to implement this trait for a type.
///
/// ```no_run
/// use objc2::{extern_protocol, ProtocolType};
///
/// extern_protocol!(
/// unsafe trait MyProtocol {}
/// unsafe impl ProtocolType for dyn MyProtocol {}
/// );
///
/// let protocol = <dyn MyProtocol>::protocol();
/// ```
pub unsafe trait ProtocolType {
/// The name of the Objective-C protocol that this type represents.
const NAME: &'static str;
/// Get a reference to the Objective-C protocol object that this type
/// represents.
///
/// May register the protocol with the runtime if it wasn't already.
///
/// Note that some protocols [are not registered with the runtime][p-obj],
/// depending on various factors. In those cases, this function may return
/// `None`.
///
/// [p-obj]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html#//apple_ref/doc/uid/TP30001163-CH15-TPXREF149
///
///
/// # Panics
///
/// This may panic if something went wrong with getting or declaring the
/// protocol, e.g. if the program is not properly linked to the framework
/// that defines the protocol.
fn protocol() -> Option<&'static AnyProtocol> {
AnyProtocol::get(Self::NAME)
}
#[doc(hidden)]
const __INNER: ();
}

View File

@@ -0,0 +1,833 @@
# Framework crate changelog
Changes to `objc2`'s framework crates will be documented in this file.
The history of these crates are a bit convoluted; the Foundation parts
originally existed as `objc2-foundation`, but later it was integrated into
`objc2` under `objc2::foundation`, later again moved into `icrate::Foundation`
as part of the larger `icrate`, and finally `icrate` was split back out into
`objc2-foundation` and other smaller crates - hence the confusing versioning.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased - YYYY-MM-DD
## 0.2.1 - 2024-05-21
### Added
* `NS_OPTIONS` enums are now `bitflags!`-style enums.
This means that they have the usual `ops::BitAnd`, `ops::BitOr`, `ops::Not`,
etc. implementations that you would expect from an option enum.
* Added optional support for a few methods depending on types from `libc`.
* Added new framework crates:
- `CoreBluetooth` / `objc2-core-bluetooth`.
- `Virtualization` / `objc2-virtualization`.
- `Symbols` / `objc2-symbols`.
- `UIKit` / `objc2-ui-kit`.
- `Accounts` / `objc2-accounts`.
- `AppTrackingTransparency` / `objc2-app-tracking-transparency`.
- `MLCompute` / `objc2-ml-compute`.
- `ContactsUI` / `objc2-contacts-ui`.
- `NearbyInteraction` / `objc2-nearby-interaction`.
- `ScreenCaptureKit` / `objc2-screen-capture-kit`.
- `CoreImage` / `objc2-core-image`.
- `CoreMotion` / `objc2-core-motion`.
- `MultipeerConnectivity` / `objc2-multipeer-connectivity`.
- `NaturalLanguage` / `objc2-natural-language`.
- `CoreML` / `objc2-core-ml`.
- `Vision` / `objc2-vision`.
- `AVKit` / `objc2-av-kit`.
- `NetworkExtension` / `objc2-network-extension`.
- `SensitiveContentAnalysis` / `objc2-sensitive-content-analysis`.
- `PhotosUI` / `objc2-photos-ui`.
- `FinderSync` / `objc2-finder-sync`.
- `Social` / `objc2-social`.
* `objc2-quartz-core`: Added `CAMetalDrawable` and `CAMetalLayer`.
* `objc2-app-kit`: Added methods to access `CALayer` from `NSView`.
* `objc2-metal`: Added `MTLCounterErrorValue`, `MTLCounterDontSample` and
`MTLTextureSwizzleChannelsDefault` constants.
### Changed
* Updated SDK from Xcode 15.3 to 15.4.
View the release notes to learn more details:
- [15.4](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_4-release-notes)
### Fixed
* `objc2-metal`: All protocols are now marked as `IsRetainable`.
This may technically break your code if you had some custom object that
implements the protocol, but was marked with `Mutability = Mutable`, but
Metal protocols are assumed retainable by Objective-C code, so that'd have
been unsound anyway.
* Add `#[cfg(target_os = "...")]` when a platform-specific dependency is used.
This improves iOS support, as crates like `objc2-app-kit` are no longer
enabled on this platform.
* Fix dependency feature flags (e.g. `block2`) not enabling the matching
features in dependencies (e.g. not enabling `objc2-foundation/block2`).
### Removed
* `objc2-metal`: Removed internal `__MTLPackedFloat3` and made `MTLPackedFloat3` public.
* `objc2-core-data`: Removed broken GNUStep support.
* `objc2-quartz-core`: Removed broken GNUStep support.
## 0.2.0 - 2024-04-17
### Added
* Added `NSObject` categories, notably those used by key-value coding and
observing.
* Added a few statics that were previously omitted (notably a few
`NSWindowLevel` constants).
### Changed
* Updated SDK from Xcode 15.0.1 to 15.3.
View the release notes to learn more details:
- [15.1](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_1-release-notes)
- [15.2](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_2-release-notes)
- [15.3](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_3-release-notes)
* **BREAKING**: Changed how categories are handled; now, when a library has
defined methods on a class defined in a different framework, a helper trait
is output with the methods, instead of the methods being implemented
directly on the type.
* **BREAKING**: Changed how enums are handled; now a newtype is generated for
each enum, with the enum variants as constants on that newtype, instead of
cluttering the top-level namespace.
* **BREAKING**: Split `icrate` into multiple smaller crates all prefixed with
`objc2-`, and changed how feature flags work.
Feature flags are now based on the header file name where the item is
defined, instead of being based on the class name itself.
These two things should decrease compilation times by quite a lot, at the
cost of having to specify more precisely what you need in your `Cargo.toml`.
An example migration can be seen in the following:
```toml
# Before
[dependencies.icrate]
version = "0.1.0"
features = [
"Foundation",
"Foundation_NSNotification",
"Foundation_NSString",
"Foundation_NSThread",
"Foundation_NSArray",
"Foundation_NSMutableArray",
"AppKit",
"AppKit_NSApplication",
]
# After
# Moved to `objc2-foundation` and `objc2-app-kit` crates.
[dependencies]
# Removed `Foundation_NSMutableArray`, it is included via. `NSArray`.
# Added `NSObject` as the `NSCopying` protocol comes from there.
objc2-foundation = { version = "0.2", features = ["NSNotification", "NSString", "NSThread", "NSObject", "NSArray"] }
# Added `NSResponder` as it's required by `NSApplication`.
# Added `NSRunningApplication` as a lot of application constants come from here.
objc2-app-kit = { version = "0.2", features = ["NSResponder", "NSApplication", "NSRunningApplication"] }
```
* Marked `NSView::isFlipped`, `NSView::convertRect_toView`,
`NSWindow::convertRectToScreen` and `NSWindow::convertPointFromScreen` as
safe.
* Renamed the `block` and `objective-c` feature flags to `block2` and `objc2`.
* **BREAKING**: Updated `block2` to `v0.5`.
### Deprecated
* Deprecated `Foundation::MainThreadMarker::run_on_main`, use the new
free-standing function `objc2_foundation::run_on_main` instead.
### Removed
* Removed private functionality in the `Speech` framework. This was never
intended to be exposed by Apple.
This is technically a breaking change, but will be allowed in a minor
version since it isn't supported by Apple.
### Fixed
* Fixed the `QuartzCore` and `Photos` frameworks not being loaded correctly.
* Fixed a few feature gates on methods showing up unnecessarily.
* **BREAKING**: Added `MainThreadMarker` parameter to a few methods where it
was erroneously missing.
* **BREAKING**: Fix the type of `GKMatchProperties` and `NSWindow`'s
`restorationClass`.
* **BREAKING**: Fixed feature flags not being emitted on typedefs, enums,
functions and statics.
## icrate 0.1.2 - 2023-04-18
## Fixed
* Fixed "multiple applicable items in scope" error when upgrading `objc2` to `v0.5.1`.
## icrate 0.1.1 - 2023-04-17
### Deprecated
* Deprecated the `icrate` crate, it has been split into multiple smaller crates.
## icrate 0.1.0 - 2023-12-23
### Added
* Added `MainThreadMarker` `From` implementation for `MainThreadOnly` types.
* Added `Send` and `Sync` implementations for a bunch more types (same as the
ones Swift marks as `@Sendable`).
* Made some common methods in `AppKit` safe.
* Added missing `NSCopying` and `NSMutableCopying` zone methods.
* Added `Eq` and `Ord` implementations for `NSNumber`, since its
handling of floating point values allows it.
* Added `NS[Mutable]Dictionary::from_id_slice` and
`NS[Mutable]Dictionary::from_slice`.
* Added `NSMutableDictionary::insert` and `NSMutableSet::insert` which can
be more efficient than the previous insertion methods.
### Changed
* Updated SDK from Xcode 14.2 to 15.0.1.
View the release notes to learn more details:
- [14.3](https://developer.apple.com/documentation/xcode-release-notes/xcode-14_3-release-notes)
- [14.3.1](https://developer.apple.com/documentation/xcode-release-notes/xcode-14_3_1-release-notes)
- [15.0](https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes)
- [15.0.1](https://developer.apple.com/documentation/xcode-release-notes/xcode-15_0_1-release-notes)
Breaking changes are noted elsewhere in this changelog entry.
* Moved the `ns_string!` macro to `icrate::Foundation::ns_string`. The old
location in the crate root is deprecated.
* **BREAKING**: The following two methods on
`MTLAccelerationStructureCommandEncoder` now take a nullable scratch buffer:
- `refitAccelerationStructure_descriptor_destination_scratchBuffer_scratchBufferOffset`
- `refitAccelerationStructure_descriptor_destination_scratchBuffer_scratchBufferOffset_options`
* **BREAKING**: Marked UI-related classes as `MainThreadOnly`, and UI-related
protocols as `IsMainThreadOnly`.
This means that they can now only be constructed, retrieved and used on the
main thread, meaning you usually have to aquire a `MainThreadMarker` first.
```rust, ignore
// Before
let app = unsafe { NSApplication::sharedApplication() };
let view = unsafe { NSView::initWithFrame(NSView::alloc(), frame) };
// Do something with `app` and `view`
// After
let mtm = MainThreadMarker::new().unwrap();
let app = unsafe { NSApplication::sharedApplication(mtm) };
let view = unsafe { NSView::initWithFrame(mtm.alloc(), frame) };
// Do something with `app` and `view`
```
* **BREAKING**: Changed the `NSApp` static to be a function taking `MainThreadMarker`.
* **BREAKING**: Renamed `NS[Mutable]Dictionary::from_keys_and_objects` to
`NS[Mutable]Dictionary::from_vec`.
* **BREAKING**: Renamed `NSMutableDictionary::insert` and
`NSMutableSet::insert` to `insert_id`.
* **BREAKING**: `CWWiFiClient::interfaceNames` has been renamed to `CWWiFiClient::interfaceNames_class`.
* **BREAKING**: Updated `objc2` to `v0.5.0`.
* **BREAKING**: Updated `block2` to `v0.4.0`.
### Removed
* **BREAKING**: Removed the `MainThreadMarker` argument from the closure
passed to `MainThreadBound::get_on_main`.
* **BREAKING**: Removed `Foundation::CopyHelper` since it is superseded by
`objc2::mutability::CounterpartOrSelf`.
* **BREAKING**: Removed the following APIs, as they are no longer available in macOS 14 / iOS 17:
- `NSFileProviderDomain::volumeUUID`
- `CLBeaconIdentityConstraint::UUID`
- `CLBeaconIdentityConstraint::major`
- `CLBeaconIdentityConstraint::minor`
- `ASIdentifierManager::clearAdvertisingIdentifier`
* Removed private `MetricKit::_MXSignpostMetricsSnapshot` function.
### Fixed
* **BREAKING**: Added `Eq + Hash` requirement on most `NSDictionary` and
`NSSet` methods, thereby making sure that the types are actually correct
to use in such hashing collections.
* **BREAKING**: Added `HasStableHash` requirement on `NSDictionary` and
`NSSet` creation methods, fixing a long-standing soundess issue.
* Fixed the protocol names of `NSAccessibilityElementProtocol`,
`NSTextAttachmentCellProtocol` and `NSFileProviderItemProtocol`.
* **BREAKING**: Generic types no longer strictly require `Message` (although
most of their trait implementations still require that).
* **BREAKING**: Removed a workaround that made the `NSCopying` and
`NSMutableCopying` protocols not act as regular protocols (many methods used
`AnyObject` instead of the correct `ProtocolObject<dyn NSCopying>`).
* Update the minimum deployment target, which adds a few missing protocol
implementations and methods for `NSPopover` and `NSLayoutAnchor`.
* **BREAKING**: `CKSystemSharingUIObserver` and `CKLocationSortDescriptor` are no longer marked thread safe.
* **BREAKING**: `NSColor::ignoresAlpha` now requires a main thread marker.
## icrate 0.0.4 - 2023-07-31
### Changed
* **BREAKING**: Updated `block2` to `v0.3.0`.
### Fixed
* Documentation on docs.rs.
## icrate 0.0.3 - 2023-06-20
### Added
* Added the following frameworks:
- `HealthKit`
- `MediaPlayer`
- `MetricKit`
- `PhotoKit`
* Added `NSCopying` and `NSMutableCopying` implementations for the classes
that implement those protocols.
* Added the following methods:
- `NSArray::get_retained`
- `NSArray::first_retained`
- `NSArray::last_retained`
- `NSSet::get_retained`
- `NSSet::to_vec`
- `NSSet::to_vec_retained`
- `NSDictionary::get_retained`
- `NSDictionary::keys_retained`
- `NSDictionary::values_retained`
* Added `MainThreadMarker::alloc` for allocating objects that need to be so on
the main thread.
* Added automatically generated `new`/`init` methods for all types.
* Added `FromIterator` impls for various collection types.
### Changed
* **BREAKING**: Renamed the `from_slice` method on `NSArray`, `NSSet`,
`NSMutableArray` and `NSMutableSet` to `from_id_slice`, and provided a new
`from_slice` method that takes `&[&T]` instead.
* **BREAKING**: Changed `NSMutableArray::replace` to return an `Result` in
case the index was out of bounds.
* **BREAKING**: Changed `NSMutableArray::remove` to return an `Option` in case
the index was out of bounds.
* **BREAKING**: Removed ownership parameter from generic types, since the
ownership/mutability information is now stored in `ClassType::Mutability`.
* **BREAKING**: Renamed `NSMutableCopying::mutable_copy` to `::mutableCopy`.
* **BREAKING**: The default value for `NSUUID` was changed from a nil UUID to
a new random UUID.
* **BREAKING**: Changed how iteration works.
Instead of the single `NSFastEnumerator`, we now have concrete types
`array::Iter`, `array::IterMut`, `array::IterRetained` and
`array::IntoIter`, which allows iterating over `NSArray` in different ways.
Combined with proper `IntoIterator` implementations for collection types,
you can now do:
```rust, ignore
let mut array: Id<NSMutableArray<T>> = ...;
for item in &array {
// item: &T
}
// If T: IsMutable
for item in &mut array {
// item: &mut T
}
// If T: IsIdCloneable
for item in array.iter_retained() {
// item: Id<T>
}
for item in array {
// item: Id<T>
}
```
(similar functionality exist for `NSSet` and `NSDictionary`).
* **BREAKING**: Renamed `NSDictionary` methods:
- `keys` -> `keys_vec`.
- `values` -> `values_vec`.
- `values_mut` -> `values_vec_mut`.
- `keys_and_objects` -> `to_vecs`.
- `iter_keys` -> `keys`.
- `iter_values` -> `values`.
* **BREAKING**: `NSDictionary::keys_retained` and
`NSDictionary::values_retained` now return an iterator instead.
* **BREAKING**: Updated `objc2` to `v0.4.0`.
* **BREAKING**: Updated `block2` to `v0.2.0`.
### Removed
* **BREAKING**: Removed various redundant `NSProxy` methods.
* **BREAKING**: Removed `NSArray::to_shared_vec` and `NSArray::into_vec`, use
`NSArray::to_vec` or `NSArray::to_vec_retained` instead.
* **BREAKING**: Removed associated types from `NSCopying` and
`NSMutableCopying`, that information is now specified in
`ClassType::Mutability` instead.
* **BREAKING**: Removed a few `init` methods on subclasses that were declared
on categories on their superclass. These should be re-added at some point.
### Fixed
* Soundness issues with enumeration / iteration over collection types.
## icrate 0.0.2 - 2023-02-07
### Added
* Added the following frameworks:
- `Accessibility`
- `AdServices`
- `AdSupport`
- `AutomaticAssessmentConfiguration`
- `Automator`
- `BackgroundAssets`
- `BackgroundTasks`
- `BusinessChat`
- `CallKit`
- `ClassKit`
- `CloudKit`
- `Contacts`
- `CoreLocation`
- `DataDetection`
- `DeviceCheck`
- `EventKit`
- `ExceptionHandling`
- `ExtensionKit`
- `ExternalAccessory`
- `FileProvider`
- `FileProviderUI`
- `GameController`
- `GameKit`
- `IdentityLookup`
- `InputMethodKit`
- `LinkPresentation`
- `LocalAuthentication`
- `LocalAuthenticationEmbeddedUI`
- `MailKit`
- `MapKit`
- `Metal`
- `MetalFX`
- `MetalKit`
- `OSAKit`
- `CoreAnimation` (also known as `QuartzCore`)
- `SoundAnalysis`
- `Speech`
- `StoreKit`
- `UniformTypeIdentifiers`
- `UserNotifications`
- `WebKit`
* Updated the SDK version from XCode `14.0.1` to `14.2`.
- See differences [here](https://sdk.news/macOS-13.0/).
* Added `Foundation::MainThreadBound` helper struct for things that are only
accesible from the main thread.
* Added `#[deprecated]` annotations to the autogenerated output.
* Added disambiguation for duplicated methods (e.g. `NSThread::threadPriority`
vs. `NSThread::threadPriority_class`).
* Properly implemented protocols for defined classes.
### Changed
* Cfg-gated static `ns_string!` functionality behind the
`unstable-static-nsstring` cargo feature.
* Autogenerated method parameters are now in snake-case, for better IDE
support.
* **BREAKING**: Cfg-gate all classes, and everything that references said
classes.
This means that to use e.g. `Foundation::NSThread::name`, you have to enable
the `Foundation_NSThread` and `Foundation_NSString` cargo features.
* **BREAKING**: Updated `objc2` to `v0.3.0-beta.5`.
* **BREAKING**: Updated `block2` to `v0.2.0-alpha.8`.
### Removed
* **BREAKING**: The optional `uuid` integration, since one might want to use
`icrate` internally in that crate in the future, and that would break.
* **BREAKING**: Removed `NSNib::instantiateWithOwner_topLevelObjects`,
`NSBundle::loadNibNamed_owner_topLevelObjects` and `NSFreeMapTable` since
they had weird memory management.
### Fixed
* Ensure we never hit a memory management issue again.
* **BREAKING**: Fixed a few `*mut` pointers that should've been `*const`.
* **BREAKING**: Fixed a few generic ownership parameters that defaulted to
`Shared`.
* Removed a few instances of `TodoProtocols`.
* Fixed type-encoding of a few `struct`s.
* Fixed `NSProxy` trait methods.
* **BREAKING**: Fixed type in methods that worked with out-parameters.
## icrate 0.0.1 - 2022-12-24
### Added
* Added `NSString::write_to_file`.
* Added `NSLock` class and `NSLocking` protocol.
* Added autogenerated implementations of the following frameworks:
- `AppKit`
- `AuthenticationServices`
- `CoreData`
- `Foundation`
### Changed
* **BREAKING**: Moved from `objc2::foundation` into `icrate::Foundation`.
* **BREAKING**: Changed the following methods:
- `NSString`
- `concat` -> `stringByAppendingString`
- `join_path` -> `stringByAppendingPathComponent`
- `has_prefix` -> `hasPrefix`
- `has_suffix` -> `hasSuffix`
- `NSMutableString`
- `from_nsstring` -> `stringWithString`
- `with_capacity` -> `stringWithCapacity`
- `push_nsstring` -> `appendString`
- `replace` -> `setString`
- `NSAttributedString`
- `init_with_attributes` -> `unsafe initWithString_attributes`
- `init_with_string` -> `initWithString`
- `new_with_attributes` -> `unsafe new_with_attributes`
- `len_utf16` -> `length`
- `NSMutableAttributedString`
- `replace` -> `setAttributedString`
- `NSBundle`
- `main` -> `mainBundle`
- `info` -> `infoDictionary`
- `NSDictionary`
- `keys_array` -> `allKeys`
- `into_values_array` -> `allValues`
- `NSMutableDictionary`
- `clear` -> `removeAllObjects`
- `NSMutableArray`
- `clear` -> `removeAllObjects`
- `NSMutableSet`
- `clear` -> `removeAllObjects`
- `NSError`
- `user_info` -> `userInfo`
- `localized_description` -> `localizedDescription`
- `NSException`
- `user_info` -> `userInfo`
- `NSMutableData`
- `from_data` -> `dataWithData`
- `with_capacity` -> `dataWithCapacity`
- `set_len` -> `setLength`
- `NSUUID`
- `new_v4` -> `UUID`
- `string` -> `UUIDString`
- `NSThread`
- `current` -> `currentThread`
- `main` -> `mainThread`
- `is_main` -> `isMainThread`
- `NSProcessInfo`
- `process_info` -> `processInfo`
* **BREAKING**: Make `NSComparisonResult` work like all other enums.
* **BREAKING**: Changed `NSDictionary` to be `Shared` by default.
* **BREAKING** (TEMPORARY): Renamed `NSEnumerator`, `NSFastEnumeration` and
`NSFastEnumerator` until the story around them are properly figured out.
* **BREAKING**: Make `NSArray::objects_in_range` return an `Option` (it was
unsound before).
### Fixed
* Fixed `NSZone` not specifying a `#[repr(...)]`.
## objc2::foundation 0.3.0-beta.3 - 2022-09-01
### Added
* Added `NSSet`.
* Added `NSMutableSet`.
* Added `NSMutableDictionary`.
* Added `NSNotFound`.
* Added `NSBundle`.
* Added `NSTimeInterval`.
* Added `NSString::len_utf16` and `NSAttributedString::len_utf16`.
* Added `NSString::concat` and `NSString::join_path`.
* Added `CGSize`, `CGPoint` and `CGRect` (aliases to equivalent `NS`-types
that helps readability).
### Changed
* **BREAKING**: `NSSize::new` no longer requires it's arguments to be
non-negative. Use `NSSize::abs` or `NSRect::standardize` if the API you're
binding to requires a non-negative size.
## objc2::foundation 0.3.0-beta.2 - 2022-08-28
### Added
* Added `NSNumber`.
* Added `NSError`.
* Implement `UnwindSafe` and `RefUnwindSafe` for all objects.
* Implemented `IntoIterator` for references to `NSArray`, `NSMutableArray`,
`NSData` and `NSMutableData`.
* Implemented `Extend` for `NSMutableArray`.
* Add extra `Extend<&u8>` impl for `NSMutableData`.
* Added function `NSValue::contains_encoding` for determining if the encoding
of the `NSValue` matches the encoding of the given type.
* Added functions `get_range`, `get_point`, `get_size` and `get_rect` to
`NSValue` to help safely returning various types it will commonly contain.
* `NSArray` and `NSMutableArray` now have sensible defaults for the ownership
of the objects they contain.
### Changed
* **BREAKING**: Moved from external crate `objc2_foundation` into
`objc2::foundation`.
* **BREAKING**: Made `NSValue` not generic any more. While we loose some
type-safety from this, it makes `NSValue` much more useful in the real
world!
* **BREAKING**: Made `NSArray::new` generic over ownership.
* **BREAKING**: Made `NSObject::is_kind_of` take a generic `T: ClassType`
instead of a `runtime::Class`.
### Fixed
* Made `Debug` impls for all objects print something useful.
### Removed
* `NSObject::hash_code`, `NSObject::is_equal` and `NSObject::description` in
favour of having the trait implementations `Hash`, `PartiqalEq` and `Debug`.
## objc2-foundation 0.2.0-alpha.6 - 2022-07-19
### Added
* Added `MainThreadMarker` to help with designing APIs where a method is only
safe to call on the main thread.
* Added `NSException` object.
* Added `extern_class!` macro to help with defining interfaces to classes.
Further changelog for this can be found in `CHANGELOG.md`.
* Added `declare_class!` macro to help with declaring custom classes.
Further changelog for this can be found in `CHANGELOG.md`.
* Expose the `objc2` version that this uses in the crate root.
* Added `NSZone`.
### Changed
* Changed a few `Debug` impls.
## objc2-foundation 0.2.0-alpha.5 - 2022-06-13
### Added
* Objects now `Deref` to their superclasses. E.g. `NSMutableArray` derefs to
`NSArray`, which derefs to `NSObject`, which derefs to `Object`.
This allows more ergonomic usage.
* Implement `PartialOrd` and `Ord` for `NSString` and `NSRange`.
* Added `NSString::has_prefix` and `NSString::has_suffix`.
* Added `NSRange` methods `new`, `is_empty`, `contains` and `end`.
* Added `NSThread` object.
* Added `is_multi_threaded` and `is_main_thread` helper functions.
* Added `NSProcessInfo` object.
* Added `NSMutableData` methods `from_data`, `with_capacity` and `push`.
* Added `io::Write` and `iter::Extend` implementation for `NSMutableData`.
* Added `NSUUID` object.
* Added `NSMutableString` object.
* Added basic `NSAttributedString` object.
* Added basic `NSMutableAttributedString` object.
* Added `NSInteger` and `NSUInteger` (type aliases to `isize` and `usize`).
* Added `CGFloat`.
* Added `NSPoint`.
* Added `NSSize`.
* Added `NSRect`.
* Implement `Borrow` and `BorrowMut` for all objects.
* Implement `ToOwned` for copyable types.
### Changed
* **BREAKING**: Removed the following helper traits in favor of inherent
methods on the objects themselves:
- `INSMutableArray`
- `INSArray`
- `INSMutableData`
- `INSData`
- `INSDictionary`
- `INSString`
- `INSValue`
- `INSObject`
This changed because objects now deref to their superclasses.
* **BREAKING**: Relaxed a lot of bounds from `INSObject` to `Message`. At some
point in the future a new trait will be introduced which remedies this
change.
* **BREAKING**: Removed the `I` prefix from:
- `INSCopying` (now `NSCopying`)
- `INSMutableCopying` (now `NSMutableCopying`)
- `INSFastEnumeration` (now `NSFastEnumeration`)
* **BREAKING**: Renamed `NSMutableData::append` to `extend_from_slice`.
## 0.2.0-alpha.4 - 2022-01-03
### Added
* Implement `PartialOrd` and `Ord` for `NSComparisonResult` and `NSValue`.
* Implement `fmt::Display` for `NSValue`.
* Implement `DefaultId` for relevant objects.
* Implement `AsRef` and `Index` for `NSData` and `NSMutableData`.
* Implement `AsMut` and `IndexMut` for `NSMutableData`.
### Changed
* **BREAKING**: Renamed `INSFastEnumeration::enumerator` to
`INSFastEnumeration::iter_fast`.
### Removed
* **BREAKING**: Removed `Deref` and `DerefMut` from `NSData` and
`NSMutableData`, since these invoke a non-trivial amount of code, and could
lead to hard-to-diagnose performance issues.
## objc2-foundation 0.2.0-alpha.3 - 2021-12-22
### Added
* **BREAKING**: Added associated `Ownership` type to `NSCopying`.
* **BREAKING**: Added associated `Ownership` type to `INSData`.
* **BREAKING**: Added associated `Ownership` type to `INSArray`.
* Added common trait impls (`PartialEq`, `Eq`, `Hash` and `Debug`) to
`NSValue`, `NSDictionary`, `NSArray` and `NSMutableArray`.
### Changed
* **BREAKING**: Made some creation methods a bit less generic (e.g.
`INSDictionary::from_keys_and_objects` now always returns `Id<_, Shared>`).
* Relax bounds on generic `INSObject` impls.
### Removed
* **BREAKING**: Removed associated `Ownership` type from `INSObject`; instead,
it is present on the types that actually need it (for example `NSCopying`).
* **BREAKING**: Removed `Sized` bound on `INSObject`.
### Fixed
* Soundness issue with `NSValue`, `NSDictionary`, `NSArray` and
`NSMutableArray` not specifying a `#[repr(...)]`.
* **BREAKING**: `NSObject` is no longer `Send` and `Sync` (because its
subclasses may not be).
## objc2-foundation 0.2.0-alpha.2 - 2021-11-22
### Added
* **BREAKING**: Added associated `Ownership` type to `INSObject` to specify
whether the type can be mutated or not. `NSString` is a prime example of a
type that you may never get a `Owned/&mut` reference to, since it is very
easy to create two `NSString`s with the same underlying allocation.
* Added helper `is_empty` methods.
* Added `INSArray::first_mut` and `INSArray::last_mut`.
### Changed
* **BREAKING**: Renamed a lot of methods to better match Rusts naming scheme:
- `INSArray`
- `count` -> `len`
- `object_at` -> `get`
- `mut_object_at` -> `get_mut`
- `shared_object_at` -> `get_retained`
- `first_object` -> `first`
- `last_object` -> `last`
- `object_enumerator` -> `iter`
- `INSMutableArray`
- `add_object` -> `push`
- `insert_object_at` -> `insert`
- `replace_object_at` -> `replace`
- `remove_object_at` -> `remove`
- `remove_last_object` -> `pop`
- `remove_all_objects` -> `clear`
- `INSDictionary`
- `count` -> `len`
- `object_for` -> `get`
- `key_enumerator` -> `iter_keys`
- `object_enumerator` -> `iter_values`
- `INSValue`
- `value` -> `get`
- `from_value` -> `new`
- `NSComparisonResult`
- `from_ordering` -> `from`
- `as_ordering` -> `into`
- `NSRange`
- `from_range` -> `from`
- `as_range` -> `into`
* Use `SliceId` for better performance when creating arrays and dictionaries.
### Removed
* **BREAKING**: Removed the `object_struct!` macro. It may be re-added in
another form in the future.
* **BREAKING**: Removed `NSMutableSharedArray<T>` and `NSSharedArray<T>` type
aliases. Use `NSMutableArray<T, Shared>` and `NSArray<T, Shared>` instead.
* **BREAKING**: Removed `Any / 'static` bound on `INSObject`. This allows
implementing it for objects that contain lifetimes from the outer scope.
### Fixed
* **BREAKING**: Marked `INS...` traits as `unsafe` to implement.
* **BREAKING**: Removed `new` method from `INSObject` since some classes don't
want this called. It has been re-added to other `INS...` traits on a case by
case basis (in particular not `NSValue`).
* **BREAKING**: `INSString::as_str` now takes an a reference to
`objc2::rc::AutoreleasePool`. This ensure that the returned `&str` is only
used while the current autorelease pool is valid.
* Fixed `NSData::from_vec` on GNUStep.
## objc2-foundation 0.2.0-alpha.1 - 2021-10-28
### Added
* Implement new `RefEncode` trait for objects.
* Implement `Encode` for `NSComparisonResult` and `NSFastEnumerationState`.
* Implement `RefEncode` for objects and `NSFastEnumerationState`.
### Changed
* **BREAKING**: Uses `Id` from `objc2::rc` module instead of `objc_id` crate.
* **BREAKING**: `INSValue::encoding` now returns `&str` instead of `Encoding`.
### Fixed
* Use proper `#[repr(C)]` structs to represent Objective-C objects.
* `INSString::from_str` on GNUStep (`UTF8_ENCODING` was the wrong type).
## objc2-foundation 0.2.0-alpha.0 - 2021-08-29
Note: This is the version that is, as of this writing, available on the
`master` branch in the original `objc-foundation` project.
### Added
* Implement `Display` for `NSString`.
* Make `INSObject::class` faster using the `objc::class!` macro.
### Changed
* **BREAKING**: Forked the project, the crate name is now `objc2-foundation`.
### Fixed
* Fixed types in various calls to `objc::msg_send!` for better verification.
## objc-foundation [0.1.1] - 2016-06-19
### Fixed
* An issue with passing functions (instead of function pointers) in
`INSMutableArray::sort_by`.
## objc-foundation [0.1.0] - 2016-03-20
### Changed
* Update `objc` to `v0.2`.
* Update `objc_id` to `v0.1`.
## objc-foundation [0.0.4] - 2015-12-09
### Removed
* `libc` dependency.
## objc-foundation [0.0.3] - 2015-11-07
### Added
* `object_struct!` macro.
### Changed
* `libc` version can both be `0.1` and `0.2`.
## objc-foundation [0.0.2] - 2015-09-03
### Added
* `Any` bound on `INSObject`, because of a change in `objc` `v0.1.6`.
## objc-foundation [0.0.1] - 2015-06-13
Initial release.
[0.1.1]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.1.0...objc-foundation-0.1.1
[0.1.0]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.0.4...objc-foundation-0.1.0
[0.0.4]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.0.3...objc-foundation-0.0.4
[0.0.3]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.0.2...objc-foundation-0.0.3
[0.0.2]: https://github.com/madsmtm/objc2/compare/objc-foundation-0.0.1...objc-foundation-0.0.2
[0.0.1]: https://github.com/madsmtm/objc2/releases/tag/objc-foundation-0.0.1

View File

@@ -0,0 +1,32 @@
# About the generated framework crates
The `objc2` project provides (mostly) autogenerated interfaces to Apple's
Objective-C frameworks like AppKit, Foundation, Metal, WebKit, and so on.
The bindings currently contain very little documentation, you should view
[Apple's developer documentation][apple-doc-index] for detailed
information about each API. (There are [plans][#309] for importing that
documentation here).
These crates uses `objc2` to declare the external interface to the
Objective-C classes and protocols. It is highly recommended that you read
the documentation here for details on how the Objective-C interop works.
They also use `block2::Block` in the public API, check out the documentation
for the [`block2`] crate for how to call such methods using a closure.
[apple-doc-index]: https://developer.apple.com/documentation/technologies
[#309]: https://github.com/madsmtm/objc2/issues/309
[`block2`]: https://docs.rs/block2
## Currently supported versions
- macOS: `10.12-14.5`
- iOS/iPadOS: `10.0-17.5` (WIP)
- tvOS: `10.0-17.5` (WIP)
- watchOS: `5.0-10.5` (WIP)
- visionOS: `1.0-1.2` (WIP)
These bindings are currently generated from the SDKs in Xcode 15.4.
The Xcode version will be periodically updated.

View File

@@ -0,0 +1,43 @@
# Cargo features in framework crates
Each framework crate has a set of Cargo features that control which parts of
it that is enabled. These are split into two categories; file and dependency
features.
This is quite important for compilation speed, but if you don't want to bother
with it, such as when just starting a new project and experimenting or when
running an example, use the `"all"` feature.
## File features
Each framework C header corresponds to one Cargo feature, and everything that
was declared inside of that header is locked behind that Cargo feature.
As an example, let's use `MetalKit`. This framework has four public C headers,
`MTKDefines.h`, `MTKModel.h`, `MTKTextureLoader.h` and `MTKView.h`. This in
turn means we get four Cargo features in `objc2-metal-kit`, `MTKDefines`,
`MTKModel`, `MTKTextureLoader` and `MTKView`, that enables the functionality
exposed by each of those headers, as well as any required dependency features
(e.g. `MTKModel.h` uses `MTLDevice`, so `objc2-metal/MTLDevice` is enabled for
you).
## Dependency features
As you can see above, frameworks rarely stand alone, instead they often have
some sort of dependency on other frameworks. Some of these dependencies are
considered required, and enabled by default (often `objc2-foundation`), while
for some it makes sense to allow control of whether the dependency is enabled.
Let's keep using `MetalKit` as the example. By default, `objc2-metal-kit` will
import the dependencies `objc2`, `objc2-foundation` and `objc2-metal`, since
those are central to how the `MetalKit` works.
But it also has an optional dependency on `objc2-app-kit` and `block2`, since
those are not always required; in this case, `objc2-app-kit` is only needed if
you want to use the `MTKView` class, so it would be wasteful to enable the
dependency if you didn't need that class.
Such optional dependencies can be enabled with Cargo features of the same name
as the dependency.

View File

@@ -0,0 +1,23 @@
# Use of `Deref`
Framework crates uses the [`Deref`] trait in a bit special way: All objects
deref to their superclasses. For example, `NSMutableArray` derefs to
`NSArray`, which in turn derefs to `NSObject`.
Note that this is explicitly recommended against in [the
documentation][`Deref`] and [the Rust Design patterns
book][anti-pattern-deref] (see those links for details).
Due to Objective-C objects only ever being accessible behind pointers in
the first place, the problems stated there are less severe, and having the
implementation just means that everything is much nicer when you actually
want to use the objects!
All objects also implement [`AsRef`] and [`AsMut`] to their superclass,
and can be used in [`Retained::into_super`], so if you favour explicit
conversion, that is a possibility too.
[`Deref`]: std::ops::Deref
[`ClassType`]: crate::ClassType
[anti-pattern-deref]: https://rust-unofficial.github.io/patterns/anti_patterns/deref.html
[`Retained::into_super`]: crate::rc::Retained::into_super

View File

@@ -0,0 +1,12 @@
# List of framework crates
The following is a full list of currently supported framework crates.
See [#393] if the framework that you need is not here. Note that a lot of
frameworks are CoreFoundation-like, those are more difficult to support, see
[#556] for more information.
[#393]: https://github.com/madsmtm/objc2/issues/393
[#556]: https://github.com/madsmtm/objc2/issues/556
<!-- list_data.md is included here in the final docs -->

View File

@@ -0,0 +1,75 @@
| Framework | Crate | Documentation |
| --- | --- | --- |
| `AVKit` | [![`objc2-av-kit`](https://badgen.net/crates/v/objc2-av-kit)](https://crates.io/crates/objc2-av-kit) | [![docs.rs](https://docs.rs/objc2-av-kit/badge.svg)](https://docs.rs/objc2-av-kit/) |
| `Accessibility` | [![`objc2-accessibility`](https://badgen.net/crates/v/objc2-accessibility)](https://crates.io/crates/objc2-accessibility) | [![docs.rs](https://docs.rs/objc2-accessibility/badge.svg)](https://docs.rs/objc2-accessibility/) |
| `Accounts` | [![`objc2-accounts`](https://badgen.net/crates/v/objc2-accounts)](https://crates.io/crates/objc2-accounts) | [![docs.rs](https://docs.rs/objc2-accounts/badge.svg)](https://docs.rs/objc2-accounts/) |
| `AdServices` | [![`objc2-ad-services`](https://badgen.net/crates/v/objc2-ad-services)](https://crates.io/crates/objc2-ad-services) | [![docs.rs](https://docs.rs/objc2-ad-services/badge.svg)](https://docs.rs/objc2-ad-services/) |
| `AdSupport` | [![`objc2-ad-support`](https://badgen.net/crates/v/objc2-ad-support)](https://crates.io/crates/objc2-ad-support) | [![docs.rs](https://docs.rs/objc2-ad-support/badge.svg)](https://docs.rs/objc2-ad-support/) |
| `AppKit` | [![`objc2-app-kit`](https://badgen.net/crates/v/objc2-app-kit)](https://crates.io/crates/objc2-app-kit) | [![docs.rs](https://docs.rs/objc2-app-kit/badge.svg)](https://docs.rs/objc2-app-kit/) |
| `AppTrackingTransparency` | [![`objc2-app-tracking-transparency`](https://badgen.net/crates/v/objc2-app-tracking-transparency)](https://crates.io/crates/objc2-app-tracking-transparency) | [![docs.rs](https://docs.rs/objc2-app-tracking-transparency/badge.svg)](https://docs.rs/objc2-app-tracking-transparency/) |
| `AuthenticationServices` | [![`objc2-authentication-services`](https://badgen.net/crates/v/objc2-authentication-services)](https://crates.io/crates/objc2-authentication-services) | [![docs.rs](https://docs.rs/objc2-authentication-services/badge.svg)](https://docs.rs/objc2-authentication-services/) |
| `AutomaticAssessmentConfiguration` | [![`objc2-automatic-assessment-configuration`](https://badgen.net/crates/v/objc2-automatic-assessment-configuration)](https://crates.io/crates/objc2-automatic-assessment-configuration) | [![docs.rs](https://docs.rs/objc2-automatic-assessment-configuration/badge.svg)](https://docs.rs/objc2-automatic-assessment-configuration/) |
| `Automator` | [![`objc2-automator`](https://badgen.net/crates/v/objc2-automator)](https://crates.io/crates/objc2-automator) | [![docs.rs](https://docs.rs/objc2-automator/badge.svg)](https://docs.rs/objc2-automator/) |
| `BackgroundAssets` | [![`objc2-background-assets`](https://badgen.net/crates/v/objc2-background-assets)](https://crates.io/crates/objc2-background-assets) | [![docs.rs](https://docs.rs/objc2-background-assets/badge.svg)](https://docs.rs/objc2-background-assets/) |
| `BackgroundTasks` | [![`objc2-background-tasks`](https://badgen.net/crates/v/objc2-background-tasks)](https://crates.io/crates/objc2-background-tasks) | [![docs.rs](https://docs.rs/objc2-background-tasks/badge.svg)](https://docs.rs/objc2-background-tasks/) |
| `BusinessChat` | [![`objc2-business-chat`](https://badgen.net/crates/v/objc2-business-chat)](https://crates.io/crates/objc2-business-chat) | [![docs.rs](https://docs.rs/objc2-business-chat/badge.svg)](https://docs.rs/objc2-business-chat/) |
| `CallKit` | [![`objc2-call-kit`](https://badgen.net/crates/v/objc2-call-kit)](https://crates.io/crates/objc2-call-kit) | [![docs.rs](https://docs.rs/objc2-call-kit/badge.svg)](https://docs.rs/objc2-call-kit/) |
| `ClassKit` | [![`objc2-class-kit`](https://badgen.net/crates/v/objc2-class-kit)](https://crates.io/crates/objc2-class-kit) | [![docs.rs](https://docs.rs/objc2-class-kit/badge.svg)](https://docs.rs/objc2-class-kit/) |
| `CloudKit` | [![`objc2-cloud-kit`](https://badgen.net/crates/v/objc2-cloud-kit)](https://crates.io/crates/objc2-cloud-kit) | [![docs.rs](https://docs.rs/objc2-cloud-kit/badge.svg)](https://docs.rs/objc2-cloud-kit/) |
| `Contacts` | [![`objc2-contacts`](https://badgen.net/crates/v/objc2-contacts)](https://crates.io/crates/objc2-contacts) | [![docs.rs](https://docs.rs/objc2-contacts/badge.svg)](https://docs.rs/objc2-contacts/) |
| `ContactsUI` | [![`objc2-contacts-ui`](https://badgen.net/crates/v/objc2-contacts-ui)](https://crates.io/crates/objc2-contacts-ui) | [![docs.rs](https://docs.rs/objc2-contacts-ui/badge.svg)](https://docs.rs/objc2-contacts-ui/) |
| `CoreBluetooth` | [![`objc2-core-bluetooth`](https://badgen.net/crates/v/objc2-core-bluetooth)](https://crates.io/crates/objc2-core-bluetooth) | [![docs.rs](https://docs.rs/objc2-core-bluetooth/badge.svg)](https://docs.rs/objc2-core-bluetooth/) |
| `CoreData` | [![`objc2-core-data`](https://badgen.net/crates/v/objc2-core-data)](https://crates.io/crates/objc2-core-data) | [![docs.rs](https://docs.rs/objc2-core-data/badge.svg)](https://docs.rs/objc2-core-data/) |
| `CoreImage` | [![`objc2-core-image`](https://badgen.net/crates/v/objc2-core-image)](https://crates.io/crates/objc2-core-image) | [![docs.rs](https://docs.rs/objc2-core-image/badge.svg)](https://docs.rs/objc2-core-image/) |
| `CoreLocation` | [![`objc2-core-location`](https://badgen.net/crates/v/objc2-core-location)](https://crates.io/crates/objc2-core-location) | [![docs.rs](https://docs.rs/objc2-core-location/badge.svg)](https://docs.rs/objc2-core-location/) |
| `CoreML` | [![`objc2-core-ml`](https://badgen.net/crates/v/objc2-core-ml)](https://crates.io/crates/objc2-core-ml) | [![docs.rs](https://docs.rs/objc2-core-ml/badge.svg)](https://docs.rs/objc2-core-ml/) |
| `CoreMotion` | [![`objc2-core-motion`](https://badgen.net/crates/v/objc2-core-motion)](https://crates.io/crates/objc2-core-motion) | [![docs.rs](https://docs.rs/objc2-core-motion/badge.svg)](https://docs.rs/objc2-core-motion/) |
| `CoreWLAN` | [![`objc2-core-wlan`](https://badgen.net/crates/v/objc2-core-wlan)](https://crates.io/crates/objc2-core-wlan) | [![docs.rs](https://docs.rs/objc2-core-wlan/badge.svg)](https://docs.rs/objc2-core-wlan/) |
| `DataDetection` | [![`objc2-data-detection`](https://badgen.net/crates/v/objc2-data-detection)](https://crates.io/crates/objc2-data-detection) | [![docs.rs](https://docs.rs/objc2-data-detection/badge.svg)](https://docs.rs/objc2-data-detection/) |
| `DeviceCheck` | [![`objc2-device-check`](https://badgen.net/crates/v/objc2-device-check)](https://crates.io/crates/objc2-device-check) | [![docs.rs](https://docs.rs/objc2-device-check/badge.svg)](https://docs.rs/objc2-device-check/) |
| `EventKit` | [![`objc2-event-kit`](https://badgen.net/crates/v/objc2-event-kit)](https://crates.io/crates/objc2-event-kit) | [![docs.rs](https://docs.rs/objc2-event-kit/badge.svg)](https://docs.rs/objc2-event-kit/) |
| `ExceptionHandling` | [![`objc2-exception-handling`](https://badgen.net/crates/v/objc2-exception-handling)](https://crates.io/crates/objc2-exception-handling) | [![docs.rs](https://docs.rs/objc2-exception-handling/badge.svg)](https://docs.rs/objc2-exception-handling/) |
| `ExtensionKit` | [![`objc2-extension-kit`](https://badgen.net/crates/v/objc2-extension-kit)](https://crates.io/crates/objc2-extension-kit) | [![docs.rs](https://docs.rs/objc2-extension-kit/badge.svg)](https://docs.rs/objc2-extension-kit/) |
| `ExternalAccessory` | [![`objc2-external-accessory`](https://badgen.net/crates/v/objc2-external-accessory)](https://crates.io/crates/objc2-external-accessory) | [![docs.rs](https://docs.rs/objc2-external-accessory/badge.svg)](https://docs.rs/objc2-external-accessory/) |
| `FileProvider` | [![`objc2-file-provider`](https://badgen.net/crates/v/objc2-file-provider)](https://crates.io/crates/objc2-file-provider) | [![docs.rs](https://docs.rs/objc2-file-provider/badge.svg)](https://docs.rs/objc2-file-provider/) |
| `FileProviderUI` | [![`objc2-file-provider-ui`](https://badgen.net/crates/v/objc2-file-provider-ui)](https://crates.io/crates/objc2-file-provider-ui) | [![docs.rs](https://docs.rs/objc2-file-provider-ui/badge.svg)](https://docs.rs/objc2-file-provider-ui/) |
| `FinderSync` | [![`objc2-finder-sync`](https://badgen.net/crates/v/objc2-finder-sync)](https://crates.io/crates/objc2-finder-sync) | [![docs.rs](https://docs.rs/objc2-finder-sync/badge.svg)](https://docs.rs/objc2-finder-sync/) |
| `Foundation` | [![`objc2-foundation`](https://badgen.net/crates/v/objc2-foundation)](https://crates.io/crates/objc2-foundation) | [![docs.rs](https://docs.rs/objc2-foundation/badge.svg)](https://docs.rs/objc2-foundation/) |
| `GameController` | [![`objc2-game-controller`](https://badgen.net/crates/v/objc2-game-controller)](https://crates.io/crates/objc2-game-controller) | [![docs.rs](https://docs.rs/objc2-game-controller/badge.svg)](https://docs.rs/objc2-game-controller/) |
| `GameKit` | [![`objc2-game-kit`](https://badgen.net/crates/v/objc2-game-kit)](https://crates.io/crates/objc2-game-kit) | [![docs.rs](https://docs.rs/objc2-game-kit/badge.svg)](https://docs.rs/objc2-game-kit/) |
| `HealthKit` | [![`objc2-health-kit`](https://badgen.net/crates/v/objc2-health-kit)](https://crates.io/crates/objc2-health-kit) | [![docs.rs](https://docs.rs/objc2-health-kit/badge.svg)](https://docs.rs/objc2-health-kit/) |
| `IdentityLookup` | [![`objc2-identity-lookup`](https://badgen.net/crates/v/objc2-identity-lookup)](https://crates.io/crates/objc2-identity-lookup) | [![docs.rs](https://docs.rs/objc2-identity-lookup/badge.svg)](https://docs.rs/objc2-identity-lookup/) |
| `InputMethodKit` | [![`objc2-input-method-kit`](https://badgen.net/crates/v/objc2-input-method-kit)](https://crates.io/crates/objc2-input-method-kit) | [![docs.rs](https://docs.rs/objc2-input-method-kit/badge.svg)](https://docs.rs/objc2-input-method-kit/) |
| `LinkPresentation` | [![`objc2-link-presentation`](https://badgen.net/crates/v/objc2-link-presentation)](https://crates.io/crates/objc2-link-presentation) | [![docs.rs](https://docs.rs/objc2-link-presentation/badge.svg)](https://docs.rs/objc2-link-presentation/) |
| `LocalAuthentication` | [![`objc2-local-authentication`](https://badgen.net/crates/v/objc2-local-authentication)](https://crates.io/crates/objc2-local-authentication) | [![docs.rs](https://docs.rs/objc2-local-authentication/badge.svg)](https://docs.rs/objc2-local-authentication/) |
| `LocalAuthenticationEmbeddedUI` | [![`objc2-local-authentication-embedded-ui`](https://badgen.net/crates/v/objc2-local-authentication-embedded-ui)](https://crates.io/crates/objc2-local-authentication-embedded-ui) | [![docs.rs](https://docs.rs/objc2-local-authentication-embedded-ui/badge.svg)](https://docs.rs/objc2-local-authentication-embedded-ui/) |
| `MLCompute` | [![`objc2-ml-compute`](https://badgen.net/crates/v/objc2-ml-compute)](https://crates.io/crates/objc2-ml-compute) | [![docs.rs](https://docs.rs/objc2-ml-compute/badge.svg)](https://docs.rs/objc2-ml-compute/) |
| `MailKit` | [![`objc2-mail-kit`](https://badgen.net/crates/v/objc2-mail-kit)](https://crates.io/crates/objc2-mail-kit) | [![docs.rs](https://docs.rs/objc2-mail-kit/badge.svg)](https://docs.rs/objc2-mail-kit/) |
| `MapKit` | [![`objc2-map-kit`](https://badgen.net/crates/v/objc2-map-kit)](https://crates.io/crates/objc2-map-kit) | [![docs.rs](https://docs.rs/objc2-map-kit/badge.svg)](https://docs.rs/objc2-map-kit/) |
| `MediaPlayer` | [![`objc2-media-player`](https://badgen.net/crates/v/objc2-media-player)](https://crates.io/crates/objc2-media-player) | [![docs.rs](https://docs.rs/objc2-media-player/badge.svg)](https://docs.rs/objc2-media-player/) |
| `Metal` | [![`objc2-metal`](https://badgen.net/crates/v/objc2-metal)](https://crates.io/crates/objc2-metal) | [![docs.rs](https://docs.rs/objc2-metal/badge.svg)](https://docs.rs/objc2-metal/) |
| `MetalFX` | [![`objc2-metal-fx`](https://badgen.net/crates/v/objc2-metal-fx)](https://crates.io/crates/objc2-metal-fx) | [![docs.rs](https://docs.rs/objc2-metal-fx/badge.svg)](https://docs.rs/objc2-metal-fx/) |
| `MetalKit` | [![`objc2-metal-kit`](https://badgen.net/crates/v/objc2-metal-kit)](https://crates.io/crates/objc2-metal-kit) | [![docs.rs](https://docs.rs/objc2-metal-kit/badge.svg)](https://docs.rs/objc2-metal-kit/) |
| `MetricKit` | [![`objc2-metric-kit`](https://badgen.net/crates/v/objc2-metric-kit)](https://crates.io/crates/objc2-metric-kit) | [![docs.rs](https://docs.rs/objc2-metric-kit/badge.svg)](https://docs.rs/objc2-metric-kit/) |
| `MultipeerConnectivity` | [![`objc2-multipeer-connectivity`](https://badgen.net/crates/v/objc2-multipeer-connectivity)](https://crates.io/crates/objc2-multipeer-connectivity) | [![docs.rs](https://docs.rs/objc2-multipeer-connectivity/badge.svg)](https://docs.rs/objc2-multipeer-connectivity/) |
| `NaturalLanguage` | [![`objc2-natural-language`](https://badgen.net/crates/v/objc2-natural-language)](https://crates.io/crates/objc2-natural-language) | [![docs.rs](https://docs.rs/objc2-natural-language/badge.svg)](https://docs.rs/objc2-natural-language/) |
| `NearbyInteraction` | [![`objc2-nearby-interaction`](https://badgen.net/crates/v/objc2-nearby-interaction)](https://crates.io/crates/objc2-nearby-interaction) | [![docs.rs](https://docs.rs/objc2-nearby-interaction/badge.svg)](https://docs.rs/objc2-nearby-interaction/) |
| `NetworkExtension` | [![`objc2-network-extension`](https://badgen.net/crates/v/objc2-network-extension)](https://crates.io/crates/objc2-network-extension) | [![docs.rs](https://docs.rs/objc2-network-extension/badge.svg)](https://docs.rs/objc2-network-extension/) |
| `OSAKit` | [![`objc2-osa-kit`](https://badgen.net/crates/v/objc2-osa-kit)](https://crates.io/crates/objc2-osa-kit) | [![docs.rs](https://docs.rs/objc2-osa-kit/badge.svg)](https://docs.rs/objc2-osa-kit/) |
| `Photos` | [![`objc2-photos`](https://badgen.net/crates/v/objc2-photos)](https://crates.io/crates/objc2-photos) | [![docs.rs](https://docs.rs/objc2-photos/badge.svg)](https://docs.rs/objc2-photos/) |
| `PhotosUI` | [![`objc2-photos-ui`](https://badgen.net/crates/v/objc2-photos-ui)](https://crates.io/crates/objc2-photos-ui) | [![docs.rs](https://docs.rs/objc2-photos-ui/badge.svg)](https://docs.rs/objc2-photos-ui/) |
| `QuartzCore` | [![`objc2-quartz-core`](https://badgen.net/crates/v/objc2-quartz-core)](https://crates.io/crates/objc2-quartz-core) | [![docs.rs](https://docs.rs/objc2-quartz-core/badge.svg)](https://docs.rs/objc2-quartz-core/) |
| `ScreenCaptureKit` | [![`objc2-screen-capture-kit`](https://badgen.net/crates/v/objc2-screen-capture-kit)](https://crates.io/crates/objc2-screen-capture-kit) | [![docs.rs](https://docs.rs/objc2-screen-capture-kit/badge.svg)](https://docs.rs/objc2-screen-capture-kit/) |
| `SensitiveContentAnalysis` | [![`objc2-sensitive-content-analysis`](https://badgen.net/crates/v/objc2-sensitive-content-analysis)](https://crates.io/crates/objc2-sensitive-content-analysis) | [![docs.rs](https://docs.rs/objc2-sensitive-content-analysis/badge.svg)](https://docs.rs/objc2-sensitive-content-analysis/) |
| `ServiceManagement` | [![`objc2-service-management`](https://badgen.net/crates/v/objc2-service-management)](https://crates.io/crates/objc2-service-management) | [![docs.rs](https://docs.rs/objc2-service-management/badge.svg)](https://docs.rs/objc2-service-management/) |
| `Social` | [![`objc2-social`](https://badgen.net/crates/v/objc2-social)](https://crates.io/crates/objc2-social) | [![docs.rs](https://docs.rs/objc2-social/badge.svg)](https://docs.rs/objc2-social/) |
| `SoundAnalysis` | [![`objc2-sound-analysis`](https://badgen.net/crates/v/objc2-sound-analysis)](https://crates.io/crates/objc2-sound-analysis) | [![docs.rs](https://docs.rs/objc2-sound-analysis/badge.svg)](https://docs.rs/objc2-sound-analysis/) |
| `Speech` | [![`objc2-speech`](https://badgen.net/crates/v/objc2-speech)](https://crates.io/crates/objc2-speech) | [![docs.rs](https://docs.rs/objc2-speech/badge.svg)](https://docs.rs/objc2-speech/) |
| `StoreKit` | [![`objc2-store-kit`](https://badgen.net/crates/v/objc2-store-kit)](https://crates.io/crates/objc2-store-kit) | [![docs.rs](https://docs.rs/objc2-store-kit/badge.svg)](https://docs.rs/objc2-store-kit/) |
| `Symbols` | [![`objc2-symbols`](https://badgen.net/crates/v/objc2-symbols)](https://crates.io/crates/objc2-symbols) | [![docs.rs](https://docs.rs/objc2-symbols/badge.svg)](https://docs.rs/objc2-symbols/) |
| `UIKit` | [![`objc2-ui-kit`](https://badgen.net/crates/v/objc2-ui-kit)](https://crates.io/crates/objc2-ui-kit) | [![docs.rs](https://docs.rs/objc2-ui-kit/badge.svg)](https://docs.rs/objc2-ui-kit/) |
| `UniformTypeIdentifiers` | [![`objc2-uniform-type-identifiers`](https://badgen.net/crates/v/objc2-uniform-type-identifiers)](https://crates.io/crates/objc2-uniform-type-identifiers) | [![docs.rs](https://docs.rs/objc2-uniform-type-identifiers/badge.svg)](https://docs.rs/objc2-uniform-type-identifiers/) |
| `UserNotifications` | [![`objc2-user-notifications`](https://badgen.net/crates/v/objc2-user-notifications)](https://crates.io/crates/objc2-user-notifications) | [![docs.rs](https://docs.rs/objc2-user-notifications/badge.svg)](https://docs.rs/objc2-user-notifications/) |
| `Virtualization` | [![`objc2-virtualization`](https://badgen.net/crates/v/objc2-virtualization)](https://crates.io/crates/objc2-virtualization) | [![docs.rs](https://docs.rs/objc2-virtualization/badge.svg)](https://docs.rs/objc2-virtualization/) |
| `Vision` | [![`objc2-vision`](https://badgen.net/crates/v/objc2-vision)](https://crates.io/crates/objc2-vision) | [![docs.rs](https://docs.rs/objc2-vision/badge.svg)](https://docs.rs/objc2-vision/) |
| `WebKit` | [![`objc2-web-kit`](https://badgen.net/crates/v/objc2-web-kit)](https://crates.io/crates/objc2-web-kit) | [![docs.rs](https://docs.rs/objc2-web-kit/badge.svg)](https://docs.rs/objc2-web-kit/) |

View File

@@ -0,0 +1,14 @@
#![doc = include_str!("README.md")]
#[doc = include_str!("deref.md")]
pub mod deref {}
#[doc = include_str!("list.md")]
#[doc = include_str!("list_data.md")]
pub mod list {}
#[doc = include_str!("CHANGELOG.md")]
pub mod changelog {}
#[doc = include_str!("cargo_features.md")]
pub mod cargo_features {}

View File

@@ -0,0 +1,139 @@
# Interop with `core-foundation`-like crates
The `objc2` project does not [yet](https://github.com/madsmtm/objc2/issues/556) provide bindings to CoreFoundation and similar frameworks.
To interact with these, you will have to use existing crates like [`core-foundation`], [`core-graphics`], [`security-framework`], [`system-configuration`] and so on.
This can, however, pose a bit of an issue, since `objc2` and [`block2`] impose certain requirements on the types involved.
[`core-foundation`]: https://crates.io/crates/core-foundation
[`core-graphics`]: https://crates.io/crates/core-graphics
[`security-framework`]: https://crates.io/crates/security-framework
[`system-configuration`]: https://crates.io/crates/system-configuration
[`block2`]: https://docs.rs/block2/latest/block2/
## Implementing `Encode` for a newtype wrapper
Due to Rust's orphan rules, `core-foundation` types do not implement `Encode`, and as such cannot be passed to/from methods and in blocks by default.
We're (slowly) working on fixing this, see [servo/core-foundation-rs#628], but in the meantime, you can use a newtype wrapper around the `CF...Ref`, and implement [`Encode`] for that wrapper.
[servo/core-foundation-rs#628]: https://github.com/servo/core-foundation-rs/pull/628
[`Encode`]: crate::encode::Encode
### Examples
Declaring an external function to [`CFRunLoopObserverCreateWithHandler`](https://developer.apple.com/documentation/corefoundation/1542816-cfrunloopobservercreatewithhandl?language=objc), which uses blocks and `block2`, and as such require its parameters to implement `Encode`.
```rust
use std::ptr;
use block2::{RcBlock, Block};
use core_foundation::base::{Boolean, CFAllocatorRef, CFIndex, CFOptionFlags, TCFType};
use core_foundation::runloop::{CFRunLoopActivity, CFRunLoopObserver, CFRunLoopObserverRef, kCFRunLoopAllActivities};
use objc2::encode::{Encode, Encoding};
#[repr(transparent)]
struct CFRunLoopObserverRefWrapper(CFRunLoopObserverRef);
// SAFETY: `CFRunLoopObserverRefWrapper` is `#[repr(transparent)]` over
// `CFRunLoopObserverRef`, which is a typedef to `struct __CFRunLoopObserver *`.
unsafe impl Encode for CFRunLoopObserverRefWrapper {
const ENCODING: Encoding = Encoding::Pointer(&Encoding::Struct("__CFRunLoopObserver", &[]));
}
extern "C" {
fn CFRunLoopObserverCreateWithHandler(
allocator: CFAllocatorRef,
activities: CFOptionFlags,
repeats: Boolean,
order: CFIndex,
block: &Block<dyn Fn(CFRunLoopObserverRefWrapper, CFRunLoopActivity)>
) -> CFRunLoopObserverRef;
}
let block = RcBlock::new(|observer: CFRunLoopObserverRefWrapper, activity| {
// Extract `CFRunLoopObserverRef` from `CFRunLoopObserverRefWrapper`
let observer = observer.0;
});
let observer = unsafe {
CFRunLoopObserver::wrap_under_create_rule(CFRunLoopObserverCreateWithHandler(
ptr::null(),
kCFRunLoopAllActivities,
false as Boolean,
0,
&block,
))
};
#
# assert!(CFRunLoopObserverRefWrapper::ENCODING.equivalent_to_str("^{__CFRunLoopObserver=}"));
```
An alternative, if you don't want to go through the trouble of creating a newtype, is to use the `"relax-void-encoding"` Cargo feature.
Here we set the [`-[CALayer borderColor]`](https://developer.apple.com/documentation/quartzcore/calayer/1410903-bordercolor?language=objc) property (which uses `CGColorRef`).
```rust, ignore
use core_foundation::base::ToVoid;
use core_graphics::color::CGColor;
use objc2_quartz_core::CALayer;
use objc2::msg_send;
fn set_border_color(layer: &CALayer, color: &CGColor) {
let color = color.to_void();
// Passing `*const c_void` here requires the "relax-void-encoding" feature
unsafe { msg_send![layer, setBorderColor: color] }
}
let layer = unsafe { CALayer::new() };
let color = CGColor::rgb(1.0, 0.0, 0.0, 1.0);
set_border_color(&layer, &color);
```
## Toll-free bridging
Certain CoreFoundation types are documented to be ["toll-free bridged"], which means that they're completely interoperable with the Foundation types. To convert between these in Rust, you'll have to cast the pointers, e.g. between `CFStringRef` and `*const NSString`.
["toll-free bridged"]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html
### Example
Toll-free bridging between `CFString` and `NSString`.
```rust
use core_foundation::base::TCFType;
use core_foundation::string::{CFString, CFStringRef};
use objc2_foundation::{NSString, ns_string};
use objc2::rc::Retained;
fn cf_string_to_ns(s: &CFString) -> &NSString {
let ptr: CFStringRef = s.as_concrete_TypeRef();
let ptr: *const NSString = ptr.cast();
// SAFETY: CFString is toll-free bridged with NSString.
unsafe { ptr.as_ref().unwrap() }
}
// Note: `NSString` is currently a bit special, and requires that we convert from
// `Retained<NSString>`, as it could otherwise have come from `&NSMutableString`,
// and then we'd loose lifetime information by converting to `CFString`.
//
// This will be changed in the future, see https://github.com/madsmtm/objc2/issues/563.
fn ns_string_to_cf(s: Retained<NSString>) -> CFString {
// Yield ownership over the string
let ptr: *const NSString = Retained::into_raw(s);
let ptr: CFStringRef = ptr.cast();
// SAFETY: NSString is toll-free bridged with CFString, and
// ownership was passed above with `Retained::into_raw`.
unsafe { CFString::wrap_under_create_rule(ptr) }
}
let cf = CFString::new("foo");
let ns = NSString::from_str("foo");
assert_eq!(cf_string_to_ns(&cf), &*ns);
assert_eq!(cf, ns_string_to_cf(ns));
```

View File

@@ -0,0 +1,255 @@
# Layered Safety
Objective-C is different from Rust<sup>[citation needed]</sup>. In particular,
Rust has a concept of "safety" (see the [nomicon] for details), which
Objective-C completely lacks.
You will find when using the framework crates that basically everything (that
has not been manually audited) is `unsafe`. So you might rightfully ask:
What's the point then? Can't I just use `msg_send!`, and save the extra
dependency?
Yes, you could, but in fact the framework crates are much safer than doing
method calling manually, even though you may end up writing `unsafe` just as
many times. I dub this "layered safety"<sup>1</sup> to capture the fact that
_not all usage of `unsafe` is created equally_!
Simply put, when using an `unsafe` method in e.g. `objc2-foundation`, you have
to ensure the compiler of much fewer things than when doing method calling
manually.
To see why this is the case, let me guide you through the various abstraction
layers that the framework crates and `objc2` provide, and we'll see how each
step makes things safer!
The framework crates are not perfect though, and there may be cases where you
have to drop down into lower-level details; luckily though, the fact that we
have this layered architecture with each step exposed along the way allows you
to do exactly that!
<sup>1: I haven't heard this concept named before, if you know of prior art on this please let me know.</sup>
[citation needed]: https://xkcd.com/285/
[nomicon]: https://doc.rust-lang.org/nomicon/intro.html
## Layer 1: `objc_msgSend`
Unlike C APIs where you define an `extern "C"` function that you want to call,
method calling is done in Objective-C using the "trampoline functions"
[`objc_msgSend`], `objc_msgSend_stret`, `objc_msgSend_fpret` and so on.
Which of these is correct depends on the target architecture and the calling
convention. Furthermore, to use these you first have to cast them to the
correct function signature using `mem::transmute`.
This is actually what's done [in the standard library][std-objc], since they
need to do it so rarely, and the extra dependency on a crate wouldn't be worth
the cost.
[`objc_msgSend`]: crate::ffi::objc_msgSend
[std-objc]: https://github.com/rust-lang/rust/blob/aa0189170057a6b56f445f05b9840caf6f260212/library/std/src/sys/unix/args.rs#L196-L248
### Example
Doing the Rust equivalent of Objective-C's `NSUInteger hash_code = [obj hash];`.
```rust, ignore
# // Fails with `unstable-c-unwind`, so disabled for now.
use std::mem::transmute;
use std::ffi::c_char;
use objc2::ffi::{objc_object, objc_msgSend, sel_registerName, NSUInteger, SEL};
let obj: *const objc_object;
# let obj = &*objc2::runtime::NSObject::new() as *const objc2::runtime::NSObject as *const _;
let sel = unsafe { sel_registerName(b"hash\0".as_ptr() as *const c_char) };
let msg_send_fn = unsafe {
transmute::<
unsafe extern "C" fn(),
unsafe extern "C" fn(*const objc_object, SEL) -> NSUInteger,
>(objc_msgSend)
};
let hash_code = unsafe { msg_send_fn(obj, sel) };
```
## Layer 2: `MessageReceiver`
We can improve on this using [`MessageReceiver::send_message`], which
abstracts away the calling convention details, as well as adding an `Encode`
bound on all the involved types. This ensures that we don't accidentally try
to pass e.g. a `Vec<T>`, which does not have a stable memory layout.
Additionally, when `debug_assertions` are enabled, the types involved in the
message send are compared to the types exposed in the Objective-C runtime.
This cannot catch mistakes like passing `null` where a non-null object was
expected, but it helps a lot with accidentally passing a `&c_int` where `int`
was expected.
[`MessageReceiver::send_message`]: crate::runtime::MessageReceiver::send_message
### Example
We'll reuse the `hash` example from above again.
```rust
use objc2::ffi::NSUInteger;
use objc2::runtime::{MessageReceiver, NSObject, Sel};
let obj: &NSObject;
# let obj = &*objc2::runtime::NSObject::new();
let sel = Sel::register("hash");
let hash_code: NSUInteger = unsafe {
MessageReceiver::send_message(obj, sel, ())
};
```
## Layer 3a: `msg_send!`
Introducing macros: [`msg_send!`] can abstract away the tediousness of writing
the selector expression, as well as ensuring that the number of arguments to
the method is correct. It also handles details surrounding Objective-C's
`BOOL` type.
[`msg_send!`]: crate::msg_send
### Examples
The `hash` example again.
```rust
use objc2::ffi::NSUInteger;
use objc2::runtime::NSObject;
use objc2::msg_send;
let obj: &NSObject;
# let obj = &*objc2::runtime::NSObject::new();
let hash_code: NSUInteger = unsafe { msg_send![obj, hash] };
```
That example is now pretty close to as minimal as it gets, so let's introduce
something more complex; creating and using an instance of [`NSData`].
```rust
use objc2::ffi::NSUInteger;
use objc2::runtime::NSObject;
use objc2::{class, msg_send};
let obj: *const NSObject = unsafe { msg_send![class!(NSData), new] };
let length: NSUInteger = unsafe { msg_send![obj, length] };
// We have to specify the return type here, see layer 4 below
let _: () = unsafe { msg_send![obj, release] };
```
[`NSData`]: https://developer.apple.com/documentation/foundation/nsdata?language=objc
## Layer 3b: `msg_send_id!`
As you can see in the new example involving `NSData`, it can be quite tedious
to remember the `release` call when you're done with the object. Furthermore,
whether you need to `retain` and `release` the object involves subtle rules
that depend on the name of the method!
Objective-C solved this years ago with the introduction of "ARC". Similarly,
we can solve this with [`msg_send_id!`] and the smart pointer [`rc::Retained`],
which work together to ensure that the memory management of the object is done
correctly.
[`msg_send_id!`]: crate::msg_send_id
[`rc::Retained`]: crate::rc::Retained
### Example
The `NSData` example again.
```rust
use objc2::ffi::NSUInteger;
use objc2::rc::Retained;
use objc2::runtime::NSObject;
use objc2::{class, msg_send, msg_send_id};
let obj: Retained<NSObject> = unsafe { msg_send_id![class!(NSData), new] };
let length: NSUInteger = unsafe { msg_send![&obj, length] };
// `obj` goes out of scope, `release` is automatically sent to the object
```
## Layer 4: `extern_x` macros
There's still a problem with the above: we can't actually make a reusable
`hash` nor `length` function, since `NSObject` can refer to any object, and
all objects do not actually respond to that method.
To help with this, we have the [`extern_class!`] macro, which define a new
type resembling `NSObject`, but which represents the `NSData` class instead.
This allows us to make a completely safe API for downstream users!
Along with this, we can now use the [`extern_methods!`] macro to help with
defining our methods, which is also a big improvement over the `msg_send!` /
`msg_send_id!` macros, since it allows us to directly "see" the types, instead
of having them work by type-inference.
[`extern_class!`]: crate::extern_class
[`extern_methods!`]: crate::extern_methods
### Example
The `NSData` example again.
```rust
use objc2::ffi::NSUInteger;
use objc2::rc::Retained;
use objc2::runtime::NSObject;
use objc2::{extern_class, extern_methods, mutability, ClassType};
extern_class!(
#[derive(PartialEq, Eq, Hash)]
pub struct NSData;
unsafe impl ClassType for NSData {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl NSData {
#[method_id(new)]
pub fn new() -> Retained<Self>;
#[method(length)]
pub fn length(&self) -> NSUInteger;
}
);
let obj = NSData::new();
let length = obj.length();
```
## Layer 5: Framework crates
Apple has a _lot_ of Objective-C code, and manually defining an interface to
all of it would take a lifetime. Especially keeping track of which methods are
nullable, and which are not, is difficult.
Instead, we can autogenerate the above definition from the headers directly
using type information exposed by `clang`, giving us a very high confidence
that it is correct!
### Example
The `NSData` example again.
```rust, ignore
use objc2_foundation::NSData;
let obj = NSData::new();
let length = obj.length();
```

12
vendor/objc2/src/topics/mod.rs vendored Normal file
View File

@@ -0,0 +1,12 @@
//! Various explanations and topics of discussion.
pub mod about_generated;
#[cfg(not(feature = "gnustep-1-7"))]
#[doc = include_str!("core_foundation_interop.md")]
pub mod core_foundation_interop {}
#[doc = include_str!("layered_safety.md")]
pub mod layered_safety {}
#[cfg(not(doctest))]
#[doc = include_str!("../../CHANGELOG.md")]
pub mod changelog {}

285
vendor/objc2/src/verify.rs vendored Normal file
View File

@@ -0,0 +1,285 @@
use core::fmt;
use core::hash::Hash;
use std::error::Error;
use crate::encode::{Encoding, EncodingBox};
use crate::runtime::{EncodingParseError, Method};
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) enum Inner {
MethodNotFound,
EncodingParseError(EncodingParseError),
MismatchedReturn(EncodingBox, Encoding),
MismatchedArgumentsCount(usize, usize),
MismatchedArgument(usize, EncodingBox, Encoding),
}
impl fmt::Display for Inner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MethodNotFound => write!(f, "method not found"),
Self::EncodingParseError(e) => write!(f, "{e}"),
Self::MismatchedReturn(expected, actual) => {
write!(
f,
"expected return to have type code '{expected}', but found '{actual}'",
)
}
Self::MismatchedArgumentsCount(expected, actual) => {
write!(f, "expected {expected} arguments, but {actual} were given",)
}
Self::MismatchedArgument(i, expected, actual) => {
write!(
f,
"expected argument at index {i} to have type code '{expected}', but found '{actual}'",
)
}
}
}
}
/// Failed verifying selector on a class.
///
/// This is returned in the error case of [`AnyClass::verify_sel`], see that
/// for details.
///
/// This implements [`Error`], and a description of the error can be retrieved
/// using [`fmt::Display`].
///
/// [`AnyClass::verify_sel`]: crate::runtime::AnyClass::verify_sel
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct VerificationError(Inner);
impl From<EncodingParseError> for VerificationError {
fn from(e: EncodingParseError) -> Self {
Self(Inner::EncodingParseError(e))
}
}
impl From<Inner> for VerificationError {
fn from(inner: Inner) -> Self {
Self(inner)
}
}
impl fmt::Display for VerificationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Delegate to inner
fmt::Display::fmt(&self.0, f)
}
}
impl Error for VerificationError {}
/// Relaxed version of `Encoding::equivalent_to_box` that allows
/// `*mut c_void` and `*const c_void` to be used in place of other pointers,
/// and allows signed types where unsigned types are excepted.
///
/// Note: This is a top-level comparison; `*mut *mut c_void` or structures
/// containing `*mut c_void` are not allowed differently than usual.
fn relaxed_equivalent_to_box(encoding: &Encoding, expected: &EncodingBox) -> bool {
if cfg!(feature = "relax-void-encoding")
&& matches!(encoding, Encoding::Pointer(&Encoding::Void))
&& matches!(expected, EncodingBox::Pointer(_))
{
return true;
}
if cfg!(feature = "relax-sign-encoding") {
let actual_signed = match encoding {
Encoding::UChar => &Encoding::Char,
Encoding::UShort => &Encoding::Short,
Encoding::UInt => &Encoding::Int,
Encoding::ULong => &Encoding::Long,
Encoding::ULongLong => &Encoding::LongLong,
enc => enc,
};
let expected_signed = match expected {
EncodingBox::UChar => &EncodingBox::Char,
EncodingBox::UShort => &EncodingBox::Short,
EncodingBox::UInt => &EncodingBox::Int,
EncodingBox::ULong => &EncodingBox::Long,
EncodingBox::ULongLong => &EncodingBox::LongLong,
enc => enc,
};
if actual_signed == expected_signed {
return true;
}
}
encoding.equivalent_to_box(expected)
}
pub(crate) fn verify_method_signature(
method: &Method,
args: &[Encoding],
ret: &Encoding,
) -> Result<(), VerificationError> {
let mut iter = method.types();
// TODO: Verify stack layout
let (expected, _stack_layout) = iter.extract_return()?;
if !relaxed_equivalent_to_box(ret, &expected) {
return Err(Inner::MismatchedReturn(expected, ret.clone()).into());
}
iter.verify_receiver()?;
iter.verify_sel()?;
let actual_count = args.len();
for (i, actual) in args.iter().enumerate() {
if let Some(res) = iter.next() {
// TODO: Verify stack layout
let (expected, _stack_layout) = res?;
if !relaxed_equivalent_to_box(actual, &expected) {
return Err(Inner::MismatchedArgument(i, expected, actual.clone()).into());
}
} else {
return Err(Inner::MismatchedArgumentsCount(i, actual_count).into());
}
}
let remaining = iter.count();
if remaining != 0 {
return Err(Inner::MismatchedArgumentsCount(actual_count + remaining, actual_count).into());
}
let expected_count = method.name().number_of_arguments();
if expected_count != actual_count {
return Err(Inner::MismatchedArgumentsCount(expected_count, actual_count).into());
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ffi;
use crate::runtime::Sel;
use crate::test_utils;
use crate::{msg_send, sel};
use alloc::string::ToString;
use core::ffi::c_void;
use core::panic::{RefUnwindSafe, UnwindSafe};
#[test]
fn test_verify_message() {
let cls = test_utils::custom_class();
assert!(cls.verify_sel::<(), u32>(sel!(foo)).is_ok());
assert!(cls.verify_sel::<(u32,), ()>(sel!(setFoo:)).is_ok());
let metaclass = cls.metaclass();
metaclass
.verify_sel::<(i32, i32), i32>(sel!(addNumber:toNumber:))
.unwrap();
}
#[test]
fn test_verify_message_errors() {
let cls = test_utils::custom_class();
// Unimplemented selector (missing colon)
let err = cls.verify_sel::<(), ()>(sel!(setFoo)).unwrap_err();
assert_eq!(err.to_string(), "method not found");
// Incorrect return type
let err = cls.verify_sel::<(u32,), u64>(sel!(setFoo:)).unwrap_err();
assert_eq!(
err.to_string(),
"expected return to have type code 'v', but found 'Q'"
);
// Too many arguments
let err = cls.verify_sel::<(u32, i8), ()>(sel!(setFoo:)).unwrap_err();
assert_eq!(err.to_string(), "expected 1 arguments, but 2 were given");
// Too few arguments
let err = cls.verify_sel::<(), ()>(sel!(setFoo:)).unwrap_err();
assert_eq!(err.to_string(), "expected 1 arguments, but 0 were given");
// Incorrect argument type
let err = cls.verify_sel::<(Sel,), ()>(sel!(setFoo:)).unwrap_err();
assert_eq!(
err.to_string(),
"expected argument at index 0 to have type code 'I', but found ':'"
);
// <https://github.com/madsmtm/objc2/issues/566>
let res = cls.verify_sel::<(), ffi::NSUInteger>(sel!(getNSInteger));
let expected = if cfg!(feature = "relax-sign-encoding") {
Ok(())
} else if cfg!(target_pointer_width = "64") {
Err("expected return to have type code 'q', but found 'Q'".to_string())
} else {
Err("expected return to have type code 'i', but found 'I'".to_string())
};
assert_eq!(res.map_err(|e| e.to_string()), expected);
// Metaclass
let metaclass = cls.metaclass();
let err = metaclass
.verify_sel::<(i32, i32, i32), i32>(sel!(addNumber:toNumber:))
.unwrap_err();
assert_eq!(err.to_string(), "expected 2 arguments, but 3 were given");
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found '^i'"]
fn test_send_message_verified() {
let obj = test_utils::custom_object();
let _: *const i32 = unsafe { msg_send![&obj, foo] };
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "invalid message send to +[CustomObject abcDef]: method not found"]
fn test_send_message_verified_to_class() {
let cls = test_utils::custom_class();
let _: i32 = unsafe { msg_send![cls, abcDef] };
}
#[test]
fn test_marker_traits() {
fn assert_marker_traits<T: Send + Sync + UnwindSafe + RefUnwindSafe + Unpin>() {}
assert_marker_traits::<VerificationError>();
}
#[test]
fn test_get_reference() {
let mut obj = test_utils::custom_object();
let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] };
let res: &u32 = unsafe { msg_send![&obj, fooReference] };
assert_eq!(*res, 42);
let res: *const u32 = unsafe { msg_send![&obj, fooReference] };
assert_eq!(unsafe { *res }, 42);
let res: *mut u32 = unsafe { msg_send![&obj, fooReference] };
assert_eq!(unsafe { *res }, 42);
}
#[test]
#[cfg_attr(
all(debug_assertions, not(feature = "relax-void-encoding")),
should_panic = "invalid message send to -[CustomObject fooReference]: expected return to have type code '^I', but found '^v'"
)]
fn test_get_reference_void() {
let mut obj = test_utils::custom_object();
let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] };
let res: *mut c_void = unsafe { msg_send![&obj, fooReference] };
let res: *mut u32 = res.cast();
assert_eq!(unsafe { *res }, 42);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic = "invalid message send to -[CustomObject foo]: expected return to have type code 'I', but found '^v'"]
fn test_get_integer_void() {
let obj = test_utils::custom_object();
let _: *mut c_void = unsafe { msg_send![&obj, foo] };
}
}