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

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 ()>()
);
}
}