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
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
//! Functionality for dynamically declaring Objective-C classes.
//!
//! Classes can be declared using the [`ClassBuilder`] struct. Instance
//! variables and methods can then be added before the class is ultimately
//! registered.
//!
//! **Note**: You likely don't need the dynamicism that this module provides!
//! Consider using the [`declare_class!`][crate::declare_class] macro instead.
//!
//!
//! # Example
//!
//! The following example demonstrates declaring a class named `MyNumber` that
//! has one ivar, a `u32` named `_number` and a few methods for constructor
//! methods and methods for interfacing with the number.
//!
//! ```
//! use objc2::declare::ClassBuilder;
//! use objc2::foundation::NSObject;
//! use objc2::rc::{Id, Owned};
//! use objc2::runtime::{Class, Object, Sel};
//! use objc2::{class, sel, msg_send, msg_send_id, ClassType};
//! # #[cfg(feature = "gnustep-1-7")]
//! # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
//!
//! fn register_class() -> &'static Class {
//!     // Inherit from NSObject
//!     let mut builder = ClassBuilder::new("MyNumber", NSObject::class())
//!         .expect("a class with the name MyNumber likely already exists");
//!
//!     // Add an instance variable of type `u32`
//!     builder.add_ivar::<u32>("_number");
//!
//!     // Add an Objective-C method for initializing an instance with a number
//!     unsafe extern "C" fn init_with_number(
//!         this: &mut Object,
//!         _cmd: Sel,
//!         number: u32,
//!     ) -> Option<&mut Object> {
//!         let this: Option<&mut Object> = msg_send![super(this, NSObject::class()), init];
//!         this.map(|this| {
//!             // SAFETY: The ivar is added with the same type above
//!             this.set_ivar::<u32>("_number", number);
//!             this
//!         })
//!     }
//!     unsafe {
//!         builder.add_method(
//!             sel!(initWithNumber:),
//!             init_with_number as unsafe extern "C" fn(_, _, _) -> _,
//!         );
//!     }
//!
//!     // Add convenience method for getting a new instance with the number
//!     extern "C" fn with_number(
//!         cls: &Class,
//!         _cmd: Sel,
//!         number: u32,
//!     ) -> *mut Object {
//!         let obj: Option<Id<Object, Owned>> = unsafe {
//!             msg_send_id![
//!                 msg_send_id![cls, alloc],
//!                 initWithNumber: number,
//!             ]
//!         };
//!         obj.map(|obj| obj.autorelease_return()).unwrap_or(std::ptr::null_mut())
//!     }
//!     unsafe {
//!         builder.add_class_method(
//!             sel!(withNumber:),
//!             with_number as extern "C" fn(_, _, _) -> _,
//!         );
//!     }
//!
//!     // Add an Objective-C method for setting the number
//!     extern "C" fn my_number_set(this: &mut Object, _cmd: Sel, number: u32) {
//!         // SAFETY: The ivar is added with the same type above
//!         unsafe { this.set_ivar::<u32>("_number", number) }
//!     }
//!     unsafe {
//!         builder.add_method(sel!(setNumber:), my_number_set as extern "C" fn(_, _, _));
//!     }
//!
//!     // Add an Objective-C method for getting the number
//!     extern "C" fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
//!         // SAFETY: The ivar is added with the same type above
//!         unsafe { *this.ivar::<u32>("_number") }
//!     }
//!     unsafe {
//!         builder.add_method(sel!(number), my_number_get as extern "C" fn(_, _) -> _);
//!     }
//!
//!     builder.register()
//! }
//!
//! // Usage
//!
//! // Note: you should only do class registration once! This can be ensure
//! // with `std::sync::Once` or the `once_cell` crate.
//! let cls = register_class();
//!
//! let obj: Id<Object, Owned> = unsafe {
//!     msg_send_id![cls, withNumber: 42u32]
//! };
//!
//! let n: u32 = unsafe { msg_send![&obj, number] };
//! assert_eq!(n, 42);
//!
//! let _: () = unsafe { msg_send![&obj, setNumber: 12u32] };
//! let n: u32 = unsafe { msg_send![&obj, number] };
//! assert_eq!(n, 12);
//! ```

mod ivar;
mod ivar_drop;
mod ivar_forwarding_impls;

use alloc::format;
use alloc::string::ToString;
use core::mem;
use core::mem::ManuallyDrop;
use core::ptr;
use core::ptr::NonNull;
use std::ffi::CString;

use crate::encode::{Encode, EncodeArguments, Encoding, RefEncode};
use crate::ffi;
use crate::runtime::{Bool, Class, Imp, Object, Protocol, Sel};
use crate::sel;
use crate::Message;

