Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1,15 @@
/// A marker type that can be used within [`Id`] to indicate that the object
/// has been allocated but not initialized.
///
/// The reason we use `Option<Id<Allocated<T>, O>>` instead of just `*mut T`
/// is:
/// - To allow releasing allocated objects, e.g. in the face of panics.
/// - To safely know the object is valid (albeit uninitialized).
/// - To allow specifying ownership.
///
/// [`Id`]: crate::rc::Id
#[repr(transparent)]
#[derive(Debug)]
pub struct Allocated<T: ?Sized>(T);
// Explicitly don't implement `Deref`, `Message` nor `RefEncode`!

View file

@ -0,0 +1,329 @@
use core::cell::UnsafeCell;
use core::ffi::c_void;
use core::fmt;
use core::marker::PhantomData;
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
use std::{cell::RefCell, thread_local, vec::Vec};
use crate::ffi;
/// An Objective-C autorelease pool.
///
/// The pool is drained when dropped.
///
/// This is not [`Send`], since `objc_autoreleasePoolPop` must be called on
/// the same thread.
///
/// And this is not [`Sync`], since you can only autorelease a reference to a
/// pool on the current thread.
#[derive(Debug)]
pub struct AutoreleasePool {
/// This is an opaque handle, and is not guaranteed to be neither a valid
/// nor aligned pointer.
context: *mut c_void,
/// May point to data that is mutated (even though we hold shared access).
p: PhantomData<*mut UnsafeCell<c_void>>,
}
#[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>> = RefCell::new(Vec::new());
}
impl AutoreleasePool {
/// Construct a new autorelease pool.
///
/// Use the [`autoreleasepool`] block for a safe alternative.
///
/// # Safety
///
/// The caller must ensure that when handing out `&'p AutoreleasePool` to
/// functions that this is the innermost pool.
///
/// Additionally, the pools must be dropped in the same order they were
/// created.
#[doc(alias = "objc_autoreleasePoolPush")]
#[inline]
unsafe fn new() -> Self {
// TODO: Make this function pub when we're more certain of the API
let context = unsafe { ffi::objc_autoreleasePoolPush() };
#[cfg(all(debug_assertions, not(feature = "unstable-autoreleasesafe")))]
POOLS.with(|c| c.borrow_mut().push(context));
Self {
context,
p: 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")))]
POOLS.with(|c| {
assert_eq!(
c.borrow().last(),
Some(&self.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]
#[allow(clippy::needless_lifetimes)]
pub unsafe fn ptr_as_ref<'p, T: ?Sized>(&'p self, ptr: *const T) -> &'p 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]
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::mut_from_ref)]
pub unsafe fn ptr_as_mut<'p, T: ?Sized>(&'p self, ptr: *mut T) -> &'p mut T {
self.__verify_is_inner();
// SAFETY: Checked by the caller
unsafe { ptr.as_mut().unwrap_unchecked() }
}
}
impl Drop for AutoreleasePool {
/// 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.
///
/// However, we would 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.
///
/// Fortunately, the above statement was true in the past, but since
/// [revision `371`] of objc4 (ships with MacOS 10.5) the exception is now
/// retained when `@throw` is encountered.
///
/// Hence it is safe to drain the pool when unwinding.
///
/// TODO: Verify this claim on 32bit!
///
/// [clang documentation]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#autoreleasepool
/// [revision `371`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-371/runtime/objc-exception.m#L479-L482
#[doc(alias = "objc_autoreleasePoolPop")]
#[inline]
fn drop(&mut self) {
unsafe { ffi::objc_autoreleasePoolPop(self.context) }
#[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"
)
});
}
}
impl fmt::Pointer for AutoreleasePool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.context, f)
}
}
/// 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 just 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 easily be accomplished with an `PhantomData<AutoreleasePool>`
/// if the `unstable-autoreleasesafe` feature is enabled.
pub unsafe trait AutoreleaseSafe {}
}
#[cfg(not(feature = "unstable-autoreleasesafe"))]
unsafe impl<T: ?Sized> AutoreleaseSafe for T {}
#[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 reference to the enclosing function to give it
/// a lifetime parameter that autoreleased objects can refer to.
///
/// The given reference 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. You can test the compile error with the
/// `unstable-autoreleasesafe` crate feature on nightly Rust.
///
/// Note that this is mostly useful for preventing leaks (as any Objective-C
/// method may leak internally). If implementing an interface to an object,
/// you should try to return retained pointers with [`msg_send_id!`] wherever
/// you can instead, since having to use this function can be quite cumbersome
/// for your users!
///
/// [`msg_send_id!`]: crate::msg_send_id
///
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// use core::mem::ManuallyDrop;
/// use objc2::{class, msg_send, msg_send_id};
/// use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned};
/// use objc2::runtime::Object;
///
/// fn needs_lifetime_from_pool<'p>(pool: &'p AutoreleasePool) -> &'p mut Object {
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![class!(NSObject), new] };
/// let obj = ManuallyDrop::new(obj);
/// let obj: *mut Object = unsafe { msg_send![obj, autorelease] };
/// // Lifetime of the returned reference is bounded by the pool
/// unsafe { pool.ptr_as_mut(obj) }
///
/// // Or simply
/// // let obj: Id<Object, Owned> = unsafe { msg_send_id![class!(NSObject), new] };
/// // obj.autorelease(pool)
/// }
///
/// autoreleasepool(|pool| {
/// // Create `obj` and autorelease it to the pool
/// let obj = needs_lifetime_from_pool(pool);
/// // ... use `obj` here
/// // `obj` is deallocated when the pool ends
/// });
/// ```
///
/// Fails to compile because `obj` does not live long enough for us to
/// safely take it out of the pool:
///
/// ```compile_fail
/// # use objc2::{class, msg_send_id};
/// # use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned};
/// # use objc2::runtime::Object;
/// #
/// # fn needs_lifetime_from_pool<'p>(pool: &'p AutoreleasePool) -> &'p mut Object {
/// # let obj: Id<Object, Owned> = unsafe { msg_send_id![class!(NSObject), new] };
/// # obj.autorelease(pool)
/// # }
/// #
/// let obj = autoreleasepool(|pool| {
/// let obj = needs_lifetime_from_pool(pool);
/// // Use `obj`
/// obj
/// });
/// ```
///
/// Incorrect usage which 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::{class, msg_send_id};
/// # use objc2::rc::{autoreleasepool, AutoreleasePool, Id, Owned};
/// # use objc2::runtime::Object;
/// #
/// # fn needs_lifetime_from_pool<'p>(pool: &'p AutoreleasePool) -> &'p mut Object {
/// # let obj: Id<Object, Owned> = unsafe { msg_send_id![class!(NSObject), new] };
/// # obj.autorelease(pool)
/// # }
/// #
/// autoreleasepool(|outer_pool| {
/// let obj = autoreleasepool(|inner_pool| {
/// let obj = needs_lifetime_from_pool(outer_pool);
/// obj
/// });
/// // `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!");
/// ```
#[doc(alias = "@autoreleasepool")]
#[inline]
pub fn autoreleasepool<T, F>(f: F) -> T
where
for<'p> F: FnOnce(&'p AutoreleasePool) -> T + AutoreleaseSafe,
{
let pool = unsafe { AutoreleasePool::new() };
f(&pool)
}
#[cfg(all(test, feature = "unstable-autoreleasesafe"))]
mod tests {
use super::AutoreleaseSafe;
use crate::runtime::Object;
fn requires_autoreleasesafe<T: AutoreleaseSafe>() {}
#[test]
fn test_autoreleasesafe() {
requires_autoreleasesafe::<usize>();
requires_autoreleasesafe::<*mut Object>();
requires_autoreleasesafe::<&mut Object>();
}
}

