objc2 0.6.3

Objective-C interface and runtime bindings
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
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(crate) unsafe 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(crate) unsafe 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(crate) fn into_ptr(this: Self) -> *mut T {
        let this = ManuallyDrop::new(this);
        this.ptr as *mut T
    }

    /// Initialize the instance variables for this object.
    ///
    /// This consumes the allocated instance, and returns the now partially
    /// initialized instance instead, which can be further used in
    /// [`msg_send!`] `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(crate) unsafe fn new(ptr: *mut T) -> Self {
        Self {
            ptr,
            p: PhantomData,
            p_auto_traits: PhantomData,
        }
    }

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

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

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

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

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

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

    use static_assertions::assert_not_impl_any;

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

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

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

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

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

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

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