Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  allocated_partial_init.rs

  Sprache: Rust
 

use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use core::{fmt, ptr};

use crate::__macro_helpers::defined_ivars::initialize_ivars;
use crate::runtime::{objc_release_fast, AnyClass, AnyObject};
use crate::{DefinedClass, Message};

/// An Objective-C object that has been allocated, but not initialized.
///
/// Objective-C splits the allocation and initialization steps up into two, so
/// we need to track it in the type-system whether something has been
/// initialized or not.
///
/// Note that allocation in Objective-C can fail, e.g. in Out Of Memory
/// situations! This is handled by `objc2` automatically, but if you really
/// need to, you can check for this explicitly by inspecting the pointer
/// returned from [`as_ptr`].
///
/// Note also that this represents that the _current_ class's instance
/// variables are not yet initialized; but subclass instance variables may
/// have been so.
///
/// See [Apple's documentation on Object Allocation][object-allocation] for a
/// few more details.
///
/// [`as_ptr`]: Self::as_ptr
/// [object-allocation]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectAllocation/ObjectAllocation.html
///
///
/// # Memory layout
///
/// This is guaranteed to have the same size and alignment as a pointer to the
/// object, `*const T`. The pointer may be NULL.
#[repr(transparent)]
#[derive(Debug)]
#[cfg_attr(
    feature = "unstable-coerce-pointee",
    derive(std::marker::CoercePointee)
)]
pub struct Allocated<T: ?Sized> {
    /// The yet-to-be initialized object.
    ///
    /// We don't use `Retained` here, since that has different auto-trait
    /// impls, and requires in its safety contract that the object is
    /// initialized (which makes it difficult to ensure correctness if such
    /// things are split across different files). Additionally, we want to
    /// have fine control over NULL-ness.
    ///
    /// Covariance is correct, same as `Retained`.
    ptr: *const T, // Intentionally not `NonNull`!
    /// Necessary for dropck, as with `Retained`.
    p: PhantomData<T>,
    /// Necessary for restricting auto traits.
    ///
    /// We _could_ probably implement auto traits `Send` and `Sync` here, but to be
    /// safe, we won't for now.
    p_auto_traits: PhantomData<AnyObject>,
}

// Explicitly don't implement `Deref`, `Message` nor `RefEncode`.
//
// We do want to implement `Receiver` though, to allow the user to type
// `self: Allocated<Self>`.
#[cfg(feature = "unstable-arbitrary-self-types")]
impl<T: ?Sized> core::ops::Receiver for Allocated<T> {
    type Target = T;
}