879
third-party/vendor/objc2/src/rc/id.rs vendored Normal file
View file

@ -0,0 +1,879 @@
use core::fmt;
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop};
use core::ops::{Deref, DerefMut};
use core::panic::{RefUnwindSafe, UnwindSafe};
use core::ptr::NonNull;
use super::Allocated;
use super::AutoreleasePool;
use super::{Owned, Ownership, Shared};
use crate::ffi;
use crate::{ClassType, Message};
/// An pointer for Objective-C reference counted objects.
///
/// [`Id`] strongly references or "retains" the given object `T`, and
/// "releases" it again when dropped, thereby ensuring it will be deallocated
/// at the right time.
///
/// An [`Id`] can either be [`Owned`] or [`Shared`], represented with the `O`
/// type parameter.
///
/// If owned, it is guaranteed that there are no other references to the
/// object, and the [`Id`] can therefore be mutably dereferenced.
///
/// If shared, however, it can only be immutably dereferenced because there
/// may be other references to the object, since a shared [`Id`] can be cloned
/// to provide exactly that.
///
/// An [`Id<T, Owned>`] can be safely converted to a [`Id<T, Shared>`] using
/// [`Id::into_shared`] or `From`/`Into`. The opposite is not safely possible,
/// but the unsafe option [`Id::from_shared`] is provided.
///
/// `Option<Id<T, O>>` is guaranteed to have the same size as a pointer to the
/// object.
///
///
/// # Comparison to `std` types
///
/// `Id<T, Owned>` can be thought of as the Objective-C equivalent of [`Box`]
/// from the standard library: It is a unique pointer to some allocated
/// object, and that means you're allowed to get a mutable reference to it.
///
/// Likewise, `Id<T, Shared>` is the Objective-C equivalent of [`Arc`]: It is
/// a reference-counting pointer that, when cloned, increases the reference
/// count.
///
/// [`Box`]: alloc::boxed::Box
/// [`Arc`]: alloc::sync::Arc
///
/// # Caveats
///
/// If the inner type implements [`Drop`], that implementation will 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.
///
/// This allows `?Sized` types `T`, but the intention is to only support when
/// `T` is an `extern type` (yet unstable).
///
/// # Examples
///
/// ```no_run
/// use objc2::msg_send_id;
/// use objc2::runtime::{Class, Object};
/// use objc2::rc::{Id, Owned, Shared, WeakId};
///
/// let cls = Class::get("NSObject").unwrap();
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
/// // obj will be released when it goes out of scope
///
/// // share the object so we can clone it
/// let obj: Id<_, Shared> = obj.into();
/// let another_ref = obj.clone();
/// // dropping our other reference will decrement the retain count
/// drop(another_ref);
///
/// let weak = WeakId::new(&obj);
/// assert!(weak.load().is_some());
/// // After the object is deallocated, our weak pointer returns none
/// drop(obj);
/// assert!(weak.load().is_none());
/// ```
///
/// ```no_run
/// # use objc2::{class, msg_send_id};
/// # use objc2::runtime::Object;
/// # use objc2::rc::{Id, Owned, Shared};
/// # type T = Object;
/// let mut owned: Id<T, Owned>;
/// # owned = unsafe { msg_send_id![class!(NSObject), new] };
/// let mut_ref: &mut T = &mut *owned;
/// // Do something with `&mut T` here
///
/// let shared: Id<T, Shared> = owned.into();
/// let cloned: Id<T, Shared> = shared.clone();
/// // Do something with `&T` here
/// ```
#[repr(transparent)]
// TODO: Figure out if `Message` bound on `T` would be better here?
// TODO: Add `ptr::Thin` bound on `T` to allow for only extern types
// TODO: Consider changing the name of Id -> Retain
pub struct Id<T: ?Sized, O: Ownership> {
/// A pointer to the contained object. The pointer is always retained.
///
/// It is important that this is `NonNull`, since we want to dereference
/// it later, and be able to use the null-pointer optimization.
///
/// Additionally, covariance is correct because we're either the unique
/// owner of `T` (O = Owned), or `T` is immutable (O = Shared).
ptr: NonNull<T>,
/// Necessary for dropck even though we never actually run T's destructor,
/// because it might have a `dealloc` that assumes that contained
/// references outlive the type.
///
/// See <https://doc.rust-lang.org/nightly/nomicon/phantom-data.html>
item: PhantomData<T>,
/// To prevent warnings about unused type parameters.
own: PhantomData<O>,
/// Marks the type as !UnwindSafe. Later on we'll re-enable this.
///
/// See <https://github.com/rust-lang/rust/issues/93367> for why this is
/// required.
notunwindsafe: PhantomData<&'static mut ()>,
}
impl<T: ?Sized, O: Ownership> Id<T, O> {
#[inline]
unsafe fn new_nonnull(ptr: NonNull<T>) -> Self {
Self {
ptr,
item: PhantomData,
own: PhantomData,
notunwindsafe: PhantomData,
}
}
}
impl<T: Message + ?Sized, O: Ownership> Id<Allocated<T>, O> {
#[inline]
pub(crate) unsafe fn new_allocated(ptr: *mut T) -> Option<Self> {
// SAFETY: Upheld by the caller
NonNull::new(ptr as *mut Allocated<T>).map(|ptr| unsafe { Self::new_nonnull(ptr) })
}
#[inline]
pub(crate) unsafe fn assume_init(this: Self) -> Id<T, O> {
let ptr = ManuallyDrop::new(this).ptr;
// NonNull::cast
let ptr = ptr.as_ptr() as *mut T;
let ptr = unsafe { NonNull::new_unchecked(ptr) };
// SAFETY: The pointer is valid.
// Caller verifies that the object is allocated.
unsafe { Id::new_nonnull(ptr) }
}
}
impl<T: Message + ?Sized, O: Ownership> Id<T, O> {
/// Constructs an [`Id`] to an object that already has +1 retain count.
///
/// This is useful when you have a retain count that has been handed off
/// from somewhere else, usually Objective-C methods like `init`, `alloc`,
/// `new`, `copy`, or methods with the `ns_returns_retained` attribute.
///
/// Since most of the above methods create new objects, and you therefore
/// hold unique access to the object, you would often set the ownership to
/// be [`Owned`].
///
/// But some immutable objects (like `NSString`) don't always return
/// unique references, so in those case you would use [`Shared`].
///
/// Returns `None` if the pointer was null.
///
///
/// # Safety
///
/// The caller must ensure the given object has +1 retain count, and that
/// the object pointer otherwise follows the same safety requirements as
/// in [`Id::retain`].
///
///
/// # Example
///
/// ```no_run
/// # use objc2::{class, msg_send, msg_send_id};
/// # use objc2::runtime::{Class, Object};
/// # use objc2::rc::{Id, Owned};
/// let cls: &Class;
/// # let cls = class!(NSObject);
/// let obj: &mut Object = unsafe { msg_send![cls, alloc] };
/// let obj: Id<Object, Owned> = unsafe { Id::new(msg_send![obj, init]).unwrap() };
/// // Or utilizing `msg_send_id`:
/// let obj = unsafe { msg_send_id![cls, alloc] };
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![obj, init] };
/// // Or in this case simply just:
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
/// ```
///
/// ```no_run
/// # use objc2::{class, msg_send_id};
/// # use objc2::runtime::Object;
/// # use objc2::rc::{Id, Shared};
/// # type NSString = Object;
/// let cls = class!(NSString);
/// // NSString is immutable, so don't create an owned reference to it
/// let obj: Id<NSString, Shared> = unsafe { msg_send_id![cls, new] };
/// ```
#[inline]
// Note: We don't take a reference as a parameter since it would be too
// easy to accidentally create two aliasing mutable references.
pub unsafe fn new(ptr: *mut T) -> Option<Id<T, O>> {
// Should optimize down to nothing.
// SAFETY: Upheld by the caller
NonNull::new(ptr).map(|ptr| unsafe { Id::new_nonnull(ptr) })
}
/// Returns a raw pointer to the object.
///
/// The pointer is valid for at least as long as the `Id` is held.
///
/// See [`Id::as_mut_ptr`] for the mutable equivalent.
///
/// This is an associated method, and must be called as `Id::as_ptr(obj)`.
#[inline]
pub fn as_ptr(this: &Id<T, O>) -> *const T {
this.ptr.as_ptr()
}
#[inline]
pub(crate) fn consume_as_ptr(this: ManuallyDrop<Self>) -> *mut T {
this.ptr.as_ptr()
}
#[inline]
pub(crate) fn option_into_ptr(obj: Option<Self>) -> *mut T {
// Difficult to write this in an ergonomic way with ?Sized
// So we just hack it with transmute!
// SAFETY: Option<Id<T, _>> has the same size as *mut T
unsafe { mem::transmute::<ManuallyDrop<Option<Self>>, *mut T>(ManuallyDrop::new(obj)) }
}
}
impl<T: Message + ?Sized> Id<T, Owned> {
/// Returns a raw mutable pointer to the object.
///
/// The pointer is valid for at least as long as the `Id` is held.
///
/// See [`Id::as_ptr`] for the immutable equivalent.
///
/// This is an associated method, and must be called as
/// `Id::as_mut_ptr(obj)`.
#[inline]
pub fn as_mut_ptr(this: &mut Id<T, Owned>) -> *mut T {
this.ptr.as_ptr()
}
}
// TODO: Add ?Sized bound
impl<T: Message, O: Ownership> Id<T, O> {
/// Convert the type of the given object to another.
///
/// This is equivalent to a `cast` between two pointers.
///
/// See [`Id::into_super`] for a safe alternative.
///
/// This is common to do when you know that an object is a subclass of
/// a specific class (e.g. casting an instance of `NSString` to `NSObject`
/// is safe because `NSString` is a subclass of `NSObject`).
///
/// All `'static` objects can safely be cast to [`Object`], since that
/// assumes no specific class.
///
/// [`Object`]: crate::runtime::Object
///
///
/// # Safety
///
/// You must ensure that the object can be reinterpreted as the given
/// type.
///
/// If `T` is not `'static`, you must ensure that `U` ensures that the
/// data contained by `T` is kept alive for as long as `U` lives.
///
/// Additionally, you must ensure that any safety invariants that the new
/// type has are upheld.
#[inline]
pub unsafe fn cast<U: Message>(this: Self) -> Id<U, O> {
let ptr = ManuallyDrop::new(this).ptr.cast();
// SAFETY: The object is forgotten, so we have +1 retain count.
//
// Caller verifies that the returned object is of the correct type.
unsafe { Id::new_nonnull(ptr) }
}
/// Retains the given object pointer.
///
/// This is useful when you have been given a pointer to an object from
/// some API, and you would like to ensure that the object stays around
/// so that you can work with it.
///
/// If said API is a normal Objective-C method, you probably want to use
/// [`Id::retain_autoreleased`] instead.
///
/// This is rarely used to construct owned [`Id`]s, see [`Id::new`] for
/// that.
///
/// Returns `None` if the pointer was null.
///
///
/// # Safety
///
/// The caller must ensure that the ownership is correct; that is, there
/// must be no [`Owned`] pointers or mutable references to the same
/// object, and when creating owned [`Id`]s, there must be no other
/// pointers or references to the object.
///
/// Additionally, the pointer must be valid as a reference (aligned,
/// dereferencable and initialized, see the [`std::ptr`] module for more
/// information).
///
/// Finally, if you do not know the concrete type of `T`, it may not be
/// `'static`, and hence you must ensure that the data that `T` references
/// lives for as long as `T`.
///
/// [`std::ptr`]: core::ptr
//
// This would be illegal:
// ```no_run
// let owned: Id<T, Owned>;
// // Lifetime information is discarded
// let retained: Id<T, Shared> = unsafe { Id::retain(&*owned) };
// // Which means we can still mutate `Owned`:
// let x: &mut T = &mut *owned;
// // While we have an immutable reference
// let y: &T = &*retained;
// ```
#[doc(alias = "objc_retain")]
#[inline]
pub unsafe fn retain(ptr: *mut T) -> Option<Id<T, O>> {
// SAFETY: The caller upholds that the pointer is valid
let res: *mut T = unsafe { ffi::objc_retain(ptr.cast()) }.cast();
debug_assert_eq!(res, ptr, "objc_retain did not return the same pointer");
// SAFETY: We just retained the object, so it has +1 retain count
unsafe { Self::new(res) }
}
/// Retains a previously autoreleased object pointer.
///
/// This is useful when calling Objective-C methods that return
/// autoreleased objects, see [Cocoa's Memory Management Policy][mmRules].
///
/// This has exactly the same semantics as [`Id::retain`], except it can
/// sometimes avoid putting the object into the autorelease pool, possibly
/// yielding increased speed and reducing memory pressure.
///
/// Note: This relies heavily on being inlined right after [`msg_send!`],
/// be careful not accidentally require instructions between these.
///
/// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
/// [`msg_send!`]: crate::msg_send
///
///
/// # Safety
///
/// Same as [`Id::retain`].
#[doc(alias = "objc_retainAutoreleasedReturnValue")]
#[inline]
pub unsafe fn retain_autoreleased(ptr: *mut T) -> Option<Id<T, O>> {
// Add magic nop instruction to participate in the fast autorelease
// scheme.
//
// See `callerAcceptsOptimizedReturn` in `objc-object.h`:
// https://github.com/apple-oss-distributions/objc4/blob/objc4-838/runtime/objc-object.h#L1209-L1377
//
// We will unconditionally emit these instructions, even if they end
// up being unused (for example because we're unlucky with inlining,
// some other work is done between the objc_msgSend and this, or the
// runtime version is too old to support it).
//
// It may seem like there should be a better way to do this, but
// emitting raw assembly is exactly what Clang and Swift does:
// swiftc: https://github.com/apple/swift/blob/swift-5.5.3-RELEASE/lib/IRGen/GenObjC.cpp#L148-L173
// Clang: https://github.com/llvm/llvm-project/blob/889317d47b7f046cf0e68746da8f7f264582fb5b/clang/lib/CodeGen/CGObjC.cpp#L2339-L2373
//
// Resources:
// - https://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html
// - https://www.galloway.me.uk/2012/02/how-does-objc_retainautoreleasedreturnvalue-work/
// - https://github.com/gfx-rs/metal-rs/issues/222
// - https://news.ycombinator.com/item?id=29311736
// - https://stackoverflow.com/a/23765612
//
// SAFETY:
// Based on https://doc.rust-lang.org/stable/reference/inline-assembly.html#rules-for-inline-assembly
//
// We don't care about the value of the register (so it's okay to be
// undefined), and its value is preserved.
//
// nomem: No reads or writes to memory are performed (this `mov`
// operates entirely on registers).
// preserves_flags: `mov` doesn't modify any flags.
// nostack: We don't touch the stack.
// Only worth doing on the Apple runtime.
// Not supported on TARGET_OS_WIN32.
#[cfg(all(feature = "apple", not(target_os = "windows")))]
{
// Supported since macOS 10.7.
#[cfg(target_arch = "x86_64")]
{
// x86_64 looks at the next call instruction.
//
// This is expected to be a PLT entry - if the user specifies
// `-Zplt=no`, a GOT entry will be created instead, and this
// will not work.
}
// Supported since macOS 10.8.
#[cfg(target_arch = "arm")]
unsafe {
core::arch::asm!("mov r7, r7", options(nomem, preserves_flags, nostack))
};
// Supported since macOS 10.10.
#[cfg(target_arch = "aarch64")]
unsafe {
core::arch::asm!("mov fp, fp", options(nomem, preserves_flags, nostack))
};
// Supported since macOS 10.12.
#[cfg(target_arch = "x86")]
unsafe {
core::arch::asm!("mov ebp, ebp", options(nomem, preserves_flags, nostack))
};
}
// SAFETY: Same as `retain`, this is just an optimization.
let res: *mut T = unsafe { ffi::objc_retainAutoreleasedReturnValue(ptr.cast()) }.cast();
// Ideally, we'd be able to specify that the above call should never
// be tail-call optimized (become a `jmp` instruction instead of a
// `call`); Rust doesn't really have a way of doing this currently, so
// we just emit a simple `nop` to make such tail-call optimizations
// less likely to occur.
//
// This is brittle! We should find a better solution!
#[cfg(all(feature = "apple", not(target_os = "windows"), target_arch = "x86_64"))]
{
// SAFETY: Similar to above.
unsafe { core::arch::asm!("nop", options(nomem, preserves_flags, nostack)) };
// TODO: Possibly more efficient alternative? Also consider PLT.
// #![feature(asm_sym)]
// core::arch::asm!(
// "mov rdi, rax",
// "call {}",
// sym objc2::ffi::objc_retainAutoreleasedReturnValue,
// inout("rax") obj,
// clobber_abi("C"),
// );
}
debug_assert_eq!(
res, ptr,
"objc_retainAutoreleasedReturnValue did not return the same pointer"
);
unsafe { Self::new(res) }
}
#[inline]
fn autorelease_inner(self) -> *mut T {
// Note that this (and the actual `autorelease`) is not an associated
// function. This breaks the guideline that smart pointers shouldn't
// add inherent methods, but since autoreleasing only works on already
// retained objects it is hard to imagine a case where the inner type
// has a method with the same name.
let ptr = ManuallyDrop::new(self).ptr.as_ptr();
// SAFETY: The `ptr` is guaranteed to be valid and have at least one
// retain count.
// And because of the ManuallyDrop, we don't call the Drop
// implementation, so the object won't also be released there.
let res: *mut T = unsafe { ffi::objc_autorelease(ptr.cast()) }.cast();
debug_assert_eq!(res, ptr, "objc_autorelease did not return the same pointer");
res
}
/// Autoreleases and prepares the [`Id`] to be returned to Objective-C.
///
/// The object is not immediately released, but will be when the innermost
/// autorelease pool is drained.
///
/// This is useful when [declaring your own methods][declare] where you
/// will often find yourself in need of returning autoreleased objects to
/// properly follow [Cocoa's Memory Management Policy][mmRules].
///
/// To that end, you could use [`Id::autorelease`], but that would require
/// you to have an [`AutoreleasePool`] object at hand, which you clearly
/// won't have in such cases. This function doesn't require a `pool`
/// object (but as a downside returns a pointer instead of a reference).
///
/// This is also more efficient than a normal `autorelease`, it makes a
/// best effort attempt to hand off ownership of the retain count to a
/// subsequent call to `objc_retainAutoreleasedReturnValue` /
/// [`Id::retain_autoreleased`] in the enclosing call frame. Note: This
/// optimization relies heavily on this function being tail called, so be
/// careful to call this function at the end of your method.
///
/// [declare]: crate::declare
/// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
///
///
/// # Examples
///
/// ```
/// use objc2::{class, msg_send_id, sel};
/// use objc2::declare::ClassBuilder;
/// use objc2::rc::{Id, Owned};
/// use objc2::runtime::{Class, Object, Sel};
/// #
/// # #[cfg(feature = "gnustep-1-7")]
/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
///
/// let mut builder = ClassBuilder::new("ExampleObject", class!(NSObject)).unwrap();
///
/// extern "C" fn get(cls: &Class, _cmd: Sel) -> *mut Object {
/// let obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new] };
/// obj.autorelease_return()
/// }
///
/// unsafe {
/// builder.add_class_method(
/// sel!(get),
/// get as extern "C" fn(_, _) -> _,
/// );
/// }
///
/// let cls = builder.register();
/// ```
#[doc(alias = "objc_autoreleaseReturnValue")]
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
#[inline]
pub fn autorelease_return(self) -> *mut T {
// See `autorelease_inner` for why this is an inherent method
let ptr = ManuallyDrop::new(self).ptr.as_ptr();
// SAFETY: Same as `autorelease_inner`, this is just an optimization.
let res: *mut T = unsafe { ffi::objc_autoreleaseReturnValue(ptr.cast()) }.cast();
debug_assert_eq!(
res, ptr,
"objc_autoreleaseReturnValue did not return the same pointer"
);
res
}
}
// TODO: Consider something like this
// #[cfg(block)]
// impl<T: Block, O> Id<T, O> {
// #[doc(alias = "objc_retainBlock")]
// pub unsafe fn retain_block(block: *mut T) -> Option<Self> {
// todo!()
// }
// }
// TODO: Add ?Sized bound
impl<T: Message> Id<T, Owned> {
/// Autoreleases the owned [`Id`], returning a mutable reference bound to
/// the pool.
///
/// The object is not immediately released, but will be when the innermost
/// / current autorelease pool (given as a parameter) is drained.
#[doc(alias = "objc_autorelease")]
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
#[inline]
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::mut_from_ref)]
pub fn autorelease<'p>(self, pool: &'p AutoreleasePool) -> &'p mut T {
let ptr = self.autorelease_inner();
// SAFETY: The pointer is valid as a reference, and we've consumed
// the unique access to the `Id` so mutability is safe.
unsafe { pool.ptr_as_mut(ptr) }
}
/// Promote a shared [`Id`] to an owned one, allowing it to be mutated.
///
///
/// # Safety
///
/// The caller must ensure that there are no other pointers (including
/// [`WeakId`][`super::WeakId`] pointers) to the same object.
///
/// This also means that the given [`Id`] should have a retain count of
/// exactly 1 (except when autoreleases are involved).
///
/// In general, this is wildly unsafe, do see if you can find a different
/// solution!
#[inline]
pub unsafe fn from_shared(obj: Id<T, Shared>) -> Self {
// Note: We can't debug_assert retainCount because of autoreleases
let ptr = ManuallyDrop::new(obj).ptr;
// SAFETY: The pointer is valid
// Ownership rules are upheld by the caller
unsafe { <Id<T, Owned>>::new_nonnull(ptr) }
}
/// Convert an owned to a shared [`Id`], allowing it to be cloned.
///
/// This is also implemented as a `From` conversion, but this name is more
/// explicit, which may be useful in some cases.
#[inline]
pub fn into_shared(obj: Self) -> Id<T, Shared> {
let ptr = ManuallyDrop::new(obj).ptr;
// SAFETY: The pointer is valid, and ownership is simply decreased
unsafe { <Id<T, Shared>>::new_nonnull(ptr) }
}
}
// TODO: Add ?Sized bound
impl<T: Message> Id<T, Shared> {
/// Autoreleases the shared [`Id`], returning an aliased reference bound
/// to the pool.
///
/// The object is not immediately released, but will be when the innermost
/// / current autorelease pool (given as a parameter) is drained.
#[doc(alias = "objc_autorelease")]
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
#[inline]
#[allow(clippy::needless_lifetimes)]
pub fn autorelease<'p>(self, pool: &'p AutoreleasePool) -> &'p T {
let ptr = self.autorelease_inner();
// SAFETY: The pointer is valid as a reference
unsafe { pool.ptr_as_ref(ptr) }
}
}
impl<T: ClassType + 'static, O: Ownership> Id<T, O>
where
T::Super: 'static,
{
/// Convert the object into it's superclass.
#[inline]
pub fn into_super(this: Self) -> Id<T::Super, O> {
// SAFETY:
// - The casted-to type is a superclass of the type.
// - Both types are `'static` (this could maybe be relaxed a bit, but
// let's just be on the safe side)!
unsafe { Self::cast::<T::Super>(this) }
}
}
impl<T: Message> From<Id<T, Owned>> for Id<T, Shared> {
/// Convert an owned to a shared [`Id`], allowing it to be cloned.
///
/// Same as [`Id::into_shared`].
#[inline]
fn from(obj: Id<T, Owned>) -> Self {
Id::into_shared(obj)
}
}
// TODO: Add ?Sized bound
impl<T: Message> Clone for Id<T, Shared> {
/// Makes a clone of the shared object.
///
/// This increases the object's reference count.
#[doc(alias = "objc_retain")]
#[doc(alias = "retain")]
#[inline]
fn clone(&self) -> Self {
// SAFETY: The pointer is valid
let obj = unsafe { Id::retain(self.ptr.as_ptr()) };
// SAFETY: `objc_retain` always returns the same object pointer, and
// the pointer is guaranteed non-null by Id.
unsafe { obj.unwrap_unchecked() }
}
}
/// `#[may_dangle]` (see [this][dropck_eyepatch]) doesn't apply here since we
/// don't run `T`'s destructor (rather, we want to discourage having `T`s with
/// a destructor); and even if we did run the destructor, it would not be safe
/// to add since we cannot verify that a `dealloc` method doesn't access
/// borrowed data.
///
/// [dropck_eyepatch]: https://doc.rust-lang.org/nightly/nomicon/dropck.html#an-escape-hatch
impl<T: ?Sized, O: Ownership> Drop for Id<T, O> {
/// Releases the retained object.
///
/// The contained object's destructor (if it has one) is never run!
#[doc(alias = "objc_release")]
#[doc(alias = "release")]
#[inline]
fn drop(&mut self) {
// We could technically run the destructor for `T` when `O = Owned`,
// and when `O = Shared` with (retainCount == 1), but that would be
// confusing and inconsistent since we cannot guarantee that it's run.
// SAFETY: The `ptr` is guaranteed to be valid and have at least one
// retain count
unsafe { ffi::objc_release(self.ptr.as_ptr().cast()) };
}
}
// https://doc.rust-lang.org/nomicon/arc-mutex/arc-base.html#send-and-sync
/// The `Send` implementation requires `T: Sync` because `Id<T, Shared>` give
/// access to `&T`.
///
/// Additiontally, it requires `T: Send` because if `T: !Send`, you could
/// clone a `Id<T, Shared>`, send it to another thread, and drop the clone
/// last, making `dealloc` get called on the other thread, and violate
/// `T: !Send`.
unsafe impl<T: Sync + Send + ?Sized> Send for Id<T, Shared> {}
/// The `Sync` implementation requires `T: Sync` because `&Id<T, Shared>` give
/// access to `&T`.
///
/// Additiontally, it requires `T: Send`, because if `T: !Send`, you could
/// clone a `&Id<T, Shared>` from another thread, and drop the clone last,
/// making `dealloc` get called on the other thread, and violate `T: !Send`.
unsafe impl<T: Sync + Send + ?Sized> Sync for Id<T, Shared> {}
/// `Id<T, Owned>` are `Send` if `T` is `Send` because they give the same
/// access as having a T directly.
unsafe impl<T: Send + ?Sized> Send for Id<T, Owned> {}
/// `Id<T, Owned>` are `Sync` if `T` is `Sync` because they give the same
/// access as having a `T` directly.
unsafe impl<T: Sync + ?Sized> Sync for Id<T, Owned> {}
impl<T: ?Sized, O: Ownership> Deref for Id<T, O> {
type Target = T;
/// Obtain an immutable reference to the object.
// Box doesn't inline, but that's because it's a compiler built-in
#[inline]
fn deref(&self) -> &T {
// SAFETY: The pointer's validity is verified when the type is created
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized> DerefMut for Id<T, Owned> {
/// Obtain a mutable reference to the object.
#[inline]
fn deref_mut(&mut self) -> &mut T {
// SAFETY: The pointer's validity is verified when the type is created
// Additionally, the owned `Id` is the unique owner of the object, so
// mutability is safe.
unsafe { self.ptr.as_mut() }
}
}
impl<T: ?Sized, O: Ownership> fmt::Pointer for Id<T, O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.ptr.as_ptr(), f)
}
}
// This is valid without `T: Unpin` because we don't implement any projection.
//
// See https://doc.rust-lang.org/1.54.0/src/alloc/boxed.rs.html#1652-1675
// and the `Arc` implementation.
impl<T: ?Sized, O: Ownership> Unpin for Id<T, O> {}
impl<T: RefUnwindSafe + ?Sized, O: Ownership> RefUnwindSafe for Id<T, O> {}
// Same as `Arc<T>`.
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Id<T, Shared> {}
// Same as `Box<T>`.
impl<T: UnwindSafe + ?Sized> UnwindSafe for Id<T, Owned> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::msg_send;
use crate::rc::{autoreleasepool, RcTestObject, ThreadTestData};
use crate::runtime::Object;
#[track_caller]
fn assert_retain_count(obj: &Object, expected: usize) {
let retain_count: usize = unsafe { msg_send![obj, retainCount] };
assert_eq!(retain_count, expected);
}
#[test]
fn test_drop() {
let mut expected = ThreadTestData::current();
let obj = RcTestObject::new();
expected.alloc += 1;
expected.init += 1;
expected.assert_current();
drop(obj);
expected.release += 1;
expected.dealloc += 1;
expected.assert_current();
}
#[test]
fn test_autorelease() {
let obj: Id<_, Shared> = RcTestObject::new().into();
let cloned = obj.clone();
let mut expected = ThreadTestData::current();
autoreleasepool(|pool| {
let _ref = obj.autorelease(pool);
expected.autorelease += 1;
expected.assert_current();
assert_retain_count(&cloned, 2);
});
expected.release += 1;
expected.assert_current();
assert_retain_count(&cloned, 1);
autoreleasepool(|pool| {
let _ref = cloned.autorelease(pool);
expected.autorelease += 1;
expected.assert_current();
});
expected.release += 1;
expected.dealloc += 1;
expected.assert_current();
}
#[test]
fn test_clone() {
let obj: Id<_, Owned> = RcTestObject::new();
assert_retain_count(&obj, 1);
let mut expected = ThreadTestData::current();
let obj: Id<_, Shared> = obj.into();
expected.assert_current();
assert_retain_count(&obj, 1);
let cloned = obj.clone();
expected.retain += 1;
expected.assert_current();
assert_retain_count(&cloned, 2);
assert_retain_count(&obj, 2);
drop(obj);
expected.release += 1;
expected.assert_current();
assert_retain_count(&cloned, 1);
drop(cloned);
expected.release += 1;
expected.dealloc += 1;
expected.assert_current();
}
#[test]
fn test_retain_autoreleased_works_as_retain() {
let obj: Id<_, Shared> = RcTestObject::new().into();
let mut expected = ThreadTestData::current();
let ptr = Id::as_ptr(&obj) as *mut RcTestObject;
let _obj2: Id<_, Shared> = unsafe { Id::retain_autoreleased(ptr) }.unwrap();
expected.retain += 1;
expected.assert_current();
}
#[test]
fn test_cast() {
let obj: Id<RcTestObject, _> = RcTestObject::new();
let expected = ThreadTestData::current();
// SAFETY: Any object can be cast to `Object`
let obj: Id<Object, _> = unsafe { Id::cast(obj) };
expected.assert_current();
// SAFETY: The object was originally `RcTestObject`
let _obj: Id<RcTestObject, _> = unsafe { Id::cast(obj) };
expected.assert_current();
}
}

View file

@ -0,0 +1,302 @@
//! Trivial forwarding impls on `Id`.
//!
//! 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::iter::FusedIterator;
use core::ops::{Deref, DerefMut};
use core::pin::Pin;
use core::task::{Context, Poll};
use std::error::Error;
use std::io;
use super::{Id, Owned, Ownership};
impl<T: PartialEq + ?Sized, O: Ownership> PartialEq for Id<T, O> {
#[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, O: Ownership> Eq for Id<T, O> {}
impl<T: PartialOrd + ?Sized, O: Ownership> PartialOrd for Id<T, O> {
#[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, O: Ownership> Ord for Id<T, O> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T: hash::Hash + ?Sized, O: Ownership> hash::Hash for Id<T, O> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
impl<T: hash::Hasher + ?Sized> hash::Hasher for Id<T, Owned> {
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, O: Ownership> fmt::Display for Id<T, O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Debug + ?Sized, O: Ownership> fmt::Debug for Id<T, O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<I: Iterator + ?Sized> Iterator for Id<I, Owned> {
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
(**self).next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(**self).size_hint()
}
fn nth(&mut self, n: usize) -> Option<I::Item> {
(**self).nth(n)
}
}
impl<I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for Id<I, Owned> {
fn next_back(&mut self) -> Option<I::Item> {
(**self).next_back()
}
fn nth_back(&mut self, n: usize) -> Option<I::Item> {
(**self).nth_back(n)
}
}
impl<I: ExactSizeIterator + ?Sized> ExactSizeIterator for Id<I, Owned> {
fn len(&self) -> usize {
(**self).len()
}
}
impl<I: FusedIterator + ?Sized> FusedIterator for Id<I, Owned> {}
// TODO: Consider this impl
// impl<'a, T, O: Ownership> IntoIterator for &'a Id<T, O>
// where
// &'a T: IntoIterator,
// {
// type Item = <&'a T as IntoIterator>::Item;
// type IntoIter = <&'a T as IntoIterator>::IntoIter;
//
// fn into_iter(self) -> Self::IntoIter {
// (**self).into_iter()
// }
// }
impl<T, O: Ownership> borrow::Borrow<T> for Id<T, O> {
fn borrow(&self) -> &T {
Deref::deref(self)
}
}
impl<T> borrow::BorrowMut<T> for Id<T, Owned> {
fn borrow_mut(&mut self) -> &mut T {
DerefMut::deref_mut(self)
}
}
impl<T: ?Sized, O: Ownership> AsRef<T> for Id<T, O> {
fn as_ref(&self) -> &T {
Deref::deref(self)
}
}
impl<T: ?Sized> AsMut<T> for Id<T, Owned> {
fn as_mut(&mut self) -> &mut T {
DerefMut::deref_mut(self)
}
}
impl<T: Error + ?Sized, O: Ownership> Error for Id<T, O> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
(**self).source()
}
}
impl<T: io::Read + ?Sized> io::Read for Id<T, Owned> {
#[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> io::Write for Id<T, Owned> {
#[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> io::Seek for Id<T, Owned> {
#[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> io::BufRead for Id<T, Owned> {
#[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> Future for Id<T, Owned> {
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

View file

@ -0,0 +1,72 @@
//! Helper traits for Id.
use super::{Id, Owned, Ownership};
use crate::Message;
/// Helper trait for functionality on slices containing [`Id`]s.
pub trait SliceId {
/// The type of the items in the slice.
type Item: ?Sized;
/// Convert a slice of [`Id`]s into a slice of references.
fn as_slice_ref(&self) -> &[&Self::Item];
/// Convert a mutable slice of [`Id`]s into a mutable slice of references.
fn as_slice_mut(&mut self) -> &mut [&Self::Item];
}
/// Helper trait for functionality on slices containing owned [`Id`]s.
pub trait SliceIdMut: SliceId {
/// Convert a mutable slice of mutable [`Id`]s into a mutable slice of
/// mutable references.
fn as_mut_slice_mut(&mut self) -> &mut [&mut Self::Item];
}
impl<T: Message + ?Sized, O: Ownership> SliceId for [Id<T, O>] {
type Item = T;
fn as_slice_ref(&self) -> &[&T] {
let ptr = self as *const Self as *const [&T];
// SAFETY: Id<T, O> and &T have the same memory layout. Further safety
// follows from `Deref` impl.
unsafe { ptr.as_ref().unwrap_unchecked() }
}
fn as_slice_mut(&mut self) -> &mut [&T] {
let ptr = self as *mut Self as *mut [&T];
// SAFETY: Id<T, O> and &T have the same memory layout. Further safety
// follows from `Deref` impl.
unsafe { ptr.as_mut().unwrap_unchecked() }
}
}
impl<T: Message + ?Sized> SliceIdMut for [Id<T, Owned>] {
fn as_mut_slice_mut(&mut self) -> &mut [&mut T] {
let ptr = self as *mut Self as *mut [&mut T];
// SAFETY: Id<T, O> and &mut T have the same memory layout, and the
// `Id` is `Owned` so we're allowed to hand out mutable references.
// Further safety follows from `DerefMut` impl.
unsafe { ptr.as_mut().unwrap_unchecked() }
}
}
/// Helper trait to implement [`Default`] on types whoose default value is an
/// [`Id`].
// TODO: Maybe make this `unsafe` and provide a default implementation?
pub trait DefaultId {
/// Indicates whether the default value is mutable or immutable.
type Ownership: Ownership;
/// The default [`Id`] for a type.
///
/// On most objects the implementation would just be sending a message to
/// the `new` selector.
fn default_id() -> Id<Self, Self::Ownership>;
}
impl<T: DefaultId + ?Sized> Default for Id<T, T::Ownership> {
#[inline]
fn default() -> Self {
T::default_id()
}
}

132
third-party/vendor/objc2/src/rc/mod.rs vendored Normal file
View file

@ -0,0 +1,132 @@
//! Utilities for reference counting Objective-C objects.
//!
//! These utilities in this module provide ARC-like semantics for working with
//! Objective-C's reference counted objects.
//!
//! A smart pointer [`Id`] is provided to ensure that Objective-C objects are
//! retained and released when created and dropped, respectively.
//!
//! To enforce aliasing rules, an `Id` can be either owned or shared; if it is
//! owned, meaning the `Id` is the only reference to the object, it can be
//! mutably dereferenced. An owned `Id` can be converted to a shared `Id`,
//! which can be cloned to allow multiple references.
//!
//! Weak references may be created using the [`WeakId`] struct; these will not
//! retain the object, but one can attempt to load them and obtain an `Id`, 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.
//!
//! [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
//!
#![cfg_attr(feature = "apple", doc = "```")]
#![cfg_attr(not(feature = "apple"), doc = "```no_run")]
//! use objc2::{class, msg_send_id};
//! use objc2::rc::{autoreleasepool, Id, Shared, WeakId};
//! use objc2::runtime::Object;
//!
//! // Id will release the object when dropped
//! let obj: Id<Object, Shared> = unsafe {
//! msg_send_id![class!(NSObject), new]
//! };
//!
//! // Cloning retains the object an additional time
//! let cloned = obj.clone();
//! autoreleasepool(|pool| {
//! // Autorelease consumes the Id, but won't
//! // actually release until the end of an autoreleasepool
//! let obj_ref: &Object = cloned.autorelease(pool);
//! });
//!
//! // Weak references won't retain the object
//! let weak = WeakId::new(&obj);
//! drop(obj);
//! assert!(weak.load().is_none());
//! ```
mod allocated;
mod autorelease;
mod id;
mod id_forwarding_impls;
mod id_traits;
mod ownership;
mod weak_id;
#[cfg(test)]
mod test_object;
pub use self::allocated::Allocated;
pub use self::autorelease::{autoreleasepool, AutoreleasePool, AutoreleaseSafe};
pub use self::id::Id;
pub use self::id_traits::{DefaultId, SliceId, SliceIdMut};
pub use self::ownership::{Owned, Ownership, Shared};
pub use self::weak_id::WeakId;
#[cfg(test)]
pub(crate) use self::test_object::{RcTestObject, ThreadTestData};
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
use core::mem::size_of;
use super::{Id, Owned, Ownership, Shared, WeakId};
use crate::runtime::Object;
#[repr(C)]
struct TestType {
inner: Object,
}
#[repr(C)]
struct MyObject<'a> {
inner: Object,
p: PhantomData<&'a str>,
}
/// Test that `Id<T, O>` is covariant over `T`.
#[allow(unused)]
fn assert_id_variance<'a, 'b, O: Ownership>(
obj: &'a Id<MyObject<'static>, O>,
) -> &'a Id<MyObject<'b>, O> {
obj
}
/// Test that `WeakId<T>` is covariant over `T`.
#[allow(unused)]
fn assert_weak_id_variance<'a, 'b>(
obj: &'a WeakId<MyObject<'static>>,
) -> &'a WeakId<MyObject<'b>> {
obj
}
#[test]
fn test_size_of() {
assert_eq!(size_of::<Id<TestType, Owned>>(), size_of::<&TestType>());
assert_eq!(size_of::<Id<TestType, Shared>>(), size_of::<&TestType>());
assert_eq!(
size_of::<Option<Id<TestType, Owned>>>(),
size_of::<&TestType>()
);
assert_eq!(
size_of::<Option<Id<TestType, Shared>>>(),
size_of::<&TestType>()
);
assert_eq!(
size_of::<Option<WeakId<TestType>>>(),
size_of::<*const ()>()
);
}
}

