objc2/runtime/
mod.rs

1//! # Direct runtime bindings.
2//!
3//! This module contains safe(r) bindings to common parts of the Objective-C
4//! runtime. See the [`ffi`][crate::ffi] module for details on the raw
5//! bindings.
6//!
7//!
8//! # Example
9//!
10//! Using features of the runtime to query information about `NSObject`.
11//!
12//! ```
13#![doc = include_str!("../../examples/introspection.rs")]
14//! ```
15#![allow(clippy::missing_panics_doc)]
16
17use alloc::ffi::CString;
18use alloc::vec::Vec;
19use core::ffi::c_char;
20use core::ffi::c_uint;
21use core::ffi::{c_void, CStr};
22use core::fmt;
23use core::hash;
24use core::panic::{RefUnwindSafe, UnwindSafe};
25use core::ptr::{self, NonNull};
26
27// Note: While this is not public, it is still a breaking change to remove,
28// since `objc2-foundation` relies on it.
29#[doc(hidden)]
30pub mod __nsstring;
31mod bool;
32mod define;
33mod malloc;
34mod message_receiver;
35mod method_encoding_iter;
36mod method_implementation;
37mod nsobject;
38mod nsproxy;
39mod nszone;
40mod protocol_object;
41mod retain_release_fast;
42
43pub(crate) use self::method_encoding_iter::{EncodingParseError, MethodEncodingIter};
44pub(crate) use self::retain_release_fast::{objc_release_fast, objc_retain_fast};
45use crate::encode::{Encode, EncodeArguments, EncodeReturn, Encoding, OptionEncode, RefEncode};
46use crate::msg_send;
47use crate::verify::{verify_method_signature, Inner};
48use crate::{ffi, DowncastTarget, Message};
49
50// Note: While this is not public, it is still a breaking change to remove,
51// since `objc2-foundation` relies on it.
52#[doc(hidden)]
53pub use self::nsproxy::NSProxy as __NSProxy;
54
55pub use self::bool::Bool;
56pub use self::define::{ClassBuilder, ProtocolBuilder};
57pub use self::message_receiver::MessageReceiver;
58pub use self::method_implementation::MethodImplementation;
59pub use self::nsobject::{NSObject, NSObjectProtocol};
60pub use self::nszone::NSZone;
61pub use self::protocol_object::{ImplementedBy, ProtocolObject};
62pub use crate::verify::VerificationError;
63
64#[allow(deprecated)]
65pub use crate::ffi::{BOOL, NO, YES};
66
67use self::malloc::{MallocCStr, MallocSlice};
68
69/// We do not want to expose `MallocSlice` to end users, because in the
70/// future, we want to be able to change it to `Box<[T], MallocAllocator>`.
71///
72/// So instead we use an unnameable type.
73macro_rules! MallocSlice {
74    ($t:ty) => {
75        impl std::ops::Deref<Target = [$t]> + AsRef<[$t]> + std::fmt::Debug
76    };
77}
78
79/// Same as `MallocSlice!`.
80macro_rules! MallocCStr {
81    () => {
82        impl std::ops::Deref<Target = CStr> + AsRef<CStr> + std::fmt::Debug
83    };
84}
85
86/// Implement PartialEq, Eq and Hash using pointer semantics; there's not
87/// really a better way to do it for this type
88macro_rules! standard_pointer_impls {
89    ($name:ident) => {
90        impl PartialEq for $name {
91            #[inline]
92            fn eq(&self, other: &Self) -> bool {
93                ptr::eq(self, other)
94            }
95        }
96        impl Eq for $name {}
97        impl hash::Hash for $name {
98            #[inline]
99            fn hash<H: hash::Hasher>(&self, state: &mut H) {
100                let ptr: *const Self = self;
101                ptr.hash(state)
102            }
103        }
104    };
105}
106
107/// A pointer to the start of a method implementation.
108///
109/// The first argument is a pointer to the receiver, the second argument is
110/// the selector, and the rest of the arguments follow.
111///
112///
113/// # Safety
114///
115/// This is a "catch all" type; it must be transmuted to the correct type
116/// before being called!
117///
118/// Also note that this is non-null! If you require an Imp that can be null,
119/// use `Option<Imp>`.
120#[doc(alias = "IMP")]
121pub type Imp = unsafe extern "C-unwind" fn();
122
123/// A method selector.
124///
125/// The Rust equivalent of Objective-C's `SEL _Nonnull` type. You can create
126/// this statically using the [`sel!`] macro.
127///
128/// The main reason the Objective-C runtime uses a custom type for selectors,
129/// as opposed to a plain c-string, is to support efficient comparison - a
130/// a selector is effectively an [interned string], so this makes equiality
131/// comparisons very cheap.
132///
133/// This struct guarantees the null-pointer optimization, namely that
134/// `Option<Sel>` is the same size as `Sel`.
135///
136/// Selectors are immutable.
137///
138/// [`sel!`]: crate::sel
139/// [interned string]: https://en.wikipedia.org/wiki/String_interning
140#[repr(transparent)]
141#[derive(Copy, Clone)]
142#[doc(alias = "SEL")]
143#[doc(alias = "objc_selector")]
144pub struct Sel {
145    ptr: NonNull<c_void>,
146}
147
148// SAFETY: Sel is immutable (and can be retrieved from any thread using the
149// `sel!` macro).
150unsafe impl Sync for Sel {}
151unsafe impl Send for Sel {}
152impl UnwindSafe for Sel {}
153impl RefUnwindSafe for Sel {}
154
155impl Sel {
156    #[inline]
157    #[doc(hidden)]
158    pub const unsafe fn __internal_from_ptr(ptr: *const u8) -> Self {
159        // Used in static selectors.
160        // SAFETY: Upheld by caller.
161        let ptr = unsafe { NonNull::new_unchecked(ptr as *mut c_void) };
162        Self { ptr }
163    }
164
165    #[inline]
166    pub(crate) unsafe fn from_ptr(ptr: *const c_void) -> Option<Self> {
167        // SAFETY: Caller verifies that the pointer is valid.
168        NonNull::new(ptr as *mut c_void).map(|ptr| Self { ptr })
169    }
170
171    #[inline]
172    pub(crate) const fn as_ptr(&self) -> *const c_void {
173        self.ptr.as_ptr()
174    }
175
176    // We explicitly don't do #[track_caller] here, since we expect the error
177    // to never actually happen.
178    pub(crate) unsafe fn register_unchecked(name: *const c_char) -> Self {
179        let ptr = unsafe { ffi::sel_registerName(name) };
180        // SAFETY: `sel_registerName` declares return type as `SEL _Nonnull`,
181        // at least when input is also `_Nonnull` (which it is in our case).
182        //
183        // Looking at the source code, it can fail and will return NULL if
184        // allocating space for the selector failed (which then subsequently
185        // invokes UB by calling `memcpy` with a NULL argument):
186        // <https://github.com/apple-oss-distributions/objc4/blob/objc4-841.13/runtime/objc-os.h#L1002-L1004>
187        //
188        // I suspect this will be really uncommon in practice, since the
189        // called selector is almost always going to be present in the binary
190        // already; but alas, we'll handle it!
191        ptr.expect("failed allocating selector")
192    }
193
194    /// Registers a selector with the Objective-C runtime.
195    ///
196    /// This is the dynamic version of the [`sel!`] macro, prefer to use that
197    /// when your selector is static.
198    ///
199    /// [`sel!`]: crate::sel
200    ///
201    ///
202    /// # Panics
203    ///
204    /// Panics if the runtime failed allocating space for the selector.
205    #[inline]
206    #[doc(alias = "sel_registerName")]
207    pub fn register(name: &CStr) -> Self {
208        // SAFETY: Input is a non-null, NUL-terminated C-string pointer.
209        unsafe { Self::register_unchecked(name.as_ptr()) }
210    }
211
212    /// Returns the string representation of the selector.
213    #[inline]
214    #[doc(alias = "sel_getName")]
215    pub fn name(self) -> &'static CStr {
216        // SAFETY: Input is non-null selector. Declares return type as
217        // `const char * _Nonnull`, source code agrees.
218        let ptr = unsafe { ffi::sel_getName(self) };
219        // SAFETY: The string is a valid C-style NUL-terminated string, and
220        // has static lifetime since the selector has static lifetime.
221        unsafe { CStr::from_ptr(ptr) }
222    }
223
224    pub(crate) fn number_of_arguments(self) -> usize {
225        self.name()
226            .to_bytes()
227            .iter()
228            .filter(|&&b| b == b':')
229            .count()
230    }
231}
232
233impl PartialEq for Sel {
234    #[inline]
235    fn eq(&self, other: &Self) -> bool {
236        if cfg!(feature = "gnustep-1-7") {
237            // GNUStep implements "typed" selectors, which means their pointer
238            // values sometimes differ; so let's use the runtime-provided
239            // `sel_isEqual`.
240            unsafe { ffi::sel_isEqual(*self, *other).as_bool() }
241        } else {
242            // `ffi::sel_isEqual` uses pointer comparison on Apple (the
243            // documentation explicitly notes this); so as an optimization,
244            // let's do that as well!
245            ptr::eq(self.as_ptr(), other.as_ptr())
246        }
247    }
248}
249
250impl Eq for Sel {}
251
252impl hash::Hash for Sel {
253    #[inline]
254    fn hash<H: hash::Hasher>(&self, state: &mut H) {
255        if cfg!(feature = "gnustep-1-7") {
256            // Note: We hash the name instead of the pointer on GNUStep, since
257            // they're typed.
258            self.name().hash(state);
259        } else {
260            self.as_ptr().hash(state);
261        }
262    }
263}
264
265// SAFETY: `Sel` is FFI compatible, and the encoding is `Sel`.
266unsafe impl Encode for Sel {
267    const ENCODING: Encoding = Encoding::Sel;
268}
269
270unsafe impl OptionEncode for Sel {}
271
272// RefEncode is not implemented for Sel, because there is literally no API
273// that takes &Sel, while the user could get confused and accidentally attempt
274// that.
275
276impl fmt::Display for Sel {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        // Selectors are basically always UTF-8, so it's _fine_ to do a lossy
279        // conversion here.
280        fmt::Display::fmt(&self.name().to_string_lossy(), f)
281    }
282}
283
284impl fmt::Debug for Sel {
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        f.debug_tuple("Sel").field(&self.name()).finish()
287    }
288}
289
290impl fmt::Pointer for Sel {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        fmt::Pointer::fmt(&self.ptr, f)
293    }
294}
295
296/// An opaque type that represents an instance variable.
297///
298/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/ivar?language=objc).
299#[repr(C)]
300#[doc(alias = "objc_ivar")]
301pub struct Ivar {
302    _priv: [u8; 0],
303    _p: ffi::OpaqueData,
304}
305
306// SAFETY: Ivar is immutable (and can be retrieved from AnyClass anyhow).
307unsafe impl Sync for Ivar {}
308unsafe impl Send for Ivar {}
309impl UnwindSafe for Ivar {}
310impl RefUnwindSafe for Ivar {}
311
312impl Ivar {
313    /// Returns the instance variable's name.
314    ///
315    /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418922-ivar_getname?language=objc).
316    #[inline]
317    #[doc(alias = "ivar_getName")]
318    pub fn name(&self) -> &CStr {
319        unsafe { CStr::from_ptr(ffi::ivar_getName(self)) }
320    }
321
322    /// Returns the instance variable's offset from the object base.
323    ///
324    /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418976-ivar_getoffset?language=objc).
325    #[inline]
326    #[doc(alias = "ivar_getOffset")]
327    pub fn offset(&self) -> isize {
328        unsafe { ffi::ivar_getOffset(self) }
329    }
330
331    /// Returns the instance variable's `@encode(type)` string.
332    ///
333    /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418569-ivar_gettypeencoding?language=objc).
334    #[inline]
335    #[doc(alias = "ivar_getTypeEncoding")]
336    pub fn type_encoding(&self) -> &CStr {
337        unsafe { CStr::from_ptr(ffi::ivar_getTypeEncoding(self)) }
338    }
339
340    #[inline]
341    pub(crate) fn debug_assert_encoding(&self, _expected: &Encoding) {
342        #[cfg(all(debug_assertions, not(feature = "disable-encoding-assertions")))]
343        {
344            let encoding = self.type_encoding();
345            let encoding = encoding.to_str().expect("encoding must be UTF-8");
346            assert!(
347                _expected.equivalent_to_str(encoding),
348                "wrong encoding. Tried to retrieve ivar with encoding {encoding}, but the encoding of the given type was {_expected}",
349            );
350        }
351    }
352
353    /// Returns a pointer to the instance variable / ivar on the given object.
354    ///
355    /// This is similar to [`UnsafeCell::get`], see that for more information
356    /// on what is and isn't safe to do.
357    ///
358    /// Usually you will have defined the instance variable yourself with
359    /// [`ClassBuilder::add_ivar`], the type of the ivar `T` must match the
360    /// type used in that.
361    ///
362    /// Library implementors are strongly encouraged to expose a safe
363    /// interface to the ivar.
364    ///
365    /// [`UnsafeCell::get`]: core::cell::UnsafeCell::get
366    /// [`ClassBuilder::add_ivar`]: crate::runtime::ClassBuilder::add_ivar
367    ///
368    ///
369    /// # Panics
370    ///
371    /// Panics when `debug_assertions` are enabled if the type encoding of the
372    /// ivar differs from the type encoding of `T`. This can be disabled with
373    /// the `"disable-encoding-assertions"` Cargo feature flag.
374    ///
375    ///
376    /// # Safety
377    ///
378    /// The object must have the given instance variable on it, and it must be
379    /// of type `T`. Any invariants that the object have assumed about the
380    /// value of the instance variable must not be violated.
381    ///
382    /// Note that an object can have multiple instance variables with the same
383    /// name; you must ensure that when the instance variable was retrieved,
384    /// was retrieved from the class that it was defined on. In particular,
385    /// getting a class dynamically using e.g. [`AnyObject::class`], and using
386    /// an instance variable from that here is _not_ sound in general.
387    ///
388    /// No thread synchronization is done on accesses to the variable, so you
389    /// must ensure that any access to the returned pointer do not cause data
390    /// races, and that Rust's mutability rules are not otherwise violated.
391    #[inline]
392    pub unsafe fn load_ptr<T: Encode>(&self, obj: &AnyObject) -> *mut T {
393        self.debug_assert_encoding(&T::ENCODING);
394
395        let ptr = NonNull::from(obj);
396        // SAFETY: That the ivar is valid is ensured by the caller
397        let ptr = unsafe { AnyObject::ivar_at_offset::<T>(ptr, self.offset()) };
398
399        // Safe as *mut T because `self` is `UnsafeCell`
400        ptr.as_ptr()
401    }
402
403    /// Returns a reference to the instance variable with the given name.
404    ///
405    /// See [`Ivar::load_ptr`] for more information.
406    ///
407    ///
408    /// # Panics
409    ///
410    /// Panics when `debug_assertions` are enabled if the type encoding of the
411    /// ivar differs from the type encoding of `T`. This can be disabled with
412    /// the `"disable-encoding-assertions"` Cargo feature flag.
413    ///
414    ///
415    /// # Safety
416    ///
417    /// The object must have the given instance variable on it, and it must be
418    /// of type `T`.
419    ///
420    /// No thread synchronization is done, so you must ensure that no other
421    /// thread is concurrently mutating the variable. This requirement can be
422    /// considered upheld if all mutation happens through [`Ivar::load_mut`]
423    /// (since that takes the object mutably).
424    #[inline]
425    pub unsafe fn load<'obj, T: Encode>(&self, obj: &'obj AnyObject) -> &'obj T {
426        // SAFETY: That the ivar is valid as `&T` is ensured by the caller,
427        // and the reference is properly bound to the object.
428        unsafe { self.load_ptr::<T>(obj).as_ref().unwrap_unchecked() }
429    }
430
431    /// Returns a mutable reference to the ivar with the given name.
432    ///
433    /// See [`Ivar::load_ptr`] for more information.
434    ///
435    ///
436    /// # Panics
437    ///
438    /// Panics when `debug_assertions` are enabled if the type encoding of the
439    /// ivar differs from the type encoding of `T`. This can be disabled with
440    /// the `"disable-encoding-assertions"` Cargo feature flag.
441    ///
442    ///
443    /// # Safety
444    ///
445    /// The object must have an instance variable with the given name, and it
446    /// must be of type `T`.
447    ///
448    /// This access happens through `&mut`, which means we know it to be the
449    /// only reference, hence you do not need to do any work to ensure that
450    /// data races do not happen.
451    #[inline]
452    pub unsafe fn load_mut<'obj, T: Encode>(&self, obj: &'obj mut AnyObject) -> &'obj mut T {
453        self.debug_assert_encoding(&T::ENCODING);
454
455        let ptr = NonNull::from(obj);
456        // SAFETY: That the ivar is valid is ensured by the caller
457        let mut ptr = unsafe { AnyObject::ivar_at_offset::<T>(ptr, self.offset()) };
458
459        // SAFETY: That the ivar is valid as `&mut T` is ensured by taking an
460        // `&mut` object
461        unsafe { ptr.as_mut() }
462    }
463}
464
465standard_pointer_impls!(Ivar);
466
467impl fmt::Debug for Ivar {
468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469        f.debug_struct("Ivar")
470            .field("name", &self.name())
471            .field("offset", &self.offset())
472            .field("type_encoding", &self.type_encoding())
473            .finish_non_exhaustive()
474    }
475}
476
477#[derive(Debug, PartialEq, Eq, Hash)]
478pub(crate) struct MethodDescription {
479    pub(crate) sel: Sel,
480    pub(crate) types: &'static CStr,
481}
482
483impl MethodDescription {
484    pub(crate) unsafe fn from_raw(raw: ffi::objc_method_description) -> Option<Self> {
485        let sel = raw.name?;
486        if raw.types.is_null() {
487            return None;
488        }
489        // SAFETY: We've checked that the pointer is not NULL, rest is checked
490        // by caller.
491        let types = unsafe { CStr::from_ptr(raw.types) };
492        Some(Self { sel, types })
493    }
494}
495
496/// A type that represents a method in a class definition.
497///
498/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/method?language=objc).
499#[repr(C)]
500#[doc(alias = "objc_method")]
501pub struct Method {
502    _priv: [u8; 0],
503    _p: ffi::OpaqueData,
504}
505
506// SAFETY: Method is immutable (and can be retrieved from AnyClass anyhow).
507unsafe impl Sync for Method {}
508unsafe impl Send for Method {}
509impl UnwindSafe for Method {}
510impl RefUnwindSafe for Method {}
511
512impl Method {
513    // Note: We don't take `&mut` here, since the operations on methods work
514    // atomically.
515    #[inline]
516    fn as_mut_ptr(&self) -> *mut Self {
517        let ptr: *const Self = self;
518        ptr as _
519    }
520
521    /// Returns the name of self.
522    #[inline]
523    #[doc(alias = "method_getName")]
524    pub fn name(&self) -> Sel {
525        unsafe { ffi::method_getName(self).unwrap() }
526    }
527
528    /// Returns the `Encoding` of self's return type.
529    #[doc(alias = "method_copyReturnType")]
530    pub fn return_type(&self) -> MallocCStr!() {
531        unsafe {
532            let encoding = ffi::method_copyReturnType(self);
533            MallocCStr::from_c_str(encoding)
534        }
535    }
536
537    /// Returns the `Encoding` of a single parameter type of self, or
538    /// [`None`] if self has no parameter at the given index.
539    #[doc(alias = "method_copyArgumentType")]
540    pub fn argument_type(&self, index: usize) -> Option<MallocCStr!()> {
541        unsafe {
542            let encoding = ffi::method_copyArgumentType(self, index as c_uint);
543            NonNull::new(encoding).map(|encoding| MallocCStr::from_c_str(encoding.as_ptr()))
544        }
545    }
546
547    /// An iterator over the method's types.
548    ///
549    /// It is approximately equivalent to:
550    ///
551    /// ```ignore
552    /// let types = method.types();
553    /// assert_eq!(types.next()?, method.return_type());
554    /// for i in 0..method.arguments_count() {
555    ///    assert_eq!(types.next()?, method.argument_type(i)?);
556    /// }
557    /// assert!(types.next().is_none());
558    /// ```
559    #[doc(alias = "method_getTypeEncoding")]
560    pub(crate) fn types(&self) -> MethodEncodingIter<'_> {
561        // SAFETY: The method pointer is valid and non-null
562        let cstr = unsafe { ffi::method_getTypeEncoding(self) };
563        if cstr.is_null() {
564            panic!("method type encoding was NULL");
565        }
566        // SAFETY: `method_getTypeEncoding` returns a C-string, and we just
567        // checked that it is non-null.
568        let encoding = unsafe { CStr::from_ptr(cstr) };
569        let s = encoding
570            .to_str()
571            .expect("method type encoding must be UTF-8");
572        MethodEncodingIter::new(s)
573    }
574
575    /// Returns the number of arguments accepted by self.
576    #[inline]
577    #[doc(alias = "method_getNumberOfArguments")]
578    pub fn arguments_count(&self) -> usize {
579        unsafe { ffi::method_getNumberOfArguments(self) as usize }
580    }
581
582    /// Returns the implementation of this method.
583    #[doc(alias = "method_getImplementation")]
584    pub fn implementation(&self) -> Imp {
585        unsafe { ffi::method_getImplementation(self).expect("null IMP") }
586    }
587
588    /// Set the implementation of this method.
589    ///
590    /// Note that any thread may at any point be changing method
591    /// implementations, so if you intend to call the previous method as
592    /// returned by e.g. [`Self::implementation`], beware that that may now be
593    /// stale.
594    ///
595    /// The previous implementation is returned from this function though, so
596    /// you can call that instead.
597    ///
598    /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418707-method_setimplementation?language=objc).
599    ///
600    ///
601    /// # Safety
602    ///
603    /// The given implementation function pointer must:
604    ///
605    /// 1. Have the signature expected by the Objective-C runtime and callers
606    ///    of this method.
607    ///
608    /// 2. Be at least as safe as the existing method, i.e. by overriding the
609    ///    previous method, it should not be possible for the program to cause
610    ///    UB.
611    ///
612    ///    A common mistake would be expecting e.g. a pointer to not be null,
613    ///    where the null case was handled before.
614    #[doc(alias = "method_setImplementation")]
615    pub unsafe fn set_implementation(&self, imp: Imp) -> Imp {
616        // SAFETY: The new impl is not NULL, and the rest is upheld by the
617        // caller.
618        unsafe { ffi::method_setImplementation(self.as_mut_ptr(), imp).expect("null IMP") }
619    }
620
621    /// Exchange the implementation of two methods.
622    ///
623    /// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/1418769-method_exchangeimplementations?language=objc).
624    ///
625    ///
626    /// # Safety
627    ///
628    /// The two methods must be perfectly compatible, both in signature, and
629    /// in expected (in terms of safety, not necessarily behaviour) input and
630    /// output.
631    ///
632    ///
633    /// # Example
634    ///
635    /// This is an atomic version of the following:
636    ///
637    /// ```
638    /// use objc2::runtime::Method;
639    /// # use objc2::runtime::NSObject;
640    /// # use objc2::sel;
641    /// # use crate::objc2::ClassType;
642    ///
643    /// let m1: &Method;
644    /// let m2: &Method;
645    /// #
646    /// # // Use the same method twice, to avoid actually changing anything
647    /// # m1 = NSObject::class().instance_method(sel!(hash)).unwrap();
648    /// # m2 = NSObject::class().instance_method(sel!(hash)).unwrap();
649    ///
650    /// unsafe {
651    ///     let imp = m2.set_implementation(m1.implementation());
652    ///     m1.set_implementation(imp);
653    /// }
654    /// ```
655    #[inline]
656    #[doc(alias = "method_exchangeImplementations")]
657    pub unsafe fn exchange_implementation(&self, other: &Self) {
658        // TODO: Consider checking that `self.types()` and `other.types()`
659        // match when debug assertions are enabled?
660
661        // SAFETY: Verified by caller
662        unsafe { ffi::method_exchangeImplementations(self.as_mut_ptr(), other.as_mut_ptr()) }
663    }
664}
665
666standard_pointer_impls!(Method);
667
668impl fmt::Debug for Method {
669    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670        f.debug_struct("Method")
671            .field("name", &self.name())
672            .field("types", &self.types())
673            .field("implementation", &self.implementation())
674            .finish_non_exhaustive()
675    }
676}
677
678/// An opaque type that represents an Objective-C class.
679///
680/// This is an opaque type meant to be used behind a shared reference
681/// `&AnyClass`, which is semantically equivalent to `Class _Nonnull`.
682///
683/// A nullable class can be used as `Option<&AnyClass>`.
684///
685/// See [Apple's documentation](https://developer.apple.com/documentation/objectivec/class?language=objc).
686#[repr(C)]
687#[doc(alias = "Class")]
688#[doc(alias = "objc_class")]
689pub struct AnyClass {
690    // `isa` field is deprecated and not available on GNUStep, so we don't
691    // expose it here. Use `class_getSuperclass` instead.
692    inner: AnyObject,
693}
694
695/// Use [`AnyClass`] instead.
696#[deprecated = "renamed to `runtime::AnyClass`"]
697pub type Class = AnyClass;
698
699// SAFETY: AnyClass is immutable (and can be retrieved from any thread using
700// the `class!` macro).
701unsafe impl Sync for AnyClass {}
702unsafe impl Send for AnyClass {}
703impl UnwindSafe for AnyClass {}
704impl RefUnwindSafe for AnyClass {}
705// Note that Unpin is not applicable.
706
707impl AnyClass {
708    /// Returns the class definition of a specified class, or [`None`] if the
709    /// class is not registered with the Objective-C runtime.
710    #[inline]
711    #[doc(alias = "objc_getClass")]
712    pub fn get(name: &CStr) -> Option<&'static Self> {
713        let cls = unsafe { ffi::objc_getClass(name.as_ptr()) };
714        unsafe { cls.as_ref() }
715    }
716
717    // Same as `get`, but ...
718    // fn lookup(name: &CStr) -> Option<&'static Self>;
719
720    /// Obtains the list of registered class definitions.
721    #[doc(alias = "objc_copyClassList")]
722    pub fn classes() -> MallocSlice!(&'static Self) {
723        unsafe {
724            let mut count: c_uint = 0;
725            let classes: *mut &Self = ffi::objc_copyClassList(&mut count).cast();
726            MallocSlice::from_array(classes, count as usize)
727        }
728    }
729
730    /// Returns the total number of registered classes.
731    #[inline]
732    #[doc(alias = "objc_getClassList")]
733    pub fn classes_count() -> usize {
734        unsafe { ffi::objc_getClassList(ptr::null_mut(), 0) as usize }
735    }
736
737    /// # Safety
738    ///
739    /// 1. The class pointer must be valid.
740    /// 2. The string is unbounded, so the caller must bound it.
741    pub(crate) unsafe fn name_raw<'a>(ptr: *const Self) -> &'a CStr {
742        // SAFETY: Caller ensures that the pointer is valid
743        let name = unsafe { ffi::class_getName(ptr) };
744        if name.is_null() {
745            panic!("class name was NULL");
746        }
747        // SAFETY: We've checked that the pointer is not NULL, and
748        // `class_getName` is guaranteed to return a valid C-string.
749        //
750        // That the result is properly bounded is checked by the caller.
751        unsafe { CStr::from_ptr(name) }
752    }
753
754    /// Returns the name of the class.
755    #[inline]
756    #[doc(alias = "class_getName")]
757    pub fn name(&self) -> &CStr {
758        // SAFETY: The pointer is valid, and the return is properly bounded
759        unsafe { Self::name_raw(self) }
760    }
761
762    /// # Safety
763    ///
764    /// 1. The class pointer must be valid.
765    /// 2. The caller must bound the lifetime of the returned class.
766    #[inline]
767    pub(crate) unsafe fn superclass_raw<'a>(ptr: *const Self) -> Option<&'a AnyClass> {
768        // SAFETY: Caller ensures that the pointer is valid
769        let superclass = unsafe { ffi::class_getSuperclass(ptr) };
770        // SAFETY: The result is properly bounded by the caller.
771        unsafe { superclass.as_ref() }
772    }
773
774    /// Returns the superclass of self, or [`None`] if self is a root class.
775    #[inline]
776    #[doc(alias = "class_getSuperclass")]
777    pub fn superclass(&self) -> Option<&AnyClass> {
778        // SAFETY: The pointer is valid, and the return is properly bounded
779        unsafe { Self::superclass_raw(self) }
780    }
781
782    /// Returns the metaclass of self.
783    ///
784    ///
785    /// # Example
786    ///
787    /// Get the metaclass of an object.
788    ///
789    /// ```
790    /// use objc2::runtime::NSObject;
791    /// use objc2::ClassType;
792    ///
793    /// let cls = NSObject::class();
794    /// let metacls = cls.metaclass();
795    ///
796    /// assert_eq!(metacls.name(), c"NSObject");
797    /// ```
798    #[inline]
799    #[doc(alias = "object_getClass")]
800    #[doc(alias = "objc_getMetaClass")] // Same as `AnyClass::get(name).metaclass()`
801    pub fn metaclass(&self) -> &Self {
802        let ptr: *const Self = self;
803        let ptr = unsafe { ffi::object_getClass(ptr.cast()) };
804        unsafe { ptr.as_ref().unwrap_unchecked() }
805    }
806
807    /// Whether the class is a metaclass.
808    ///
809    ///
810    /// # Example
811    ///
812    /// ```
813    /// use objc2::runtime::NSObject;
814    /// use objc2::ClassType;
815    ///
816    /// let cls = NSObject::class();
817    /// let metacls = cls.metaclass();
818    ///
819    /// assert!(!cls.is_metaclass());
820    /// assert!(metacls.is_metaclass());
821    /// ```
822    #[inline]
823    #[doc(alias = "class_isMetaClass")]
824    pub fn is_metaclass(&self) -> bool {
825        unsafe { ffi::class_isMetaClass(self).as_bool() }
826    }
827
828    /// Returns the size of instances of self.
829    #[inline]
830    #[doc(alias = "class_getInstanceSize")]
831    pub fn instance_size(&self) -> usize {
832        unsafe { ffi::class_getInstanceSize(self) }
833    }
834
835    /// Returns a specified instance method for self, or [`None`] if self and
836    /// its superclasses do not contain an instance method with the specified
837    /// selector.
838    #[inline]
839    #[doc(alias = "class_getInstanceMethod")]
840    pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
841        unsafe {
842            let method = ffi::class_getInstanceMethod(self, sel);
843            method.as_ref()
844        }
845    }
846
847    /// Returns a specified class method for self, or [`None`] if self and
848    /// its superclasses do not contain a class method with the specified
849    /// selector.
850    ///
851    /// Same as `cls.metaclass().class_method()`.
852    #[inline]
853    #[doc(alias = "class_getClassMethod")]
854    pub fn class_method(&self, sel: Sel) -> Option<&Method> {
855        unsafe {
856            let method = ffi::class_getClassMethod(self, sel);
857            method.as_ref()
858        }
859    }
860
861    /// Returns the ivar for a specified instance variable of self, or
862    /// [`None`] if self has no ivar with the given name.
863    ///
864    /// If the instance variable was not found on the specified class, the
865    /// superclasses are searched.
866    ///
867    /// Attempting to access or modify instance variables of a class that you
868    /// do no control may invoke undefined behaviour.
869    #[inline]
870    #[doc(alias = "class_getInstanceVariable")]
871    pub fn instance_variable(&self, name: &CStr) -> Option<&Ivar> {
872        unsafe {
873            let ivar = ffi::class_getInstanceVariable(self, name.as_ptr());
874            ivar.as_ref()
875        }
876    }
877
878    #[allow(unused)]
879    #[inline]
880    #[doc(alias = "class_getClassVariable")]
881    fn class_variable(&self, name: &CStr) -> Option<&Ivar> {
882        let ivar = unsafe { ffi::class_getClassVariable(self, name.as_ptr()) };
883        // SAFETY: TODO
884        unsafe { ivar.as_ref() }
885    }
886
887    /// Describes the instance methods implemented by self.
888    #[doc(alias = "class_copyMethodList")]
889    pub fn instance_methods(&self) -> MallocSlice!(&Method) {
890        unsafe {
891            let mut count: c_uint = 0;
892            let methods: *mut &Method = ffi::class_copyMethodList(self, &mut count).cast();
893            MallocSlice::from_array(methods, count as usize)
894        }
895    }
896
897    /// Checks whether this class conforms to the specified protocol.
898    #[inline]
899    #[doc(alias = "class_conformsToProtocol")]
900    pub fn conforms_to(&self, proto: &AnyProtocol) -> bool {
901        unsafe { ffi::class_conformsToProtocol(self, proto).as_bool() }
902    }
903
904    /// Get a list of the protocols to which this class conforms.
905    #[doc(alias = "class_copyProtocolList")]
906    pub fn adopted_protocols(&self) -> MallocSlice!(&AnyProtocol) {
907        unsafe {
908            let mut count: c_uint = 0;
909            let protos: *mut &AnyProtocol = ffi::class_copyProtocolList(self, &mut count).cast();
910            MallocSlice::from_array(protos, count as usize)
911        }
912    }
913
914    /// Get a list of instance variables on the class.
915    #[doc(alias = "class_copyIvarList")]
916    pub fn instance_variables(&self) -> MallocSlice!(&Ivar) {
917        unsafe {
918            let mut count: c_uint = 0;
919            let ivars: *mut &Ivar = ffi::class_copyIvarList(self, &mut count).cast();
920            MallocSlice::from_array(ivars, count as usize)
921        }
922    }
923
924    /// Check whether instances of this class respond to the given selector.
925    ///
926    /// This doesn't call `respondsToSelector:`, but works entirely within the
927    /// runtime, which means it'll always be safe to call, but may not return
928    /// exactly what you'd expect if `respondsToSelector:` has been
929    /// overwritten.
930    ///
931    /// That said, it will always return `true` if an instance of the class
932    /// responds to the selector, but may return `false` if they don't
933    /// directly (e.g. does so by using forwarding instead).
934    #[inline]
935    #[doc(alias = "class_respondsToSelector")]
936    pub fn responds_to(&self, sel: Sel) -> bool {
937        // This may call `resolveInstanceMethod:` and `resolveClassMethod:`
938        // SAFETY: The selector is guaranteed non-null.
939        unsafe { ffi::class_respondsToSelector(self, sel).as_bool() }
940    }
941
942    // <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html>
943    // fn property(&self, name: &CStr) -> Option<&Property>;
944    // fn properties(&self) -> MallocSlice!(&Property);
945    // unsafe fn replace_method(&self, name: Sel, imp: Imp, types: &CStr) -> Imp;
946    // unsafe fn replace_property(&self, name: &CStr, attributes: &[ffi::objc_property_attribute_t]);
947    // fn method_imp(&self, name: Sel) -> Imp; // + _stret
948
949    // fn get_version(&self) -> u32;
950    // unsafe fn set_version(&mut self, version: u32);
951
952    /// Verify argument and return types for a given selector.
953    ///
954    /// This will look up the encoding of the method for the given selector
955    /// and return a [`VerificationError`] if any encodings differ for the
956    /// arguments `A` and return type `R`.
957    ///
958    ///
959    /// # Example
960    ///
961    /// ```
962    /// use objc2::{class, sel};
963    /// use objc2::runtime::{AnyClass, Bool};
964    /// let cls = class!(NSObject);
965    /// let sel = sel!(isKindOfClass:);
966    /// // Verify that `isKindOfClass:`:
967    /// // - Exists on the class
968    /// // - Takes a class as a parameter
969    /// // - Returns a BOOL
970    /// let result = cls.verify_sel::<(&AnyClass,), Bool>(sel);
971    /// assert!(result.is_ok());
972    /// ```
973    #[allow(clippy::missing_errors_doc)] // Written differently in the docs
974    pub fn verify_sel<A, R>(&self, sel: Sel) -> Result<(), VerificationError>
975    where
976        A: EncodeArguments,
977        R: EncodeReturn,
978    {
979        let method = self.instance_method(sel).ok_or(Inner::MethodNotFound)?;
980        verify_method_signature(method, A::ENCODINGS, &R::ENCODING_RETURN)
981    }
982}
983
984standard_pointer_impls!(AnyClass);
985
986unsafe impl RefEncode for AnyClass {
987    const ENCODING_REF: Encoding = Encoding::Class;
988}
989
990// SAFETY: Classes act as objects, and can be sent messages.
991unsafe impl Message for AnyClass {}
992
993impl fmt::Debug for AnyClass {
994    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
995        f.debug_struct("AnyClass")
996            .field("name", &self.name())
997            .finish_non_exhaustive()
998    }
999}
1000
1001impl fmt::Display for AnyClass {
1002    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1003        // Classes are usually UTF-8, so it's probably fine to do a lossy
1004        // conversion here.
1005        fmt::Display::fmt(&self.name().to_string_lossy(), f)
1006    }
1007}
1008
1009impl AsRef<Self> for AnyClass {
1010    fn as_ref(&self) -> &Self {
1011        self
1012    }
1013}
1014
1015// This is the same as what Swift allows (`AnyClass` coerces to `AnyObject`).
1016impl AsRef<AnyObject> for AnyClass {
1017    fn as_ref(&self) -> &AnyObject {
1018        &self.inner
1019    }
1020}
1021
1022/// An opaque type that represents a protocol in the Objective-C runtime.
1023///
1024/// See [`ProtocolObject`] for objects that implement a specific protocol.
1025//
1026// The naming of this follows GNUStep; this struct does not exist in Apple's
1027// runtime, there `Protocol` is a type alias of `objc_object`.
1028#[repr(C)]
1029#[doc(alias = "objc_protocol")]
1030pub struct AnyProtocol {
1031    inner: AnyObject,
1032}
1033
1034/// Use [`AnyProtocol`] instead.
1035#[deprecated = "renamed to `runtime::AnyProtocol`"]
1036pub type Protocol = AnyProtocol;
1037
1038// SAFETY: AnyProtocol is immutable (and can be retrieved from AnyClass anyhow).
1039unsafe impl Sync for AnyProtocol {}
1040unsafe impl Send for AnyProtocol {}
1041impl UnwindSafe for AnyProtocol {}
1042impl RefUnwindSafe for AnyProtocol {}
1043// Note that Unpin is not applicable.
1044
1045impl AnyProtocol {
1046    /// Returns the protocol definition of a specified protocol, or [`None`]
1047    /// if the protocol is not registered with the Objective-C runtime.
1048    #[inline]
1049    #[doc(alias = "objc_getProtocol")]
1050    pub fn get(name: &CStr) -> Option<&'static Self> {
1051        unsafe {
1052            let proto = ffi::objc_getProtocol(name.as_ptr());
1053            proto.cast::<Self>().as_ref()
1054        }
1055    }
1056
1057    /// Obtains the list of registered protocol definitions.
1058    #[doc(alias = "objc_copyProtocolList")]
1059    pub fn protocols() -> MallocSlice!(&'static Self) {
1060        unsafe {
1061            let mut count: c_uint = 0;
1062            let protocols: *mut &Self = ffi::objc_copyProtocolList(&mut count).cast();
1063            MallocSlice::from_array(protocols, count as usize)
1064        }
1065    }
1066
1067    /// Get a list of the protocols to which this protocol conforms.
1068    #[doc(alias = "protocol_copyProtocolList")]
1069    pub fn adopted_protocols(&self) -> MallocSlice!(&AnyProtocol) {
1070        unsafe {
1071            let mut count: c_uint = 0;
1072            let protocols: *mut &AnyProtocol =
1073                ffi::protocol_copyProtocolList(self, &mut count).cast();
1074            MallocSlice::from_array(protocols, count as usize)
1075        }
1076    }
1077
1078    /// Checks whether this protocol conforms to the specified protocol.
1079    #[inline]
1080    #[doc(alias = "protocol_conformsToProtocol")]
1081    pub fn conforms_to(&self, proto: &AnyProtocol) -> bool {
1082        unsafe { ffi::protocol_conformsToProtocol(self, proto).as_bool() }
1083    }
1084
1085    /// Returns the name of self.
1086    #[inline]
1087    #[doc(alias = "protocol_getName")]
1088    pub fn name(&self) -> &CStr {
1089        unsafe { CStr::from_ptr(ffi::protocol_getName(self)) }
1090    }
1091
1092    fn method_descriptions_inner(&self, required: bool, instance: bool) -> Vec<MethodDescription> {
1093        let mut count: c_uint = 0;
1094        let descriptions = unsafe {
1095            ffi::protocol_copyMethodDescriptionList(
1096                self,
1097                Bool::new(required),
1098                Bool::new(instance),
1099                &mut count,
1100            )
1101        };
1102        if descriptions.is_null() {
1103            return Vec::new();
1104        }
1105        let descriptions = unsafe { MallocSlice::from_array(descriptions, count as usize) };
1106        descriptions
1107            .iter()
1108            .map(|desc| {
1109                unsafe { MethodDescription::from_raw(*desc) }.expect("invalid method description")
1110            })
1111            .collect()
1112    }
1113
1114    #[allow(dead_code)]
1115    #[doc(alias = "protocol_copyMethodDescriptionList")]
1116    pub(crate) fn method_descriptions(&self, required: bool) -> Vec<MethodDescription> {
1117        self.method_descriptions_inner(required, true)
1118    }
1119
1120    #[allow(dead_code)]
1121    #[doc(alias = "protocol_copyMethodDescriptionList")]
1122    pub(crate) fn class_method_descriptions(&self, required: bool) -> Vec<MethodDescription> {
1123        self.method_descriptions_inner(required, false)
1124    }
1125}
1126
1127impl PartialEq for AnyProtocol {
1128    /// Check whether the protocols are equal, or conform to each other.
1129    #[inline]
1130    #[doc(alias = "protocol_isEqual")]
1131    fn eq(&self, other: &Self) -> bool {
1132        unsafe { ffi::protocol_isEqual(self, other).as_bool() }
1133    }
1134}
1135
1136impl Eq for AnyProtocol {}
1137
1138// Don't implement `Hash` for protocol, it is unclear how that would work
1139
1140unsafe impl RefEncode for AnyProtocol {
1141    // Protocols are objects internally.
1142    const ENCODING_REF: Encoding = Encoding::Object;
1143}
1144
1145/// Note that protocols are objects, though sending messages to them is
1146/// officially deprecated.
1147//
1148// SAFETY: Protocols are NSObjects internally (and somewhat publicly, see e.g.
1149// `objc/Protocol.h`), and are returned as `Retained` in various places in
1150// Foundation. But that's considered deprecated, so we don't implement
1151// ClassType for them (even though the "Protocol" class exists).
1152unsafe impl Message for AnyProtocol {}
1153
1154impl fmt::Debug for AnyProtocol {
1155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1156        f.debug_struct("AnyProtocol")
1157            .field("name", &self.name())
1158            .finish_non_exhaustive()
1159    }
1160}
1161
1162impl fmt::Display for AnyProtocol {
1163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1164        // Protocols are usually UTF-8, so it's probably fine to do a lossy
1165        // conversion here.
1166        fmt::Display::fmt(&self.name().to_string_lossy(), f)
1167    }
1168}
1169
1170impl AsRef<Self> for AnyProtocol {
1171    fn as_ref(&self) -> &Self {
1172        self
1173    }
1174}
1175
1176impl AsRef<AnyObject> for AnyProtocol {
1177    fn as_ref(&self) -> &AnyObject {
1178        &self.inner
1179    }
1180}
1181
1182/// An Objective-C object.
1183///
1184/// This is slightly different from [`NSObject`] in that it may represent an
1185/// instance of an _arbitrary_ Objective-C class (e.g. it does not have to be
1186/// a subclass of `NSObject`, so it can represent other root classes like
1187/// `NSProxy`).
1188///
1189/// `Retained<AnyObject>` is equivalent to Objective-C's `id _Nonnull`.
1190///
1191/// This is an opaque type that contains [`UnsafeCell`], and is similar to
1192/// that in that one can safely access and perform interior mutability on this
1193/// (both via [`msg_send!`] and through ivars), so long as Rust's mutability
1194/// rules are upheld, and that data races are avoided.
1195///
1196/// Note: This is intentionally neither [`Sync`], [`Send`], [`UnwindSafe`],
1197/// [`RefUnwindSafe`] nor [`Unpin`], since that is something that may change
1198/// depending on the specific subclass. For example, `NSAutoreleasePool` is
1199/// not `Send`, it has to be deallocated on the same thread that it was
1200/// created. `NSLock` is not `Send` either.
1201///
1202/// [`UnsafeCell`]: core::cell::UnsafeCell
1203/// [`msg_send!`]: crate::msg_send
1204#[doc(alias = "id")]
1205#[doc(alias = "objc_object")]
1206#[repr(C)]
1207pub struct AnyObject {
1208    // `isa` field is deprecated, so we don't expose it here.
1209    //
1210    // Also, we need this to be a zero-sized, so that the compiler doesn't
1211    // assume anything about the layout.
1212    //
1213    // Use `object_getClass` instead.
1214    _priv: [u8; 0],
1215    _p: ffi::OpaqueData,
1216}
1217
1218/// Use [`AnyObject`] instead.
1219#[deprecated = "renamed to `runtime::AnyObject`. Consider using the correct type from the autogenerated `objc2-*` framework crates instead though"]
1220pub type Object = AnyObject;
1221
1222unsafe impl RefEncode for AnyObject {
1223    const ENCODING_REF: Encoding = Encoding::Object;
1224}
1225
1226// SAFETY: This is technically slightly wrong, not all objects implement the
1227// standard memory management methods. But not having this impl would be too
1228// restrictive, so we'll live with it.
1229//
1230// NOTE: AnyObject actually resolves to the class "Object" internally, but we
1231// don't want to expose that publicly, so we only implement Message here, not
1232// ClassType.
1233unsafe impl Message for AnyObject {}
1234
1235impl AnyObject {
1236    /// Dynamically find the class of this object.
1237    ///
1238    ///
1239    /// # Panics
1240    ///
1241    /// May panic if the object is invalid (which may be the case for objects
1242    /// returned from unavailable `init`/`new` methods).
1243    ///
1244    ///
1245    /// # Example
1246    ///
1247    /// Check that an instance of `NSObject` has the precise class `NSObject`.
1248    ///
1249    /// ```
1250    /// use objc2::ClassType;
1251    /// use objc2::runtime::NSObject;
1252    ///
1253    /// let obj = NSObject::new();
1254    /// assert_eq!(obj.class(), NSObject::class());
1255    /// ```
1256    #[inline]
1257    #[doc(alias = "object_getClass")]
1258    pub fn class(&self) -> &'static AnyClass {
1259        let ptr = unsafe { ffi::object_getClass(self) };
1260        // SAFETY: The pointer is valid, and it is safe as `'static` since
1261        // classes are static (can also be verified with the fact that they
1262        // can be retrieved via `AnyClass::get(self.class().name())`).
1263        let cls = unsafe { ptr.as_ref() };
1264
1265        // The class _should_ not be NULL, because the docs only say that if
1266        // the object is NULL, the class also is; and in practice, certain
1267        // invalid objects can contain a NULL isa pointer.
1268        cls.unwrap_or_else(|| panic!("invalid object {:?} (had NULL class)", self as *const Self))
1269    }
1270
1271    /// Change the class of the object at runtime.
1272    ///
1273    /// Returns the object's previous class.
1274    ///
1275    ///
1276    /// # Safety
1277    ///
1278    /// The new class must:
1279    ///
1280    /// 1. Be a subclass of the object's current class.
1281    ///
1282    /// 2. The subclass must not add any instance variables - importantly, the
1283    ///    instance size of old and the new classes must be the same.
1284    ///
1285    /// 3. Any overridden methods on the new class must be fully compatible
1286    ///    with the old ones.
1287    ///
1288    /// Note that in the general case, where arbitrary parts of the program
1289    /// may be trying to modify the class of the object concurrently, these
1290    /// requirements are not actually possible to uphold.
1291    ///
1292    /// Since usage of this function is expected to be extremely rare, and
1293    /// even more so trying to do it concurrently, it is recommended that you
1294    /// verify that the returned class is what you would expect, and if not,
1295    /// panic.
1296    #[inline]
1297    #[doc(alias = "object_setClass")]
1298    pub unsafe fn set_class<'s>(this: &Self, cls: &AnyClass) -> &'s AnyClass {
1299        let this: *const Self = this;
1300        let this = this as *mut Self;
1301        let ptr = unsafe { ffi::object_setClass(this, cls) };
1302        // SAFETY: The class is not NULL because the object is not NULL.
1303        let old_cls = unsafe { ptr.as_ref().unwrap_unchecked() };
1304        // TODO: Check the superclass requirement too?
1305        debug_assert_eq!(
1306            old_cls.instance_size(),
1307            cls.instance_size(),
1308            "old and new class sizes were not equal; this is UB!"
1309        );
1310        old_cls
1311    }
1312
1313    /// Offset an object pointer to get a pointer to an ivar.
1314    ///
1315    ///
1316    /// # Safety
1317    ///
1318    /// The offset must be valid for the given type.
1319    #[inline]
1320    pub(crate) unsafe fn ivar_at_offset<T>(ptr: NonNull<Self>, offset: isize) -> NonNull<T> {
1321        // `offset` is given in bytes, so we convert to `u8` and back to `T`
1322        let ptr: NonNull<u8> = ptr.cast();
1323        let ptr: *mut u8 = ptr.as_ptr();
1324        // SAFETY: The offset is valid
1325        let ptr: *mut u8 = unsafe { ptr.offset(offset) };
1326        // SAFETY: The offset operation is guaranteed to not end up computing
1327        // a NULL pointer.
1328        let ptr: NonNull<u8> = unsafe { NonNull::new_unchecked(ptr) };
1329        let ptr: NonNull<T> = ptr.cast();
1330        ptr
1331    }
1332
1333    pub(crate) fn lookup_instance_variable_dynamically(&self, name: &str) -> &'static Ivar {
1334        let name = CString::new(name).unwrap();
1335        let cls = self.class();
1336        cls.instance_variable(&name)
1337            .unwrap_or_else(|| panic!("ivar {name:?} not found on class {cls}"))
1338    }
1339
1340    /// Use [`Ivar::load`] instead.
1341    ///
1342    ///
1343    /// # Safety
1344    ///
1345    /// The object must have an instance variable with the given name, and it
1346    /// must be of type `T`.
1347    ///
1348    /// See [`Ivar::load_ptr`] for details surrounding this.
1349    #[deprecated = "this is difficult to use correctly, use `Ivar::load` instead."]
1350    pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
1351        let ivar = self.lookup_instance_variable_dynamically(name);
1352        // SAFETY: Upheld by caller
1353        unsafe { ivar.load::<T>(self) }
1354    }
1355
1356    /// Use [`Ivar::load_mut`] instead.
1357    ///
1358    ///
1359    /// # Safety
1360    ///
1361    /// The object must have an instance variable with the given name, and it
1362    /// must be of type `T`.
1363    ///
1364    /// See [`Ivar::load_ptr`] for details surrounding this.
1365    #[deprecated = "this is difficult to use correctly, use `Ivar::load_mut` instead."]
1366    pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
1367        let ivar = self.lookup_instance_variable_dynamically(name);
1368        // SAFETY: Upheld by caller
1369        unsafe { ivar.load_mut::<T>(self) }
1370    }
1371
1372    pub(crate) fn is_kind_of_class(&self, cls: &AnyClass) -> Bool {
1373        // SAFETY: The signature is correct.
1374        //
1375        // Note that `isKindOfClass:` is not available on every object, but it
1376        // is still safe to _use_, since the runtime will simply crash if the
1377        // selector isn't implemented. This is of course not _ideal_, but it
1378        // works for all of Apple's Objective-C classes, and it's what Swift
1379        // does.
1380        //
1381        // In theory, someone could have made a root object, and overwritten
1382        // `isKindOfClass:` to do something bogus - but that would conflict
1383        // with normal Objective-C code as well, so we will consider such a
1384        // thing unsound by construction.
1385        unsafe { msg_send![self, isKindOfClass: cls] }
1386    }
1387
1388    /// Attempt to downcast the object to a class of type `T`.
1389    ///
1390    /// This is the reference-variant. Use [`Retained::downcast`] if you want
1391    /// to convert a retained object to another type.
1392    ///
1393    /// [`Retained::downcast`]: crate::rc::Retained::downcast
1394    ///
1395    ///
1396    /// # Mutable classes
1397    ///
1398    /// Some classes have immutable and mutable variants, such as `NSString`
1399    /// and `NSMutableString`.
1400    ///
1401    /// When some Objective-C API signature says it gives you an immutable
1402    /// class, it generally expects you to not mutate that, even though it may
1403    /// technically be mutable "under the hood".
1404    ///
1405    /// So using this method to convert a `NSString` to a `NSMutableString`,
1406    /// while not unsound, is generally frowned upon unless you created the
1407    /// string yourself, or the API explicitly documents the string to be
1408    /// mutable.
1409    ///
1410    /// See Apple's [documentation on mutability][apple-mut] and [on
1411    /// `isKindOfClass:`][iskindof-doc] for more details.
1412    ///
1413    /// [iskindof-doc]: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418511-iskindofclass?language=objc
1414    /// [apple-mut]: https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectMutability/ObjectMutability.html
1415    ///
1416    ///
1417    /// # Generic classes
1418    ///
1419    /// Objective-C generics are called "lightweight generics", and that's
1420    /// because they aren't exposed in the runtime. This makes it impossible
1421    /// to safely downcast to generic collections, so this is disallowed by
1422    /// this method.
1423    ///
1424    /// You can, however, safely downcast to generic collections where all the
1425    /// type-parameters are [`AnyObject`].
1426    ///
1427    ///
1428    /// # Panics
1429    ///
1430    /// This works internally by calling `isKindOfClass:`. That means that the
1431    /// object must have the instance method of that name, and an exception
1432    /// will be thrown (if CoreFoundation is linked) or the process will abort
1433    /// if that is not the case. In the vast majority of cases, you don't need
1434    /// to worry about this, since both root objects [`NSObject`] and
1435    /// `NSProxy` implement this method.
1436    ///
1437    ///
1438    /// # Examples
1439    ///
1440    /// Cast an `NSString` back and forth from `NSObject`.
1441    ///
1442    /// ```
1443    /// use objc2::rc::Retained;
1444    /// use objc2_foundation::{NSObject, NSString};
1445    ///
1446    /// let obj: Retained<NSObject> = NSString::new().into_super();
1447    /// let string = obj.downcast_ref::<NSString>().unwrap();
1448    /// // Or with `downcast`, if we do not need the object afterwards
1449    /// let string = obj.downcast::<NSString>().unwrap();
1450    /// ```
1451    ///
1452    /// Try (and fail) to cast an `NSObject` to an `NSString`.
1453    ///
1454    /// ```
1455    /// use objc2_foundation::{NSObject, NSString};
1456    ///
1457    /// let obj = NSObject::new();
1458    /// assert!(obj.downcast_ref::<NSString>().is_none());
1459    /// ```
1460    ///
1461    /// Try to cast to an array of strings.
1462    ///
1463    /// ```compile_fail,E0277
1464    /// use objc2_foundation::{NSArray, NSObject, NSString};
1465    ///
1466    /// let arr = NSArray::from_retained_slice(&[NSObject::new()]);
1467    /// // This is invalid and doesn't type check.
1468    /// let arr = arr.downcast_ref::<NSArray<NSString>>();
1469    /// ```
1470    ///
1471    /// This fails to compile, since it would require enumerating over the
1472    /// array to ensure that each element is of the desired type, which is a
1473    /// performance pitfall.
1474    ///
1475    /// Downcast when processing each element instead.
1476    ///
1477    /// ```
1478    /// use objc2_foundation::{NSArray, NSObject, NSString};
1479    ///
1480    /// let arr = NSArray::from_retained_slice(&[NSObject::new()]);
1481    ///
1482    /// for elem in arr {
1483    ///     if let Some(data) = elem.downcast_ref::<NSString>() {
1484    ///         // handle `data`
1485    ///     }
1486    /// }
1487    /// ```
1488    #[inline]
1489    pub fn downcast_ref<T: DowncastTarget>(&self) -> Option<&T> {
1490        if self.is_kind_of_class(T::class()).as_bool() {
1491            // SAFETY: Just checked that the object is a class of type `T`.
1492            //
1493            // Generic `T` like `NSArray<NSString>` are ruled out by
1494            // `T: DowncastTarget`.
1495            Some(unsafe { &*(self as *const Self).cast::<T>() })
1496        } else {
1497            None
1498        }
1499    }
1500
1501    // objc_setAssociatedObject
1502    // objc_getAssociatedObject
1503    // objc_removeAssociatedObjects
1504}
1505
1506impl fmt::Debug for AnyObject {
1507    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1508        let ptr: *const Self = self;
1509        write!(f, "<{}: {:p}>", self.class(), ptr)
1510    }
1511}
1512
1513#[cfg(test)]
1514mod tests {
1515    use alloc::ffi::CString;
1516    use alloc::format;
1517    use alloc::string::ToString;
1518    use core::mem::size_of;
1519
1520    use super::*;
1521    use crate::test_utils;
1522    use crate::{class, msg_send, sel, ClassType, ProtocolType};
1523
1524    // TODO: Remove once c"" strings are in MSRV
1525    fn c(s: &str) -> CString {
1526        CString::new(s).unwrap()
1527    }
1528
1529    #[test]
1530    fn test_selector() {
1531        macro_rules! test_sel {
1532            ($s:literal, $($tt:tt)+) => {{
1533                let sel = sel!($($tt)*);
1534                let expected = Sel::register(&c($s));
1535                assert_eq!(sel, expected);
1536                assert_eq!(sel.name().to_str(), Ok($s));
1537            }}
1538        }
1539        test_sel!("abc", abc);
1540        test_sel!("abc:", abc:);
1541        test_sel!("abc:def:", abc:def:);
1542        test_sel!("abc:def:ghi:", abc:def:ghi:);
1543        test_sel!("functionWithControlPoints::::", functionWithControlPoints::::);
1544        test_sel!("initWithControlPoints::::", initWithControlPoints::::);
1545        test_sel!("setFloatValue::", setFloatValue::);
1546        test_sel!("isSupported::", isSupported::);
1547        test_sel!("addEventListener:::", addEventListener:::);
1548        test_sel!("test::arg::", test::arg::);
1549        test_sel!("test::::with::spaces::", test : :: : with : : spaces : :);
1550        test_sel!("a::b:", a::b:);
1551    }
1552
1553    #[test]
1554    fn test_empty_selector() {
1555        let s = c("");
1556        let sel = Sel::register(&s);
1557        assert_eq!(sel.name(), &*s);
1558        let s = c(":");
1559        let sel = Sel::register(&s);
1560        assert_eq!(sel.name(), &*s);
1561        let s = c("::");
1562        let sel = Sel::register(&s);
1563        assert_eq!(sel.name(), &*s);
1564    }
1565
1566    #[test]
1567    fn test_ivar() {
1568        let cls = test_utils::custom_class();
1569        let ivar = cls.instance_variable(&c("_foo")).unwrap();
1570        assert_eq!(ivar.name(), &*c("_foo"));
1571        assert!(<u32>::ENCODING.equivalent_to_str(ivar.type_encoding().to_str().unwrap()));
1572        assert!(ivar.offset() > 0);
1573        assert!(cls.instance_variables().len() > 0);
1574    }
1575
1576    #[test]
1577    fn test_instance_method() {
1578        let cls = test_utils::custom_class();
1579        let sel = Sel::register(&c("foo"));
1580        let method = cls.instance_method(sel).unwrap();
1581        assert_eq!(method.name().name(), &*c("foo"));
1582        assert_eq!(method.arguments_count(), 2);
1583
1584        assert!(<u32>::ENCODING.equivalent_to_str(method.return_type().to_str().unwrap()));
1585        assert!(Sel::ENCODING.equivalent_to_str(method.argument_type(1).unwrap().to_str().unwrap()));
1586
1587        assert!(cls.instance_methods().contains(&method));
1588    }
1589
1590    #[test]
1591    fn test_class_method() {
1592        let cls = test_utils::custom_class();
1593        let method = cls.class_method(sel!(classFoo)).unwrap();
1594        assert_eq!(method.name().name(), &*c("classFoo"));
1595        assert_eq!(method.arguments_count(), 2);
1596
1597        assert!(<u32>::ENCODING.equivalent_to_str(method.return_type().to_str().unwrap()));
1598        assert!(Sel::ENCODING.equivalent_to_str(method.argument_type(1).unwrap().to_str().unwrap()));
1599
1600        assert!(cls.metaclass().instance_methods().contains(&method));
1601    }
1602
1603    #[test]
1604    fn test_class() {
1605        let cls = test_utils::custom_class();
1606        assert_eq!(cls.name(), &*c("CustomObject"));
1607        assert!(cls.instance_size() > 0);
1608        assert!(cls.superclass().is_none());
1609
1610        assert!(cls.responds_to(sel!(foo)));
1611        assert!(cls.responds_to(sel!(setBar:)));
1612        assert!(cls.responds_to(sel!(test::test::)));
1613        assert!(!cls.responds_to(sel!(abc)));
1614        assert!(!cls.responds_to(sel!(addNumber:toNumber:)));
1615
1616        assert_eq!(AnyClass::get(cls.name()), Some(cls));
1617
1618        let metaclass = cls.metaclass();
1619        // The metaclass of a root class is a subclass of the root class
1620        assert_eq!(metaclass.superclass().unwrap(), cls);
1621        assert!(metaclass.responds_to(sel!(addNumber:toNumber:)));
1622        assert!(metaclass.responds_to(sel!(test::test::)));
1623        // TODO: This is unexpected!
1624        assert!(metaclass.responds_to(sel!(foo)));
1625
1626        let subclass = test_utils::custom_subclass();
1627        assert_eq!(subclass.superclass().unwrap(), cls);
1628    }
1629
1630    #[test]
1631    fn test_classes_count() {
1632        assert!(AnyClass::classes_count() > 0);
1633    }
1634
1635    #[test]
1636    fn test_classes() {
1637        let classes = AnyClass::classes();
1638        assert!(classes.len() > 0);
1639    }
1640
1641    #[test]
1642    fn test_protocol() {
1643        let proto = test_utils::custom_protocol();
1644        assert_eq!(proto.name(), &*c("CustomProtocol"));
1645        let class = test_utils::custom_class();
1646        assert!(class.conforms_to(proto));
1647
1648        // The selectors are broken somehow on GNUStep < 2.0
1649        if cfg!(any(not(feature = "gnustep-1-7"), feature = "gnustep-2-0")) {
1650            let desc = MethodDescription {
1651                sel: sel!(setBar:),
1652                types: CStr::from_bytes_with_nul(b"v@:i\0").unwrap(),
1653            };
1654            assert_eq!(&proto.method_descriptions(true), &[desc]);
1655            let desc = MethodDescription {
1656                sel: sel!(getName),
1657                types: CStr::from_bytes_with_nul(b"*@:\0").unwrap(),
1658            };
1659            assert_eq!(&proto.method_descriptions(false), &[desc]);
1660            let desc = MethodDescription {
1661                sel: sel!(addNumber:toNumber:),
1662                types: CStr::from_bytes_with_nul(b"i@:ii\0").unwrap(),
1663            };
1664            assert_eq!(&proto.class_method_descriptions(true), &[desc]);
1665        }
1666        assert_eq!(&proto.class_method_descriptions(false), &[]);
1667
1668        assert!(class.adopted_protocols().contains(&proto));
1669    }
1670
1671    #[test]
1672    fn test_protocol_method() {
1673        let class = test_utils::custom_class();
1674        let result: i32 = unsafe { msg_send![class, addNumber: 1, toNumber: 2] };
1675        assert_eq!(result, 3);
1676    }
1677
1678    #[test]
1679    fn class_self() {
1680        let cls = NSObject::class();
1681        let result: &'static AnyClass = unsafe { msg_send![cls, self] };
1682        assert_eq!(cls, result);
1683    }
1684
1685    #[test]
1686    fn test_subprotocols() {
1687        let sub_proto = test_utils::custom_subprotocol();
1688        let super_proto = test_utils::custom_protocol();
1689        assert!(sub_proto.conforms_to(super_proto));
1690        assert_eq!(sub_proto.adopted_protocols()[0], super_proto);
1691    }
1692
1693    #[test]
1694    fn test_protocols() {
1695        // Ensure that a protocol has been registered on linux
1696        let _ = test_utils::custom_protocol();
1697
1698        assert!(AnyProtocol::protocols().len() > 0);
1699    }
1700
1701    #[test]
1702    fn test_object() {
1703        let obj = test_utils::custom_object();
1704        let cls = test_utils::custom_class();
1705        assert_eq!(obj.class(), cls);
1706
1707        let ivar = cls.instance_variable(&c("_foo")).unwrap();
1708
1709        unsafe { *ivar.load_ptr::<u32>(&obj) = 4 };
1710        let result = unsafe { *ivar.load::<u32>(&obj) };
1711        assert_eq!(result, 4);
1712    }
1713
1714    #[test]
1715    fn test_object_ivar_unknown() {
1716        let cls = test_utils::custom_class();
1717        assert_eq!(cls.instance_variable(&c("unknown")), None);
1718    }
1719
1720    #[test]
1721    fn test_no_ivars() {
1722        let cls = ClassBuilder::new(&c("NoIvarObject"), NSObject::class())
1723            .unwrap()
1724            .register();
1725        assert_eq!(cls.instance_variables().len(), 0);
1726    }
1727
1728    #[test]
1729    #[cfg_attr(
1730        all(debug_assertions, not(feature = "disable-encoding-assertions")),
1731        should_panic = "wrong encoding. Tried to retrieve ivar with encoding I, but the encoding of the given type was C"
1732    )]
1733    fn test_object_ivar_wrong_type() {
1734        let obj = test_utils::custom_object();
1735        let cls = test_utils::custom_class();
1736        let ivar = cls.instance_variable(&c("_foo")).unwrap();
1737        let _ = unsafe { *ivar.load::<u8>(&obj) };
1738    }
1739
1740    #[test]
1741    fn test_encode() {
1742        fn assert_enc<T: Encode>(expected: &str) {
1743            assert_eq!(&T::ENCODING.to_string(), expected);
1744        }
1745        assert_enc::<&AnyObject>("@");
1746        assert_enc::<*mut AnyObject>("@");
1747        assert_enc::<&AnyClass>("#");
1748        assert_enc::<Sel>(":");
1749        assert_enc::<Option<Sel>>(":");
1750        assert_enc::<Imp>("^?");
1751        assert_enc::<Option<Imp>>("^?");
1752        assert_enc::<&AnyProtocol>("@");
1753    }
1754
1755    #[test]
1756    fn test_send_sync() {
1757        fn assert_send_sync<T: Send + Sync + ?Sized>() {}
1758        assert_send_sync::<Bool>();
1759        assert_send_sync::<AnyClass>();
1760        assert_send_sync::<Ivar>();
1761        assert_send_sync::<Method>();
1762        assert_send_sync::<AnyProtocol>();
1763        assert_send_sync::<Sel>();
1764    }
1765
1766    #[test]
1767    fn test_debug_display() {
1768        let sel = sel!(abc:);
1769        assert_eq!(format!("{sel}"), "abc:");
1770        assert_eq!(format!("{sel:?}"), "Sel(\"abc:\")");
1771        let cls = test_utils::custom_class();
1772        assert_eq!(format!("{cls}"), "CustomObject");
1773        assert_eq!(
1774            format!("{cls:?}"),
1775            "AnyClass { name: \"CustomObject\", .. }"
1776        );
1777        let protocol = test_utils::custom_protocol();
1778        assert_eq!(format!("{protocol}"), "CustomProtocol");
1779        assert_eq!(
1780            format!("{protocol:?}"),
1781            "AnyProtocol { name: \"CustomProtocol\", .. }"
1782        );
1783
1784        let object = test_utils::custom_object();
1785        assert_eq!(
1786            format!("{:?}", &*object),
1787            format!("CustomObject(<CustomObject: {:p}>)", &*object)
1788        );
1789    }
1790
1791    #[test]
1792    fn test_multiple_colon() {
1793        let class = test_utils::custom_class();
1794        let res: i32 = unsafe {
1795            MessageReceiver::send_message(class, sel!(test::test::), (1i32, 2i32, 3i32, 4i32))
1796        };
1797        assert_eq!(res, 10);
1798
1799        let obj = test_utils::custom_object();
1800        let res: i32 = unsafe {
1801            MessageReceiver::send_message(&*obj, sel!(test::test::), (1i32, 2i32, 3i32, 4i32))
1802        };
1803        assert_eq!(res, 24);
1804    }
1805
1806    #[test]
1807    fn test_sizes() {
1808        assert_eq!(size_of::<Sel>(), size_of::<*const ()>());
1809        assert_eq!(size_of::<Sel>(), size_of::<Option<Sel>>());
1810
1811        // These must be zero-sized until we get extern types, otherwise the
1812        // optimizer may invalidly assume something about their layout.
1813        assert_eq!(size_of::<AnyClass>(), 0);
1814        assert_eq!(size_of::<AnyObject>(), 0);
1815        assert_eq!(size_of::<AnyProtocol>(), 0);
1816        assert_eq!(size_of::<Ivar>(), 0);
1817        assert_eq!(size_of::<Method>(), 0);
1818    }
1819
1820    fn get_ivar_layout(cls: &AnyClass) -> *const u8 {
1821        unsafe { ffi::class_getIvarLayout(cls) }
1822    }
1823
1824    #[test]
1825    #[cfg_attr(
1826        feature = "gnustep-1-7",
1827        ignore = "ivar layout is still used on GNUStep"
1828    )]
1829    fn test_layout_does_not_matter_any_longer() {
1830        assert!(get_ivar_layout(class!(NSObject)).is_null());
1831        assert!(get_ivar_layout(class!(NSArray)).is_null());
1832        assert!(get_ivar_layout(class!(NSException)).is_null());
1833        assert!(get_ivar_layout(class!(NSNumber)).is_null());
1834        assert!(get_ivar_layout(class!(NSString)).is_null());
1835    }
1836
1837    #[test]
1838    fn test_non_utf8_roundtrip() {
1839        // Some invalid UTF-8 character
1840        let s = CStr::from_bytes_with_nul(b"\x9F\0").unwrap();
1841
1842        let sel = Sel::register(s);
1843        assert_eq!(sel.name(), s);
1844        assert_eq!(sel.to_string(), char::REPLACEMENT_CHARACTER.to_string());
1845
1846        let cls = ClassBuilder::new(s, NSObject::class()).unwrap().register();
1847        assert_eq!(cls.name(), s);
1848        assert_eq!(cls.to_string(), char::REPLACEMENT_CHARACTER.to_string());
1849
1850        let cls_runtime = AnyClass::get(s).unwrap();
1851        assert_eq!(cls, cls_runtime);
1852    }
1853
1854    #[test]
1855    fn class_is_object() {
1856        let cls = NSObject::class();
1857        let retained = cls.retain();
1858        assert_eq!(&*retained, cls);
1859
1860        let obj: &AnyObject = cls.as_ref();
1861        let superclass = obj.class();
1862        assert!(superclass.conforms_to(<dyn NSObjectProtocol>::protocol().unwrap()));
1863
1864        // Classes are NSObject subclasses in the current runtime.
1865        let ns_obj = retained.downcast::<NSObject>().unwrap();
1866        // Test that we can call NSObject methods on classes.
1867        assert_eq!(ns_obj, ns_obj);
1868        let _ = ns_obj.retainCount();
1869    }
1870
1871    #[test]
1872    fn class_has_infinite_retain_count() {
1873        let obj: &AnyObject = NSObject::class().as_ref();
1874        let obj = obj.downcast_ref::<NSObject>().unwrap();
1875
1876        let large_retain = if cfg!(feature = "gnustep-1-7") {
1877            u32::MAX as usize
1878        } else {
1879            usize::MAX
1880        };
1881
1882        assert_eq!(obj.retainCount(), large_retain);
1883        let obj2 = obj.retain();
1884        assert_eq!(obj.retainCount(), large_retain);
1885        drop(obj2);
1886        assert_eq!(obj.retainCount(), large_retain);
1887    }
1888
1889    #[test]
1890    fn protocol_is_object() {
1891        let protocol = <dyn NSObjectProtocol>::protocol().unwrap();
1892        let retained = protocol.retain();
1893        assert_eq!(&*retained, protocol);
1894
1895        // Protocols don't implement isKindOfClass: on GNUStep.
1896        if cfg!(feature = "gnustep-1-7") {
1897            return;
1898        }
1899
1900        // In the old runtime, NSObjectProtocol are not NSObject subclasses.
1901        if cfg!(all(target_os = "macos", target_arch = "x86")) {
1902            let _ = retained.downcast::<NSObject>().unwrap_err();
1903        } else {
1904            // But elsewhere they are.
1905            let obj = retained.downcast::<NSObject>().unwrap();
1906            // Test that we can call NSObject methods on protocols.
1907            assert_eq!(obj, obj);
1908            let _ = obj.retainCount();
1909        }
1910    }
1911}