impl<T: ?Sized + Message> Allocated<T> {
    /// # Safety
    ///
    /// The caller must ensure the pointer is NULL, or that the given object
    /// has +1 retain count, and that the object behind the pointer has been
    /// allocated (but not yet initialized).
    #[inline]
    pub(crateunsafe fn new(ptr: *mut T) -> Self {
        Self {
            ptr,
            p: PhantomData,
            p_auto_traits: PhantomData,
        }
    }

    /// Allocate the object with a fast path using `objc_alloc`.
    ///
    ///
    /// # Safety
    ///
    /// The object must be safe to allocate on the current thread, and the
    /// object `T` must be an instance of the class.
    #[doc(alias = "objc_alloc")]
    #[inline]
    pub(crateunsafe fn alloc(cls: &AnyClass) -> Self
    where
        T: Sized,
    {
        // Available on non-fragile Apple runtimes.
        #[cfg(all(
            target_vendor = "apple",
            not(all(target_os = "macos", target_arch = "x86"))
        ))]
        {
            // SAFETY: Thread safety checked by the caller.
            let obj: *mut T = unsafe { crate::ffi::objc_alloc(cls).cast() };
            // SAFETY: The object is newly allocated, so this has +1 retain count
            unsafe { Self::new(obj) }
        }
        #[cfg(not(all(
            target_vendor = "apple",
            not(all(target_os = "macos", target_arch = "x86"))
        )))]
        {
            // SAFETY: Thread safety checked by the caller.
            unsafe { crate::msg_send![cls, alloc] }
        }
    }

    /// Returns a raw pointer to the object.
    ///
    /// The pointer is valid for at least as long as the `Allocated` is held.
    ///
    /// See [`Allocated::as_mut_ptr`] for the mutable equivalent.
    ///
    /// This is an associated method, and must be called as
    /// `Allocated::as_ptr(obj)`.
    #[inline]
    pub fn as_ptr(this: &Self) -> *const T {
        this.ptr
    }

    /// Returns a raw mutable pointer to the object.
    ///
    /// The pointer is valid for at least as long as the `Allocated` is held.
    ///
    /// See [`Allocated::as_ptr`] for the immutable equivalent.
    ///
    /// This is an associated method, and must be called as
    /// `Allocated::as_mut_ptr(obj)`.
    ///
    ///
    /// # Note about mutable references
    ///
    /// In general, you're not allowed to create a mutable reference from
    /// `Allocated`, unless you're defining the object and know that to be
    /// safe.
    ///
    /// For example, `+[NSMutableString alloc]` is allowed to return a
    /// non-unique object as an optimization, and then only figure out
    /// afterwards whether it needs to allocate, or if it can store an
    /// `NSString` internally.
    ///
    /// Similarly, while e.g. `+[NSData alloc]` may return a unique object,
    /// calling `-[NSData init]` afterwards could return a shared empty
    /// `NSData` instance.
    #[inline]
    #[allow(unknown_lints)] // New lint below
    #[allow(clippy::needless_pass_by_ref_mut)]
    pub fn as_mut_ptr(this: &mut Self) -> *mut T {
        // Note: Mutable pointers _can_ be safe for non-mutable classes,
        // especially right when they're being allocated / initialized.
        this.ptr as *mut T
    }

    #[inline]
    pub(cratefn into_ptr(this: Self) -> *mut T {
        let this = ManuallyDrop::new(this);
        this.ptr as *mut T
    }

    /// Initialize the instance variables for this object.
    ///
    /// This consumes the allocated instance, and returns the now partially
    /// initialized instance instead, which can be further used in
    /// [`msg_send!`] `super` calls.
    ///
    /// This works very similarly to [Swift's two-phase initialization
    /// scheme][two-phase-init], see that for details.
    ///
    /// [`msg_send!`]: crate::msg_send
    /// [two-phase-init]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/#Two-Phase-Initialization
    ///
    ///
    /// # Panics
    ///
    /// If debug assertions are enabled, this function will panic if the
    /// allocated instance is `NULL`, which usually only happens in Out of
    /// Memory situations.
    ///
    /// If debug assertions are disabled, this will return a `NULL` instance
    /// and the ivars will be dropped. The NULL instance cannot cause
    /// unsoundness and will likely lead to an initialization failure later on
    /// instead, but not panicking here is done as a code-size optimization.
    //
    // Note: This is intentionally _not_ an associated method, even though
    // `Allocated` will become `MethodReceiver` in the future.
    #[inline]
    #[track_caller]
    pub fn set_ivars(self, ivars: T::Ivars) -> PartialInit<T>
    where
        T: DefinedClass + Sized,
    {
        if let Some(ptr) = NonNull::new(ManuallyDrop::new(self).ptr as *mut T) {
            // SAFETY: The pointer came from `self`, so it is valid.
            unsafe { initialize_ivars::<T>(ptr, ivars) };

            // SAFETY:
            // - The pointer came from a `ManuallyDrop<Allocated<T>>`, which means
            //   that we've now transferred ownership over +1 retain count.
            // - The instance variables for this class have been initialized above.
            unsafe { PartialInit::new(ptr.as_ptr()) }
        } else if cfg!(debug_assertions) {
            panic!("tried to initialize instance variables on a NULL allocated object")
        } else {
            // Explicitly drop the ivars in this branch
            drop(ivars);

            // Create a new NULL PartialInit, which will likely be checked for
            // NULL-ness later on, after initialization of it has failed.
            //
            // SAFETY: The pointer is NULL.
            unsafe { PartialInit::new(ptr::null_mut()) }
        }
    }
}

impl<T: ?Sized> Drop for Allocated<T> {
    #[inline]
    fn drop(&mut self) {
        // SAFETY: Allocated objects can always safely be released, since
        // destructors are written to take into account that the object may
        // not have been initialized.
        //
        // This is also safe in the case where the object is NULL,
        // since `objc_release` allows NULL pointers.
        //
        // Rest is same as `Retained`'s `Drop`.
        unsafe { objc_release_fast(self.ptr as *mut _) };
    }
}

impl<T: ?Sized> fmt::Pointer for Allocated<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Pointer::fmt(&self.ptr, f)
    }
}

/// An Objective-C object that has been allocated and initialized in the
/// current class, but not yet initialized in the superclass.
///
/// This is returned by [`Allocated::set_ivars`], and is intended to be used
/// further in [`msg_send!`] `super` calls.
///
/// [`msg_send!`]: crate::msg_send
///
///
/// # Memory layout
///
/// The memory layout of this struct is NOT currently guaranteed, as we may
/// want to be able to move a drop flag to the stack in the future.
//
// Internally, this is very similar to `Allocated`, except that we have
// different guarantees on the validity of the object.
#[repr(transparent)]
#[derive(Debug)]
pub struct PartialInit<T: ?Sized> {
    /// The partially initialized object.
    ///
    /// Variance is same as `Retained`.
    ptr: *const T, // Intentionally not NonNull<T>
    /// Necessary for dropck, as with `Retained`.
    p: PhantomData<T>,
    /// Restrict auto traits, same as `Allocated<T>`.
    p_auto_traits: PhantomData<AnyObject>,
}