View file

@ -0,0 +1,82 @@
use core::fmt::Debug;
use core::hash::Hash;
use core::panic::{RefUnwindSafe, UnwindSafe};
use super::AutoreleaseSafe;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
enum Never {}
/// A type used to mark that a struct owns the object(s) it contains,
/// so it has the sole references to them.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Owned {
inner: Never,
}
/// A type used to mark that the object(s) a struct contains are shared,
/// so there may be other references to them.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Shared {
inner: Never,
}
mod private {
pub trait Sealed {}
impl Sealed for super::Owned {}
impl Sealed for super::Shared {}
}
/// A type that marks what type of ownership a struct has over the object(s)
/// it contains; specifically, either [`Owned`] or [`Shared`].
///
/// This trait is sealed and not meant to be implemented outside of the this
/// crate.
pub trait Ownership:
private::Sealed
// Special
+ 'static
+ Sized
// Auto-traits
+ Send
+ Sync
+ Unpin
+ UnwindSafe
+ RefUnwindSafe
// Derived
+ Clone
+ Copy
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ Debug
// Custom
+ AutoreleaseSafe
{
}
impl Ownership for Owned {}
impl Ownership for Shared {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generic_ownership_traits() {
fn assert_partialeq<T: PartialEq>() {}
assert_partialeq::<Shared>();
assert_partialeq::<Owned>();
fn test_ownership_implies_partialeq<O: Ownership>() {
assert_partialeq::<O>();
}
test_ownership_implies_partialeq::<Shared>();
test_ownership_implies_partialeq::<Owned>();
}
}

