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
#[cfg(all(feature = "NSObjCRuntime", feature = "NSString"))]
use core::fmt;
use core::hint::unreachable_unchecked;
use core::panic::{RefUnwindSafe, UnwindSafe};
use objc2::exception::Exception;
use objc2::rc::Id;
use objc2::runtime::{NSObject, NSObjectProtocol};
use objc2::{extern_methods, sel};
use crate::Foundation::NSException;
// SAFETY: Exception objects are immutable data containers, and documented as
// thread safe.
unsafe impl Sync for NSException {}
unsafe impl Send for NSException {}
impl UnwindSafe for NSException {}
impl RefUnwindSafe for NSException {}
extern_methods!(
unsafe impl NSException {
#[method(raise)]
unsafe fn raise_raw(&self);
}
);
impl NSException {
/// Create a new [`NSException`] object.
///
/// Returns `None` if the exception couldn't be created (example: If the
/// process is out of memory).
#[cfg(all(feature = "NSObjCRuntime", feature = "NSString"))]
#[cfg(feature = "NSDictionary")]
pub fn new(
name: &crate::Foundation::NSExceptionName,
reason: Option<&crate::Foundation::NSString>,
user_info: Option<&crate::Foundation::NSDictionary>,
) -> Option<Id<Self>> {
use objc2::ClassType;
unsafe {
objc2::msg_send_id![
Self::alloc(),
initWithName: name,
reason: reason,
userInfo: user_info,
]
}
}
/// Raises the exception, causing program flow to jump to the local
/// exception handler.
///
/// This is equivalent to using `objc2::exception::throw`.
///
///
/// # Safety
///
/// Same as `objc2::exception::throw`.
pub unsafe fn raise(&self) -> ! {
// SAFETY: `NSException` is immutable, so it is safe to give to
// the place where `@catch` receives it.
unsafe { self.raise_raw() };
// SAFETY: `raise` will throw an exception, or abort if something
// unexpected happened.
unsafe { unreachable_unchecked() }
}
/// Convert this into an [`Exception`] object.
pub fn into_exception(this: Id<Self>) -> Id<Exception> {
// SAFETY: Downcasting to "subclass"
unsafe { Id::cast(this) }
}
fn is_nsexception(obj: &Exception) -> bool {
if obj.class().responds_to(sel!(isKindOfClass:)) {
// SAFETY: We only use `isKindOfClass:` on NSObject
let obj: *const Exception = obj;
let obj = unsafe { obj.cast::<NSObject>().as_ref().unwrap() };
obj.is_kind_of::<Self>()
} else {
false
}
}
/// Create this from an [`Exception`] object.
///
/// This should be considered a hint; it may return `Err` in very, very
/// few cases where the object is actually an instance of `NSException`.
pub fn from_exception(obj: Id<Exception>) -> Result<Id<Self>, Id<Exception>> {
if Self::is_nsexception(&obj) {
// SAFETY: Just checked the object is an NSException
Ok(unsafe { Id::cast::<Self>(obj) })
} else {
Err(obj)
}
}
}
#[cfg(all(feature = "NSObjCRuntime", feature = "NSString"))]
impl fmt::Debug for NSException {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let obj: &objc2::runtime::AnyObject = self.as_ref();
write!(f, "{obj:?} '{}'", self.name())?;
if let Some(reason) = self.reason() {
write!(f, " reason:{reason}")?;
} else {
write!(f, " reason:(NULL)")?;
}
Ok(())
}
}