impl<T: ?Sized + Message> PartialInit<T> {
    /// # Safety
    ///
    /// The caller must ensure the pointer is NULL, or that the given object
    /// is allocated, has +1 retain count, and that the class' instance
    /// variables have been initialized.
    #[inline]
    pub(crateunsafe fn new(ptr: *mut T) -> Self {
        Self {
            ptr,
            p: PhantomData,
            p_auto_traits: PhantomData,
        }
    }

    /// Returns a raw pointer to the object.
    ///
    /// The pointer is valid for at least as long as the `PartialInit` is
    /// held.
    ///
    /// See [`PartialInit::as_mut_ptr`] for the mutable equivalent.
    ///
    /// This is an associated method, and must be called as
    /// `PartialInit::as_ptr(obj)`.
    #[inline]
    pub fn as_ptr(this: &Self) -> *const T {
        this.ptr
    }

    /// Returns a raw mutable pointer to the object.
    ///
    /// The pointer is valid for at least as long as the `PartialInit` is
    /// held.
    ///
    /// See [`PartialInit::as_ptr`] for the immutable equivalent.
    ///
    /// This is an associated method, and must be called as
    /// `PartialInit::as_mut_ptr(obj)`.
    #[inline]
    #[allow(unknown_lints)] // New lint below
    #[allow(clippy::needless_pass_by_ref_mut)]
    pub fn as_mut_ptr(this: &mut Self) -> *mut T {
        this.ptr as *mut T
    }

    #[inline]
    pub(cratefn into_ptr(this: Self) -> *mut T {
        let this = ManuallyDrop::new(this);
        this.ptr as *mut T
    }
}

impl<T: ?Sized> Drop for PartialInit<T> {
    #[inline]
    fn drop(&mut self) {
        // SAFETY: Partially initialized objects can always safely be
        // released, since destructors are written to take into account that
        // the object may not have been fully initialized.
        //
        // This is also safe in the case where the object is NULL,
        // since `objc_release` allows NULL pointers.
        //
        // Rest is same as `Retained`.
        unsafe { objc_release_fast(self.ptr as *mut _) };
    }
}

impl<T: ?Sized> fmt::Pointer for PartialInit<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Pointer::fmt(&self.ptr, f)
    }
}

#[cfg(test)]
mod tests {
    use core::panic::{RefUnwindSafe, UnwindSafe};

    use static_assertions::assert_not_impl_any;

    use super::*;
    use crate::rc::RcTestObject;
    use crate::runtime::NSObject;

    #[test]
    fn auto_traits() {
        assert_not_impl_any!(Allocated<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin);
        assert_not_impl_any!(PartialInit<()>: Send, Sync, UnwindSafe, RefUnwindSafe, Unpin);
    }

    #[repr(C)]
    struct MyObject<'a> {
        inner: NSObject,
        p: PhantomData<&'a str>,
    }

    /// Test that `Allocated<T>` is covariant over `T`.
    #[allow(unused)]
    fn assert_allocated_variance<'b>(obj: Allocated<MyObject<'static>>) -> Allocated<MyObject<'b>> {
        obj
    }

    /// Test that `PartialInit<T>` is covariant over `T`.
    #[allow(unused)]
    fn assert_partialinit_variance<'b>(
        obj: PartialInit<MyObject<'static>>,
    ) -> PartialInit<MyObject<'b>> {
        obj
    }

    #[test]
    #[cfg_attr(
        debug_assertions,
        should_panic = "tried to initialize instance variables on a NULL allocated object"
    )]
    fn test_set_ivars_null() {
        // SAFETY: The pointer is NULL
        let obj: Allocated<RcTestObject> = unsafe { Allocated::new(ptr::null_mut()) };
        let _ = obj.set_ivars(());
    }

    #[test]
    #[cfg(feature = "unstable-arbitrary-self-types")]
    fn arbitrary_self_types() {
        use crate::rc::Retained;
        use crate::{extern_methods, AnyThread};

        impl RcTestObject {
            extern_methods!(
                #[unsafe(method(init))]
                fn init_with_self(self: Allocated<Self>) -> Retained<Self>;
            );
        }

        let _ = RcTestObject::alloc().init_with_self();
    }
}

Messung V0.5 in Prozent
C=64 H=88 G=76

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002