View file

@ -0,0 +1,175 @@
use core::cell::RefCell;
use core::mem::ManuallyDrop;
use core::ptr;
use super::{Id, Owned};
use crate::foundation::{NSObject, NSZone};
use crate::{declare_class, msg_send, ClassType};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct ThreadTestData {
pub(crate) alloc: usize,
pub(crate) dealloc: 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,
}
impl ThreadTestData {
/// Get the amount of method calls performed on the current thread.
pub(crate) fn current() -> ThreadTestData {
TEST_DATA.with(|data| data.borrow().clone())
}
#[track_caller]
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;
}
assert_eq!(current, expected);
}
}
std::thread_local! {
pub(crate) static TEST_DATA: RefCell<ThreadTestData> = RefCell::new(Default::default());
}
declare_class!(
/// A helper object that counts how many times various reference-counting
/// primitives are called.
#[derive(Debug, PartialEq)]
pub(crate) struct RcTestObject {}
unsafe impl ClassType for RcTestObject {
type Super = NSObject;
}
unsafe impl RcTestObject {
#[sel(newReturningNull)]
fn new_returning_null() -> *mut Self {
ptr::null_mut()
}
#[sel(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] }
}
#[sel(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] }
}
#[sel(allocReturningNull)]
fn alloc_returning_null() -> *mut Self {
ptr::null_mut()
}
#[sel(init)]
fn init(&mut self) -> *mut Self {
TEST_DATA.with(|data| data.borrow_mut().init += 1);
unsafe { msg_send![super(self), init] }
}
#[sel(initReturningNull)]
fn init_returning_null(&mut self) -> *mut Self {
let _: () = unsafe { msg_send![self, release] };
ptr::null_mut()
}
#[sel(retain)]
fn retain(&self) -> *mut Self {
TEST_DATA.with(|data| data.borrow_mut().retain += 1);
unsafe { msg_send![super(self), retain] }
}
#[sel(release)]
fn release(&self) {
TEST_DATA.with(|data| data.borrow_mut().release += 1);
unsafe { msg_send![super(self), release] }
}
#[sel(autorelease)]
fn autorelease(&self) -> *mut Self {
TEST_DATA.with(|data| data.borrow_mut().autorelease += 1);
unsafe { msg_send![super(self), autorelease] }
}
#[sel(dealloc)]
unsafe fn dealloc(&mut self) {
TEST_DATA.with(|data| data.borrow_mut().dealloc += 1);
unsafe { msg_send![super(self), dealloc] }
}
#[sel(_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
}
#[sel(copyWithZone:)]
fn copy_with_zone(&self, _zone: *const NSZone) -> *const Self {
TEST_DATA.with(|data| data.borrow_mut().copy += 1);
Id::consume_as_ptr(ManuallyDrop::new(Self::new()))
}
#[sel(mutableCopyWithZone:)]
fn mutable_copy_with_zone(&self, _zone: *const NSZone) -> *const Self {
TEST_DATA.with(|data| data.borrow_mut().mutable_copy += 1);
Id::consume_as_ptr(ManuallyDrop::new(Self::new()))
}
#[sel(copyReturningNull)]
fn copy_returning_null(&self) -> *const Self {
ptr::null()
}
#[sel(methodReturningNull)]
fn method_returning_null(&self) -> *const Self {
ptr::null()
}
}
);
unsafe impl Send for RcTestObject {}
unsafe impl Sync for RcTestObject {}
impl RcTestObject {
pub(crate) fn new() -> Id<Self, Owned> {
// Use msg_send! - msg_send_id! is tested elsewhere!
unsafe { Id::new(msg_send![Self::class(), new]) }.unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_declared_name() {
assert_eq!(RcTestObject::class().name(), RcTestObject::NAME);
}
}

View file

@ -0,0 +1,221 @@
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::{Id, Shared};
use crate::ffi;
use crate::Message;
/// A pointer type for a weak reference to an Objective-C reference counted
/// object.
///
/// Allows breaking reference cycles and safely checking whether the object
/// has been deallocated.
#[repr(transparent)]
pub struct WeakId<T: ?Sized> {
/// We give the runtime the address to this box, so that it can modify it
/// even if the `WeakId` 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>>,
/// WeakId inherits variance, dropck and various marker traits from
/// `Id<T, Shared>` because it can be loaded as a shared Id.
item: PhantomData<Id<T, Shared>>,
}
impl<T: Message> WeakId<T> {
/// Construct a new [`WeakId`] referencing the given shared [`Id`].
#[doc(alias = "objc_initWeak")]
#[inline]
pub fn new(obj: &Id<T, Shared>) -> Self {
// Note that taking `&Id<T, Owned>` would not be safe since that would
// allow loading an `Id<T, Shared>` later on.
// SAFETY: `obj` is valid
unsafe { Self::new_inner(Id::as_ptr(obj)) }
}
/// # 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 a shared (and retained) [`Id`] if the object still exists.
///
/// Returns [`None`] if the object has been deallocated or was created
/// with [`Default::default`].
#[doc(alias = "retain")]
#[doc(alias = "objc_loadWeak")]
#[doc(alias = "objc_loadWeakRetained")]
#[inline]
pub fn load(&self) -> Option<Id<T, Shared>> {
let ptr = self.inner.get();
let obj = unsafe { ffi::objc_loadWeakRetained(ptr) }.cast();
unsafe { Id::new(obj) }
}
// TODO: Add `autorelease(&self) -> Option<&T>` using `objc_loadWeak`?
}
impl<T: ?Sized> Drop for WeakId<T> {
/// Drops the `WeakId` pointer.
#[doc(alias = "objc_destroyWeak")]
#[inline]
fn drop(&mut self) {
unsafe { ffi::objc_destroyWeak(self.inner.get()) }
}
}
// TODO: Add ?Sized
impl<T> Clone for WeakId<T> {
/// Makes a clone of the `WeakId` 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> Default for WeakId<T> {
/// Constructs a new `WeakId<T>` 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()) }
}
}
/// This implementation follows the same reasoning as `Id<T, Shared>`.
unsafe impl<T: Sync + Send + ?Sized> Sync for WeakId<T> {}
/// This implementation follows the same reasoning as `Id<T, Shared>`.
unsafe impl<T: Sync + Send + ?Sized> Send for WeakId<T> {}
// Unsure about the Debug bound on T, see std::sync::Weak
impl<T: fmt::Debug + ?Sized> fmt::Debug for WeakId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(WeakId)")
}
}
// Underneath this is just a `Box`
impl<T: ?Sized> Unpin for WeakId<T> {}
// Same as `Id<T, Shared>`.
impl<T: RefUnwindSafe + ?Sized> RefUnwindSafe for WeakId<T> {}
// Same as `Id<T, Shared>`.
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for WeakId<T> {}
impl<T: Message> From<Id<T, Shared>> for WeakId<T> {
#[inline]
fn from(obj: Id<T, Shared>) -> Self {
WeakId::new(&obj)
}
}
impl<T: Message> TryFrom<WeakId<T>> for Id<T, Shared> {
type Error = ();
fn try_from(weak: WeakId<T>) -> Result<Self, ()> {
weak.load().ok_or(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rc::{RcTestObject, ThreadTestData};
use crate::runtime::Object;
#[test]
fn test_weak() {
let obj: Id<_, Shared> = RcTestObject::new().into();
let mut expected = ThreadTestData::current();
let weak = WeakId::new(&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.dealloc += 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: Id<_, Shared> = RcTestObject::new().into();
let mut expected = ThreadTestData::current();
let weak = WeakId::new(&obj);
expected.assert_current();
let weak2 = weak.clone();
if cfg!(feature = "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: WeakId<Object> = WeakId::default();
assert!(weak.load().is_none());
drop(weak);
}
}