pub use ivar::{InnerIvarType, Ivar, IvarType};
pub use ivar_drop::IvarDrop;

pub(crate) mod private {
    pub trait Sealed {}
}

/// Types that can be used as the implementation of an Objective-C method.
///
/// This is a sealed trait that is implemented for a lot of `extern "C"`
/// function pointer types.
pub trait MethodImplementation: private::Sealed {
    /// The callee type of the method.
    type Callee: RefEncode + ?Sized;
    /// The return type of the method.
    type Ret: Encode;
    /// The argument types of the method.
    type Args: EncodeArguments;

    #[doc(hidden)]
    fn __imp(self) -> Imp;
}

macro_rules! method_decl_impl {
    (@<$($l:lifetime),*> T, $r:ident, $f:ty, $($t:ident),*) => {
        impl<$($l,)* T, $r, $($t),*> private::Sealed for $f
        where
            T: Message + ?Sized,
            $r: Encode,
            $($t: Encode,)*
        {}

        impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f
        where
            T: Message + ?Sized,
            $r: Encode,
            $($t: Encode,)*
        {
            type Callee = T;
            type Ret = $r;
            type Args = ($($t,)*);

            fn __imp(self) -> Imp {
                unsafe { mem::transmute(self) }
            }
        }
    };
    (@<$($l:lifetime),*> Class, $r:ident, $f:ty, $($t:ident),*) => {
        impl<$($l,)* $r, $($t),*> private::Sealed for $f
        where
            $r: Encode,
            $($t: Encode,)*
        {}

        impl<$($l,)* $r, $($t),*> MethodImplementation for $f
        where
            $r: Encode,
            $($t: Encode,)*
        {
            type Callee = Class;
            type Ret = $r;
            type Args = ($($t,)*);

            fn __imp(self) -> Imp {
                unsafe { mem::transmute(self) }
            }
        }
    };
    (# $abi:literal; $($t:ident),*) => {
        method_decl_impl!(@<'a> T, R, extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(@<'a> T, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(@<> T, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(@<> T, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(@<'a> T, R, unsafe extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(@<'a> T, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*);

        method_decl_impl!(@<'a> Class, R, extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(@<> Class, R, unsafe extern $abi fn(*const Class, Sel $(, $t)*) -> R, $($t),*);
        method_decl_impl!(@<'a> Class, R, unsafe extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*);
    };
    ($($t:ident),*) => {
        method_decl_impl!(# "C"; $($t),*);
        #[cfg(feature = "unstable-c-unwind")]
        method_decl_impl!(# "C-unwind"; $($t),*);
    };
}

method_decl_impl!();
method_decl_impl!(A);
method_decl_impl!(A, B);
method_decl_impl!(A, B, C);
method_decl_impl!(A, B, C, D);
method_decl_impl!(A, B, C, D, E);
method_decl_impl!(A, B, C, D, E, F);
method_decl_impl!(A, B, C, D, E, F, G);
method_decl_impl!(A, B, C, D, E, F, G, H);
method_decl_impl!(A, B, C, D, E, F, G, H, I);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);

fn count_args(sel: Sel) -> usize {
    sel.name().chars().filter(|&c| c == ':').count()
}

fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString {
    // First two arguments are always self and the selector
    let mut types = format!("{}{}{}", ret, <*mut Object>::ENCODING, Sel::ENCODING);
    for enc in args {
        use core::fmt::Write;
        write!(&mut types, "{}", enc).unwrap();
    }
    CString::new(types).unwrap()
}

fn log2_align_of<T>() -> u8 {
    let align = mem::align_of::<T>();
    // Alignments are required to be powers of 2
    debug_assert!(align.count_ones() == 1);
    // log2 of a power of 2 is the number of trailing zeros
    align.trailing_zeros() as u8
}

/// A type for declaring a new class and adding new methods and ivars to it
/// before registering it.
#[derive(Debug)]
pub struct ClassBuilder {
    // Note: Don't ever construct a &mut Class, since it is possible to get
    // this pointer using `Class::classes`!
    cls: NonNull<Class>,
}

#[doc(hidden)]
#[deprecated = "Use `ClassBuilder` instead."]
pub type ClassDecl = ClassBuilder;

// SAFETY: The stuff that touch global state does so using locks internally.
//
// Modifying the class itself can only be done through `&mut`, so Sync is
// safe (e.g. we can't accidentally call `add_ivar` at the same time from two
// different threads).
//
// (Though actually, that would be safe since the entire runtime is locked
// when doing so...).
//
// Finally, there are no requirements that the class must be registered on the
// same thread that allocated it (so Send is safe).
unsafe impl Send for ClassBuilder {}
unsafe impl Sync for ClassBuilder {}

impl ClassBuilder {
    fn as_mut_ptr(&mut self) -> *mut ffi::objc_class {
        self.cls.as_ptr().cast()
    }

    fn with_superclass(name: &str, superclass: Option<&Class>) -> Option<Self> {
        let name = CString::new(name).unwrap();
        let super_ptr = superclass.map_or(ptr::null(), |c| c).cast();
        let cls = unsafe { ffi::objc_allocateClassPair(super_ptr, name.as_ptr(), 0) };
        NonNull::new(cls.cast()).map(|cls| Self { cls })
    }

    /// Constructs a [`ClassBuilder`] with the given name and superclass.
    ///
    /// Returns [`None`] if the class couldn't be allocated, or a class with
    /// that name already exist.
    pub fn new(name: &str, superclass: &Class) -> Option<Self> {
        Self::with_superclass(name, Some(superclass))
    }

    /// Constructs a [`ClassBuilder`] declaring a new root class with the
    /// given name.
    ///
    /// Returns [`None`] if the class couldn't be allocated.
    ///
    /// An implementation for `+initialize` must also be given; the runtime
    /// calls this method for all classes, so it must be defined on root
    /// classes.
    ///
    /// Note that implementing a root class is not a simple endeavor!
    /// For example, your class probably cannot be passed to Cocoa code unless
    /// the entire `NSObject` protocol is implemented.
    /// Functionality it expects, like implementations of `-retain` and
    /// `-release` used by ARC, will not be present otherwise.
    pub fn root<F>(name: &str, intitialize_fn: F) -> Option<Self>
    where
        F: MethodImplementation<Callee = Class, Args = (), Ret = ()>,
    {
        Self::with_superclass(name, None).map(|mut this| {
            unsafe { this.add_class_method(sel!(initialize), intitialize_fn) };
            this
        })
    }

    /// Adds a method with the given name and implementation.
    ///
    /// # Panics
    ///
    /// Panics if the method wasn't sucessfully added or if the selector and
    /// function take different numbers of arguments.
    ///
    /// # Safety
    ///
    /// The caller must ensure that the types match those that are expected
    /// when the method is invoked from Objective-C.
    pub unsafe fn add_method<T, F>(&mut self, sel: Sel, func: F)
    where
        T: Message + ?Sized,
        F: MethodImplementation<Callee = T>,
    {
        let encs = F::Args::ENCODINGS;
        let sel_args = count_args(sel);
        assert_eq!(
            sel_args,
            encs.len(),
            "Selector {:?} accepts {} arguments, but function accepts {}",
            sel,
            sel_args,
            encs.len(),
        );

        let types = method_type_encoding(&F::Ret::ENCODING, encs);
        let success = Bool::from_raw(unsafe {
            ffi::class_addMethod(
                self.as_mut_ptr(),
                sel.as_ptr(),
                Some(func.__imp()),
                types.as_ptr(),
            )
        });
        assert!(success.as_bool(), "Failed to add method {:?}", sel);
    }

    fn metaclass_mut(&mut self) -> *mut ffi::objc_class {
        unsafe { ffi::object_getClass(self.as_mut_ptr().cast()) as *mut ffi::objc_class }
    }

    /// Adds a class method with the given name and implementation.
    ///
    /// # Panics
    ///
    /// Panics if the method wasn't sucessfully added or if the selector and
    /// function take different numbers of arguments.
    ///
    /// # Safety
    ///
    /// The caller must ensure that the types match those that are expected
    /// when the method is invoked from Objective-C.
    pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
    where
        F: MethodImplementation<Callee = Class>,
    {
        let encs = F::Args::ENCODINGS;
        let sel_args = count_args(sel);
        assert_eq!(
            sel_args,
            encs.len(),
            "Selector {:?} accepts {} arguments, but function accepts {}",
            sel,
            sel_args,
            encs.len(),
        );

        let types = method_type_encoding(&F::Ret::ENCODING, encs);
        let success = Bool::from_raw(unsafe {
            ffi::class_addMethod(
                self.metaclass_mut(),
                sel.as_ptr(),
                Some(func.__imp()),
                types.as_ptr(),
            )
        });
        assert!(success.as_bool(), "Failed to add class method {:?}", sel);
    }

    /// Adds an ivar with type `T` and the provided name.
    ///
    ///
    /// # Panics
    ///
    /// If the ivar wasn't successfully added for some reason - this usually
    /// happens if there already was an ivar with that name.
    pub fn add_ivar<T: Encode>(&mut self, name: &str) {
        // SAFETY: The encoding is correct
        unsafe { self.add_ivar_inner::<T>(name, &T::ENCODING) }
    }

    unsafe fn add_ivar_inner<T>(&mut self, name: &str, encoding: &Encoding) {
        let c_name = CString::new(name).unwrap();
        let encoding = CString::new(encoding.to_string()).unwrap();
        let size = mem::size_of::<T>();
        let align = log2_align_of::<T>();
        let success = Bool::from_raw(unsafe {
            ffi::class_addIvar(
                self.as_mut_ptr(),
                c_name.as_ptr(),
                size,
                align,
                encoding.as_ptr(),
            )
        });
        assert!(success.as_bool(), "Failed to add ivar {}", name);
    }

    /// Adds an instance variable from an [`IvarType`].
    ///
    ///
    /// # Panics
    ///
    /// Same as [`ClassBuilder::add_ivar`].
    pub fn add_static_ivar<T: IvarType>(&mut self) {
        // SAFETY: The encoding is correct
        unsafe {
            self.add_ivar_inner::<<T::Type as InnerIvarType>::__Inner>(
                T::NAME,
                &T::Type::__ENCODING,
            )
        }
    }

    /// Adds the given protocol to self.
    ///
    /// # Panics
    ///
    /// If the protocol wasn't successfully added.
    pub fn add_protocol(&mut self, proto: &Protocol) {
        let success = unsafe { ffi::class_addProtocol(self.as_mut_ptr(), proto.as_ptr()) };
        let success = Bool::from_raw(success).as_bool();
        assert!(success, "Failed to add protocol {:?}", proto);
    }

    // fn add_property(&self, name: &str, attributes: &[ffi::objc_property_attribute_t]);

    /// Registers the [`ClassBuilder`], consuming it, and returns a reference
    /// to the newly registered [`Class`].
    pub fn register(self) -> &'static Class {
        // Forget self, otherwise the class will be disposed in drop
        let mut this = ManuallyDrop::new(self);
        unsafe { ffi::objc_registerClassPair(this.as_mut_ptr()) };
        unsafe { this.cls.as_ref() }
    }
}

impl Drop for ClassBuilder {
    fn drop(&mut self) {
        unsafe { ffi::objc_disposeClassPair(self.as_mut_ptr()) }
    }
}

/// A type for declaring a new protocol and adding new methods to it
/// before registering it.
#[derive(Debug)]
pub struct ProtocolBuilder {
    proto: NonNull<Protocol>,
}

#[doc(hidden)]
#[deprecated = "Use `ProtocolBuilder` instead."]
pub type ProtocolDecl = ProtocolBuilder;

// SAFETY: Similar to ClassBuilder
unsafe impl Send for ProtocolBuilder {}
unsafe impl Sync for ProtocolBuilder {}

impl ProtocolBuilder {
    fn as_mut_ptr(&mut self) -> *mut ffi::objc_protocol {
        self.proto.as_ptr().cast()
    }

    /// Constructs a [`ProtocolBuilder`] with the given name.
    ///
    /// Returns [`None`] if the protocol couldn't be allocated.
    pub fn new(name: &str) -> Option<Self> {
        let c_name = CString::new(name).unwrap();
        let proto = unsafe { ffi::objc_allocateProtocol(c_name.as_ptr()) };
        NonNull::new(proto.cast()).map(|proto| Self { proto })
    }

    fn add_method_description_common<Args, Ret>(
        &mut self,
        sel: Sel,
        is_required: bool,
        is_instance_method: bool,
    ) where
        Args: EncodeArguments,
        Ret: Encode,
    {
        let encs = Args::ENCODINGS;
        let sel_args = count_args(sel);
        assert_eq!(
            sel_args,
            encs.len(),
            "Selector {:?} accepts {} arguments, but function accepts {}",
            sel,
            sel_args,
            encs.len(),
        );
        let types = method_type_encoding(&Ret::ENCODING, encs);
        unsafe {
            ffi::protocol_addMethodDescription(
                self.as_mut_ptr(),
                sel.as_ptr(),
                types.as_ptr(),
                Bool::new(is_required).as_raw(),
                Bool::new(is_instance_method).as_raw(),
            );
        }
    }

    /// Adds an instance method declaration with a given description.
    pub fn add_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
    where
        Args: EncodeArguments,
        Ret: Encode,
    {
        self.add_method_description_common::<Args, Ret>(sel, is_required, true)
    }

    /// Adds a class method declaration with a given description.
    pub fn add_class_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
    where
        Args: EncodeArguments,
        Ret: Encode,
    {
        self.add_method_description_common::<Args, Ret>(sel, is_required, false)
    }

    /// Adds a requirement on another protocol.
    pub fn add_protocol(&mut self, proto: &Protocol) {
        unsafe {
            ffi::protocol_addProtocol(self.as_mut_ptr(), proto.as_ptr());
        }
    }

    /// Registers the [`ProtocolBuilder`], consuming it and returning a reference
    /// to the newly registered [`Protocol`].
    pub fn register(mut self) -> &'static Protocol {
        unsafe {
            ffi::objc_registerProtocol(self.as_mut_ptr());
            self.proto.as_ref()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::msg_send;
    use crate::test_utils;

    #[test]
    fn test_classbuilder_duplicate() {
        let cls = test_utils::custom_class();
        let builder = ClassBuilder::new("TestClassBuilderDuplicate", cls).unwrap();
        let _ = builder.register();

        assert!(ClassBuilder::new("TestClassBuilderDuplicate", cls).is_none());
    }

    #[test]
    #[should_panic = "Failed to add ivar xyz"]
    fn duplicate_ivar() {
        let cls = test_utils::custom_class();
        let builder = ClassBuilder::new("TestClassBuilderDuplicateIvar", cls).unwrap();
        // ManuallyDrop to work around GNUStep issue
        let mut builder = ManuallyDrop::new(builder);

        builder.add_ivar::<i32>("xyz");
        // Should panic:
        builder.add_ivar::<i32>("xyz");
    }

    #[test]
    #[should_panic = "Failed to add method xyz"]
    fn duplicate_method() {
        let cls = test_utils::custom_class();
        let builder = ClassBuilder::new("TestClassBuilderDuplicateMethod", cls).unwrap();
        let mut builder = ManuallyDrop::new(builder);

        extern "C" fn xyz(_this: &Object, _cmd: Sel) {}

        unsafe {
            builder.add_method(sel!(xyz), xyz as extern "C" fn(_, _));
            // Should panic:
            builder.add_method(sel!(xyz), xyz as extern "C" fn(_, _));
        }
    }

    #[test]
    #[should_panic = "Failed to add protocol NSObject"]
    fn duplicate_protocol() {
        let cls = test_utils::custom_class();
        let builder = ClassBuilder::new("TestClassBuilderDuplicateProtocol", cls).unwrap();
        let mut builder = ManuallyDrop::new(builder);

        let protocol = Protocol::get("NSObject").unwrap();

        builder.add_protocol(protocol);
        // Should panic:
        builder.add_protocol(protocol);
    }

    #[test]
    #[cfg_attr(
        feature = "gnustep-1-7",
        ignore = "Dropping ClassBuilder has weird threading side effects on GNUStep"
    )]
    fn test_classbuilder_drop() {
        let cls = test_utils::custom_class();
        let builder = ClassBuilder::new("TestClassBuilderDrop", cls).unwrap();
        drop(builder);
        // After we dropped the class, we can create a new one with the same name:
        let _builder = ClassBuilder::new("TestClassBuilderDrop", cls).unwrap();
    }

    #[test]
    fn test_custom_class() {
        // Registering the custom class is in test_utils
        let mut obj = test_utils::custom_object();
        let _: () = unsafe { msg_send![&mut obj, setFoo: 13u32] };
        let result: u32 = unsafe { msg_send![&obj, foo] };
        assert_eq!(result, 13);
    }

    #[test]
    #[cfg(feature = "malloc")]
    fn test_in_all_classes() {
        fn is_present(cls: *const Class) -> bool {
            // Check whether the class is present in Class::classes()
            Class::classes()
                .into_iter()
                .find(|&item| ptr::eq(cls, *item))
                .is_some()
        }

        let superclass = test_utils::custom_class();
        let builder = ClassBuilder::new("TestFetchWhileCreatingClass", superclass).unwrap();

        if cfg!(all(feature = "apple", target_arch = "x86_64")) {
            // It is IMO a bug that it is present here!
            assert!(is_present(builder.cls.as_ptr()));
        } else {
            assert!(!is_present(builder.cls.as_ptr()));
        }

        let cls = builder.register();
        assert!(is_present(cls));
    }

    #[test]
    fn test_class_method() {
        let cls = test_utils::custom_class();
        let result: u32 = unsafe { msg_send![cls, classFoo] };
        assert_eq!(result, 7);
    }
}