Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
15
third-party/vendor/objc2/src/rc/allocated.rs
vendored
Normal file
15
third-party/vendor/objc2/src/rc/allocated.rs
vendored
Normal 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`!
|
||||
329
third-party/vendor/objc2/src/rc/autorelease.rs
vendored
Normal file
329
third-party/vendor/objc2/src/rc/autorelease.rs
vendored
Normal 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
879
third-party/vendor/objc2/src/rc/id.rs
vendored
Normal 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();
|
||||
}
|
||||
}
|
||||
302
third-party/vendor/objc2/src/rc/id_forwarding_impls.rs
vendored
Normal file
302
third-party/vendor/objc2/src/rc/id_forwarding_impls.rs
vendored
Normal 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
|
||||
72
third-party/vendor/objc2/src/rc/id_traits.rs
vendored
Normal file
72
third-party/vendor/objc2/src/rc/id_traits.rs
vendored
Normal 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
132
third-party/vendor/objc2/src/rc/mod.rs
vendored
Normal 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 ()>()
|
||||
);
|
||||
}
|
||||
}
|
||||
82
third-party/vendor/objc2/src/rc/ownership.rs
vendored
Normal file
82
third-party/vendor/objc2/src/rc/ownership.rs
vendored
Normal 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>();
|
||||
}
|
||||
}
|
||||
175
third-party/vendor/objc2/src/rc/test_object.rs
vendored
Normal file
175
third-party/vendor/objc2/src/rc/test_object.rs
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
221
third-party/vendor/objc2/src/rc/weak_id.rs
vendored
Normal file
221
third-party/vendor/objc2/src/rc/weak_id.rs
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue