Skip to main content

cidre/
objc.rs

1#[cfg(all(
2    target_arch = "aarch64",
3    not(target_pointer_width = "32"),
4    not(feature = "classic-objc-retain-release")
5))]
6use std::arch::asm;
7use std::{borrow::Cow, ffi::c_void, marker::PhantomData, ptr::NonNull};
8
9use crate::{arc, cf::Type, objc};
10
11#[derive(Debug)]
12#[repr(transparent)]
13pub struct Class<T: Obj>(Type, PhantomData<T>);
14
15#[derive(Debug)]
16#[repr(transparent)]
17pub struct Protocol(Type);
18
19impl<T: Obj> Class<T> {
20    pub unsafe fn method_impl(&self, name: &Sel) -> *const c_void {
21        unsafe { class_getMethodImplementation(std::mem::transmute(self), name) }
22    }
23
24    pub unsafe fn add_protocol(&self, protocol: &Protocol) -> bool {
25        unsafe { class_addProtocol(std::mem::transmute(self), protocol) }
26    }
27}
28
29#[derive(Debug)]
30#[repr(transparent)]
31pub struct ClassInstExtra<T: Obj, I: Sized>(Class<T>, PhantomData<I>);
32
33impl<T: Obj, I: Sized> std::ops::Deref for ClassInstExtra<T, I> {
34    type Target = Class<T>;
35
36    fn deref(&self) -> &Self::Target {
37        &self.0
38    }
39}
40
41// class_getInstanceSize([NSObject class]);
42pub const NS_OBJECT_SIZE: usize = std::mem::size_of::<usize>();
43
44#[macro_export]
45macro_rules! init_with_default {
46    ($NewType:ty, $InnerType:ty) => {{
47        trait A {
48            fn init_fn(&self) -> Option<extern "C" fn()>;
49        }
50
51        struct B<T: ?Sized>(core::marker::PhantomData<T>);
52
53        impl<T: ?Sized> core::ops::Deref for B<T> {
54            type Target = ();
55            fn deref(&self) -> &Self::Target {
56                &()
57            }
58        }
59
60        impl<T: ?Sized> A for B<T>
61        where
62            T: Default,
63        {
64            fn init_fn(&self) -> Option<extern "C" fn()> {
65                extern "C" fn impl_init<T: Default>(
66                    s: *mut $NewType,
67                    _sel: Option<$crate::objc::Sel>,
68                ) -> $crate::arc::R<$NewType> {
69                    unsafe {
70                        let ptr: *mut u8 = s.cast();
71                        let d_ptr: *mut std::mem::ManuallyDrop<T> =
72                            ptr.add($crate::objc::NS_OBJECT_SIZE) as _;
73                        *d_ptr = std::mem::ManuallyDrop::new(T::default());
74
75                        std::mem::transmute(ptr)
76                    }
77                }
78
79                let ptr = unsafe { std::mem::transmute(impl_init::<T> as *const u8) };
80                Some(ptr)
81            }
82        }
83
84        impl A for () {
85            fn init_fn(&self) -> Option<extern "C" fn()> {
86                None
87            }
88        }
89
90        B::<$InnerType>(core::marker::PhantomData).init_fn()
91    }};
92}
93
94impl<T: Obj, I: Sized> ClassInstExtra<T, I> {
95    #[inline]
96    pub fn alloc_init(&self, var: I) -> arc::R<T> {
97        unsafe {
98            let inst = class_createInstance(std::mem::transmute(self), std::mem::size_of::<I>());
99
100            // we may skip init?
101            // let inst = inst.init();
102
103            let ptr: *mut u8 = std::mem::transmute(inst);
104            let d_ptr: *mut std::mem::ManuallyDrop<I> = ptr.add(NS_OBJECT_SIZE) as _;
105            *d_ptr = std::mem::ManuallyDrop::new(var);
106
107            std::mem::transmute(ptr)
108        }
109    }
110}
111
112impl<T: Obj, I: Sized + Default> ClassInstExtra<T, I> {
113    pub fn new(&self) -> arc::R<T> {
114        self.alloc_init(Default::default())
115    }
116}
117
118impl<T: Obj> Class<T> {
119    #[inline]
120    pub fn as_type_ref(&self) -> &Type {
121        &self.0
122    }
123
124    #[must_use]
125    #[objc::msg_send(alloc)]
126    pub fn alloc(&self) -> arc::A<T>;
127
128    // in general alloc_init is faster
129    #[objc::msg_send(new)]
130    pub unsafe fn new(&self) -> arc::Retained<T>;
131}
132
133impl<T: Obj> Obj for Class<T> {}
134
135impl<T: Obj> arc::Release for T {
136    #[inline]
137    unsafe fn release(&mut self) {
138        unsafe { <T as Obj>::release(self) }
139    }
140}
141
142impl<T: Obj> arc::Retain for T {
143    fn retained(&self) -> arc::R<Self> {
144        unsafe { Self::retain(self) }
145    }
146}
147
148pub trait Obj: Sized + arc::Retain {
149    #[inline]
150    unsafe fn retain(id: &Self) -> arc::R<Self> {
151        unsafe {
152            #[cfg(all(target_arch = "aarch64", not(feature = "classic-objc-retain-release")))]
153            {
154                let result: *mut Self;
155                core::arch::asm!(
156                    "bl _objc_retain_{obj:x}",
157                    obj = in(reg) id,
158                    lateout("x0") result,
159                    out("x16") _,
160                    out("x17") _,
161                    out("x30") _,
162                    clobber_abi("C"),
163                );
164                std::mem::transmute(result)
165            }
166
167            #[cfg(any(target_arch = "x86_64", feature = "classic-objc-retain-release"))]
168            {
169                std::mem::transmute(objc_retain(std::mem::transmute(id)))
170            }
171        }
172    }
173
174    #[inline]
175    unsafe fn release(id: &mut Self) {
176        unsafe {
177            #[cfg(all(
178                target_arch = "aarch64",
179                target_pointer_width = "64",
180                not(feature = "classic-objc-retain-release")
181            ))]
182            {
183                asm!(
184                    "bl _objc_release_{x}",
185                    x = in(reg) id,
186                    out("x16") _,
187                    out("x17") _,
188                    out("x30") _,
189                    clobber_abi("C")
190                    // system also works
191                    // clobber_abi("system")
192                );
193            }
194
195            #[cfg(any(
196                target_arch = "x86_64",
197                target_pointer_width = "32",
198                feature = "classic-objc-retain-release"
199            ))]
200            {
201                objc_release(std::mem::transmute(id));
202            }
203        }
204    }
205
206    #[objc::msg_send(description)]
207    fn desc(&self) -> arc::R<crate::ns::String>;
208
209    #[objc::msg_send(debugDescription)]
210    fn debug_desc(&self) -> arc::R<crate::ns::String>;
211
212    #[objc::msg_send(respondsToSelector:)]
213    fn responds_to_sel(&self, sel: &Sel) -> bool;
214
215    #[objc::msg_send(class)]
216    fn class(&self) -> &crate::objc::Class<Self>;
217
218    #[objc::msg_send(isKindOfClass:)]
219    fn is_kind_of_class<T: Obj>(&self, cls: &crate::objc::Class<T>) -> bool;
220
221    #[inline]
222    fn try_cast<T: Obj>(&self, cls: &crate::objc::Class<T>) -> Option<&T> {
223        if self.is_kind_of_class(cls) {
224            Some(unsafe { std::mem::transmute(self) })
225        } else {
226            None
227        }
228    }
229
230    #[inline]
231    fn try_cast_mut<T: Obj>(&mut self, cls: &crate::objc::Class<T>) -> Option<&mut T> {
232        if self.is_kind_of_class(cls) {
233            Some(unsafe { std::mem::transmute(self) })
234        } else {
235            None
236        }
237    }
238
239    #[objc::msg_send(isMemberOfClass:)]
240    fn is_member_of_class<T: Obj>(&self, cls: &crate::objc::Class<T>) -> bool;
241
242    #[cfg(not(target_os = "watchos"))]
243    #[inline]
244    fn is_tagged_ptr(&self) -> bool {
245        ((self as *const Self as usize) >> 63) == 1
246    }
247
248    #[inline]
249    fn as_id_ref(&self) -> &Id {
250        unsafe { std::mem::transmute(self) }
251    }
252}
253
254/// Use it as NSObject or id
255#[repr(transparent)]
256pub struct Id(Type);
257
258unsafe impl Send for Id {}
259
260impl Id {
261    #[inline]
262    pub unsafe fn autorelease<'ar>(id: &mut Id) -> &'ar mut Id {
263        unsafe { objc_autorelease(id) }
264    }
265
266    // #[inline]
267    // pub unsafe fn retain_autoreleased_return<'ar>(id: Option<&Id>) -> Option<arc::R<Id>> {
268    //     objc_retainAutoreleasedReturnValue(id)
269    // }
270
271    #[inline]
272    pub fn as_type_ref(&self) -> &Type {
273        &self.0
274    }
275
276    #[inline]
277    pub fn as_id_ref(&self) -> &Self {
278        self
279    }
280
281    #[objc::msg_send(isEqual:)]
282    pub fn is_equal(&self, other: &Self) -> bool;
283
284    #[objc::msg_send(hash)]
285    pub fn hash(&self) -> ns::UInteger;
286
287    pub fn as_ptr(&self) -> *const Self {
288        self as *const Self
289    }
290}
291
292impl Obj for Id {}
293
294impl std::fmt::Debug for Id {
295    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296        let desc = self.debug_desc();
297        f.write_str(&Cow::from(desc.as_cf()))
298        // f.debug_tuple("NS").field(&Cow::from(desc.as_cf())).finish()
299    }
300}
301
302#[derive(Debug)]
303#[repr(transparent)]
304pub struct Sel(NonNull<c_void>);
305
306pub mod autorelease_pool;
307pub mod ns;
308pub use autorelease_pool::AutoreleasePoolPage;
309
310pub fn ar_pool<R, F>(f: F) -> R
311where
312    F: FnOnce() -> R,
313    R: Clone, // Autoreleased doesn't implement Clone
314{
315    let _page = AutoreleasePoolPage::push();
316    f()
317}
318
319pub unsafe fn sel_reg_name(str: *const i8) -> &'static Sel {
320    unsafe { std::mem::transmute(sel_registerName(str)) }
321}
322
323#[doc(alias = "objc_super")]
324#[repr(C)]
325#[derive(Debug, Copy, Clone)]
326pub struct Super {
327    pub receiver: *mut Id,
328    pub super_class: *const Class<Id>,
329}
330
331#[link(name = "objc", kind = "dylib")]
332unsafe extern "C-unwind" {
333    #[cfg(any(target_arch = "x86_64", feature = "classic-objc-retain-release"))]
334    pub fn objc_retain<'a>(obj: &Id) -> &'a Id;
335    #[cfg(any(
336        target_arch = "x86_64",
337        target_pointer_width = "32",
338        feature = "classic-objc-retain-release"
339    ))]
340    fn objc_release(obj: &mut Id);
341
342    // fn objc_msgSend();
343
344    pub fn class_createInstance(cls: &Class<Id>, extra_bytes: usize) -> arc::A<Id>;
345    fn class_getMethodImplementation(cls: &Class<Id>, name: &Sel) -> *const c_void;
346    fn class_addProtocol(cls: &Class<Id>, protocol: &Protocol) -> bool;
347    fn objc_autorelease<'ar>(id: &mut Id) -> &'ar mut Id;
348
349    pub fn objc_retainAutoreleasedReturnValue<'ar>(obj: Option<&Id>) -> Option<arc::R<Id>>;
350    pub fn objc_retainAutoreleaseReturnValue<'ar>(obj: Option<&Id>) -> Option<&'ar Id>;
351    pub fn objc_claimAutoreleasedReturnValue() -> Option<arc::R<Id>>;
352    pub fn objc_autoreleaseReturnValue<'ar>(obj: Option<&Id>) -> Option<&'ar Id>;
353
354    pub fn objc_copyWeak<'ar>(dest: *mut *mut Id, src: *mut *mut Id) -> Option<&'ar Id>;
355    pub fn objc_destroyWeak(location: *mut *mut Id);
356    pub fn objc_storeWeak<'ar>(location: *mut *mut Id, value: Option<&Id>) -> Option<&'ar Id>;
357    pub fn objc_loadWeakRetained(location: *mut *mut Id) -> Option<arc::R<Id>>;
358
359    pub fn object_getIndexedIvars(obj: *const c_void) -> *mut c_void;
360    pub fn sel_registerName(str: *const i8) -> *const std::ffi::c_void;
361    pub fn class_addMethod(
362        cls: &Class<Id>,
363        name: &Sel,
364        imp: extern "C" fn(),
365        types: *const u8,
366    ) -> bool;
367
368    pub fn class_replaceMethod(
369        cls: &Class<Id>,
370        name: &Sel,
371        imp: extern "C" fn(),
372        types: *const u8,
373    ) -> Option<extern "C" fn()>;
374
375    pub fn objc_allocateClassPair(
376        super_cls: &Class<Id>,
377        name: *const u8,
378        extra_bytes: usize,
379    ) -> Option<&'static Class<Id>>;
380    pub fn object_getClass(obj: Option<&Id>) -> Option<&Class<Id>>;
381    pub fn objc_registerClassPair(cls: &Class<Id>);
382    pub fn objc_getClass(name: *const u8) -> Option<&'static Class<Id>>;
383    pub fn objc_getProtocol(name: *const i8) -> Option<&'static Protocol>;
384    pub fn objc_msgSendSuper(s: &Super, sel: &Sel);
385    pub static NS_OBJECT: &'static crate::objc::Class<Id>;
386    fn objc_exception_throw(exception: &Id) -> !;
387}
388
389/// Same as `define_cls!` but with open `init`
390#[macro_export]
391macro_rules! define_cls_init {
392    ($NewType:ident, $CLS:ident) => {
393        impl $crate::arc::A<$NewType> {
394            #[$crate::objc::msg_send(init)]
395            pub fn init(self) -> arc::Retained<$NewType>;
396        }
397
398        impl $NewType {
399            $crate::define_cls!($CLS);
400
401            /// shortcut to `Self::alloc().init()`
402            #[inline]
403            pub fn new() -> $crate::arc::R<$NewType> {
404                Self::alloc().init()
405            }
406        }
407    };
408}
409
410#[macro_export]
411macro_rules! define_weak_cls_init {
412    ($NewType:ident, $CLS:ident) => {
413        impl $crate::arc::A<$NewType> {
414            #[$crate::objc::msg_send(init)]
415            pub fn init(self) -> arc::Retained<$NewType>;
416        }
417
418        impl $NewType {
419            $crate::define_weak_cls!($CLS);
420
421            /// shortcut to `Self::alloc().init()`
422            #[inline]
423            pub fn new() -> Option<$crate::arc::R<$NewType>> {
424                Some(Self::alloc()?.init())
425            }
426        }
427    };
428}
429
430/// Defines class
431///
432/// Use when:
433/// + (instancetype)new NS_UNAVAILABLE;
434/// - (instancetype)init NS_UNAVAILABLE;
435#[macro_export]
436macro_rules! define_cls {
437    ($CLS:ident) => {
438        #[inline]
439        pub fn cls() -> &'static $crate::objc::Class<Self> {
440            unsafe { std::mem::transmute($CLS) }
441        }
442
443        #[inline]
444        pub fn cls_ptr() -> *const std::ffi::c_void {
445            unsafe { std::mem::transmute($CLS) }
446        }
447
448        #[inline]
449        pub fn alloc() -> $crate::arc::A<Self> {
450            Self::cls().alloc()
451        }
452    };
453}
454
455#[macro_export]
456macro_rules! define_weak_cls {
457    ($CLS:ident) => {
458        #[inline]
459        pub fn cls() -> Option<&'static $crate::objc::Class<Self>> {
460            unsafe { std::mem::transmute($CLS) }
461        }
462
463        #[inline]
464        pub fn cls_ptr() -> *const std::ffi::c_void {
465            unsafe { std::mem::transmute($CLS) }
466        }
467
468        #[inline]
469        pub fn alloc() -> Option<$crate::arc::A<Self>> {
470            Some(Self::cls()?.alloc())
471        }
472    };
473}
474
475#[macro_export]
476macro_rules! define_obj_type {
477    (
478        $(#[$outer:meta])*
479        $vis:vis
480        $NewType:ident $(+ $TraitImpl:path)*, $InnerType:path, $CLS:ident) => {
481        $crate::define_obj_type!(
482            $(#[$outer])*
483            $vis
484            $NewType(objc::Id)
485        );
486
487        impl $NewType {
488            #[allow(dead_code)]
489            #[inline]
490            pub fn inner(&self) -> &$InnerType {
491                unsafe {
492                    let ptr =  self as *const Self as *const u8;
493                    let ptr = ptr.add($crate::objc::NS_OBJECT_SIZE);
494                    &*(ptr as *const $InnerType)
495                }
496            }
497
498            #[allow(dead_code)]
499            #[inline]
500            pub fn inner_mut(&mut self) -> &mut $InnerType {
501                unsafe {
502                    let ptr: *mut u8 = self as *mut Self as *mut u8;
503                    let ptr = ptr.add($crate::objc::NS_OBJECT_SIZE);
504                    &mut *(ptr as *mut $InnerType)
505                }
506            }
507
508            #[allow(dead_code)]
509            pub fn register_cls() -> &'static $crate::objc::ClassInstExtra<Self, $InnerType> {
510                let name = concat!(stringify!($CLS), "\0");
511                let cls = unsafe { $crate::objc::objc_allocateClassPair($crate::objc::NS_OBJECT, name.as_ptr(), 0) };
512                let cls = cls.unwrap();
513                $(<Self as $TraitImpl>::cls_add_methods(cls);)*
514                $(<Self as $TraitImpl>::cls_add_protocol(cls);)*
515
516                if let Some(init_fn_ptr) = $crate::init_with_default!($NewType, $InnerType) {
517                    unsafe {
518                        let sel = $crate::objc::sel_reg_name(c"init".as_ptr() as _);
519                        let imp: extern "C" fn() = init_fn_ptr;
520                        $crate::objc::class_addMethod(cls, sel, imp, std::ptr::null());
521
522                        let sel = $crate::objc::sel_reg_name(c"alloc".as_ptr() as _);
523                        let meta_cls = $crate::objc::object_getClass(Some(std::mem::transmute(cls))).unwrap();
524
525                        extern "C" fn alloc_impl(cls: &$crate::objc::Class<$crate::ns::Id>) -> $crate::arc::A<$NewType> {
526                            unsafe {
527                                let inst = $crate::objc::class_createInstance(cls, std::mem::size_of::<$InnerType>());
528                                std::mem::transmute(inst)
529                            }
530
531                        }
532
533
534                        $crate::objc::class_addMethod(meta_cls, sel, std::mem::transmute(alloc_impl as *const u8), std::ptr::null());
535
536                    }
537                }
538
539                if std::mem::needs_drop::<$InnerType>() {
540                    extern "C" fn impl_dealloc(s: &mut $NewType, sel: &$crate::objc::Sel) {
541                        let ptr = s.inner_mut() as *mut _;
542                        unsafe {
543                            std::ptr::drop_in_place(ptr);
544                            let sup = $crate::objc::Super {
545                                receiver: std::mem::transmute(s),
546                                super_class: $crate::objc::NS_OBJECT
547                            };
548                            $crate::objc::objc_msgSendSuper(&sup, sel);
549                        }
550                    }
551                    unsafe {
552                        let sel = $crate::objc::sel_reg_name(c"dealloc".as_ptr() as _);
553                        let imp: extern "C" fn() = std::mem::transmute(impl_dealloc as *const u8);
554                        $crate::objc::class_addMethod(cls, sel, imp, std::ptr::null());
555                    }
556                }
557                unsafe { $crate::objc::objc_registerClassPair(cls) };
558                unsafe { std::mem::transmute(cls) }
559            }
560
561            #[allow(dead_code)]
562            pub fn cls() -> &'static $crate::objc::ClassInstExtra<Self, $InnerType> {
563                let name = concat!(stringify!($CLS), "\0");
564                let cls = unsafe { $crate::objc::objc_getClass(name.as_ptr()) };
565                match cls {
566                    Some(c) => unsafe { std::mem::transmute(c) }
567                    None => Self::register_cls()
568                }
569            }
570
571            #[allow(dead_code)]
572            #[inline]
573            pub fn cls_ptr() -> *const std::ffi::c_void {
574                Self::cls() as *const $crate::objc::ClassInstExtra<Self, $InnerType> as *const std::ffi::c_void
575            }
576
577            #[allow(dead_code)]
578            pub fn with(inner: $InnerType) -> $crate::arc::R<Self> {
579                Self::cls().alloc_init(inner)
580            }
581        }
582    };
583    (
584        $(#[$outer:meta])*
585        $vis:vis
586        $NewType:ident $(+ $TraitImpl:path)*, (), $CLS:ident) => {
587        $crate::define_obj_type!(
588            $(#[$outer])*
589            $vis
590            $NewType(objc::Id)
591        );
592
593        impl $NewType {
594
595            #[allow(dead_code)]
596            pub fn register_cls() -> &'static $crate::objc::ClassInstExtra<Self, ()> {
597                let name = concat!(stringify!($CLS), "\0");
598                let cls = unsafe { $crate::objc::objc_allocateClassPair($crate::objc::NS_OBJECT, name.as_ptr(), 0) };
599                let cls = cls.unwrap();
600                $(<Self as $TraitImpl>::cls_add_methods(cls);)*
601                $(<Self as $TraitImpl>::cls_add_protocol(cls);)*
602
603                unsafe { $crate::objc::objc_registerClassPair(cls) };
604                unsafe { std::mem::transmute(cls) }
605            }
606
607            #[allow(dead_code)]
608            pub fn cls() -> &'static $crate::objc::ClassInstExtra<Self, ()> {
609                let name = concat!(stringify!($CLS), "\0");
610                let cls = unsafe { $crate::objc::objc_getClass(name.as_ptr()) };
611                match cls {
612                    Some(c) => unsafe { std::mem::transmute(c) }
613                    None => Self::register_cls()
614                }
615            }
616
617            #[allow(dead_code)]
618            pub fn new() -> $crate::arc::R<Self> {
619                unsafe { Self::cls().new() }
620            }
621        }
622    };
623    (
624        $(#[$outer:meta])*
625        $vis:vis
626        $NewType:ident($BaseType:path)
627    ) => {
628        $(#[$outer])*
629        #[derive(Debug, PartialEq)]
630        #[repr(transparent)]
631        $vis struct $NewType($BaseType);
632
633        impl $crate::objc::Obj for $NewType {}
634
635        impl std::ops::Deref for $NewType {
636            type Target = $BaseType;
637
638            #[inline]
639            fn deref(&self) -> &Self::Target {
640                &self.0
641            }
642        }
643
644        impl std::ops::DerefMut for $NewType {
645            #[inline]
646            fn deref_mut(&mut self) -> &mut Self::Target {
647                &mut self.0
648            }
649        }
650
651        impl $NewType {
652            #[allow(dead_code)]
653            #[inline]
654            pub fn retained(&self) -> $crate::arc::R<Self> {
655                unsafe { $crate::objc::Obj::retain(self) }
656            }
657        }
658
659        impl PartialEq<$crate::arc::R<$NewType>> for $NewType {
660            fn eq(&self, other: &$crate::arc::R<$NewType>) -> bool {
661                self.0.is_equal(other)
662            }
663        }
664
665        impl AsRef<$crate::ns::Id> for $NewType {
666            fn as_ref(&self) -> &$crate::ns::Id {
667                self
668            }
669        }
670
671        // impl PartialEq for $NewType {
672        //     fn eq(&self, other: &$NewType) -> bool {
673        //         self.is_equal(other)
674        //     }
675        // }
676
677    };
678    (
679        $(#[$outer:meta])*
680        $vis:vis
681        $NewType:ident($BaseType:path), $CLS:ident
682        $(, #[$api_available:meta])?
683    ) => {
684        $crate::define_obj_type!(
685            $(#[$outer])*
686            $vis$
687            NewType($BaseType)
688        );
689        $(#[$api_available])?
690        $crate::define_cls_init!($NewType, $CLS);
691    };
692}
693
694impl PartialEq for Id {
695    #[inline]
696    fn eq(&self, other: &Self) -> bool {
697        self.is_equal(other)
698    }
699}
700
701impl Eq for Id {}
702impl std::hash::Hash for Id {
703    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
704        self.hash().hash(state);
705    }
706}
707
708impl Eq for arc::R<Id> {}
709impl std::hash::Hash for arc::R<Id> {
710    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
711        self.0.hash().hash(state);
712    }
713}
714
715/// Can throw any object. You may need ns::Exception::raise.
716/// [read more](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Tasks/HandlingExceptions.html)
717#[inline]
718pub fn throw(obj: &Id) -> ! {
719    // latest sonoma (4.2 Beta (23C5030f) crash on tagged ptr exception for unknown reason :(
720    // TODO: investigate on release
721    #[cfg(not(target_os = "watchos"))]
722    debug_assert!(!obj.is_tagged_ptr());
723    unsafe { objc_exception_throw(obj) }
724}
725
726#[link(name = "ns", kind = "static")]
727unsafe extern "C-unwind" {
728    fn cidre_try_catch<'ar>(
729        during: extern "C" fn(ctx: *mut c_void),
730        ctx: *mut c_void,
731    ) -> Option<&'ar Id>;
732}
733
734pub fn try_catch<'ar, F, R>(f: F) -> Result<R, &'ar Id>
735where
736    F: FnOnce() -> R,
737{
738    let mut result = None;
739    let mut wrapper = Some(|| {
740        result = Some(f());
741    });
742
743    let f = type_helper(&wrapper);
744    let ctx = &mut wrapper as *mut _ as *mut c_void;
745
746    unsafe {
747        match cidre_try_catch(std::mem::transmute(f), ctx) {
748            None => Ok(result.unwrap_unchecked()),
749            Some(e) => Err(e),
750        }
751    }
752}
753
754#[inline]
755fn type_helper<F>(_t: &Option<F>) -> extern "C-unwind" fn(t: &mut Option<F>)
756where
757    F: FnOnce(),
758{
759    extern "C-unwind" fn during<F>(f: &mut Option<F>)
760    where
761        F: FnOnce(),
762    {
763        unsafe { f.take().unwrap_unchecked()() };
764    }
765    during
766}
767
768#[cfg(target_arch = "aarch64")]
769#[cfg(test)]
770mod tests {
771
772    use super::ar_pool;
773    use crate::{arc, cf, dispatch, return_ar};
774    use std;
775
776    fn autorelease_example_ar() -> arc::Rar<dispatch::Queue> {
777        let q = dispatch::Queue::new();
778        return_ar!(q)
779    }
780
781    #[test]
782    fn autorelease() {
783        let ptr = ar_pool(|| {
784            let q = autorelease_example_ar().retained();
785            assert_eq!(2, q.as_type_ref().retain_count());
786            unsafe { q.as_type_ref().as_type_ptr() }
787        });
788
789        let _ptr: &cf::Type = unsafe { std::mem::transmute(ptr) };
790    }
791}
792pub use cidre_macros::add_methods;
793pub use cidre_macros::api_available as available;
794pub use cidre_macros::optional;
795pub use cidre_macros::protocol;
796
797/// Docs
798#[cfg(target_arch = "aarch64")]
799pub use cidre_macros::msg_send;
800#[cfg(target_arch = "aarch64")]
801pub use cidre_macros::msg_send_debug;
802#[cfg(target_arch = "x86_64")]
803pub use cidre_macros::msg_send_x86_64 as msg_send;
804
805#[cfg(test)]
806mod tests2 {
807
808    use std::collections::HashMap;
809
810    use crate::{
811        arc::{self, Retain},
812        ns,
813        objc::{self, Obj},
814        return_rar,
815    };
816
817    #[objc::protocol(Foo)]
818    trait Foo: objc::Obj {
819        #[objc::msg_send(count)]
820        fn count(&self) -> usize;
821
822        #[objc::msg_send(newObj)]
823        fn new_obj(&self) -> arc::R<ns::String>;
824
825        #[objc::msg_send(prop)]
826        fn prop(&self) -> arc::R<ns::String>;
827
828        #[objc::optional]
829        #[objc::msg_send(count2)]
830        fn count2(&self) -> usize;
831
832        fn direct_fn(&self);
833    }
834
835    static mut DROP_CALLED: bool = false;
836
837    pub struct D;
838
839    impl Drop for D {
840        fn drop(&mut self) {
841            unsafe {
842                DROP_CALLED = true;
843            }
844        }
845    }
846
847    define_obj_type!(Bla + FooImpl, D, BLA_USIZE);
848
849    impl Foo for Bla {
850        fn direct_fn(&self) {}
851    }
852
853    #[objc::add_methods]
854    impl FooImpl for Bla {
855        extern "C" fn impl_count(&self, _cmd: Option<&objc::Sel>) -> usize {
856            0
857        }
858
859        extern "C" fn impl_new_obj(&self, _cmd: Option<&objc::Sel>) -> arc::R<ns::String> {
860            ns::String::new()
861        }
862
863        extern "C" fn impl_prop_ar(&self, _cmd: Option<&objc::Sel>) -> arc::Rar<ns::String> {
864            let s = ns::str!(c"test");
865            return_rar!(s)
866        }
867    }
868
869    #[test]
870    fn basics() {
871        unsafe {
872            DROP_CALLED = false;
873        }
874        {
875            let d = Bla::with(D);
876            assert_eq!(d.prop().to_string(), "test");
877            let _r = d.retained();
878            let desc = d.desc();
879            assert!(desc.to_string().starts_with("<BLA_USIZE: "));
880        }
881        assert!(unsafe { DROP_CALLED });
882    }
883
884    #[test]
885    fn hash() {
886        fn foo() -> HashMap<arc::R<ns::Id>, arc::R<ns::String>> {
887            let a = ns::String::new();
888            let b = ns::String::new();
889            let mut map = HashMap::new();
890            let _v = map.insert(a.as_id_ref().retained(), b);
891            map
892        }
893
894        foo();
895    }
896}