@@ -34,15 +34,21 @@ extern "C" {
34
34
/// # Safety
35
35
///
36
36
/// This unwinds from Objective-C, and the exception must be caught using an
37
- /// Objective-C exception handler.
37
+ /// Objective-C exception handler like [`catch`] (and specifically not
38
+ /// [`catch_unwind`]).
38
39
///
39
40
/// This also invokes undefined behaviour until `C-unwind` is stabilized, see
40
41
/// [RFC-2945].
41
42
///
43
+ /// [`catch_unwind`]: std::panic::catch_unwind
42
44
/// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
43
45
#[ inline]
44
- pub unsafe fn throw ( exception : * mut Object ) -> ! {
45
- objc_exception_throw ( exception as * mut objc_object )
46
+ pub unsafe fn throw ( exception : Option < & Id < Object , Shared > > ) -> ! {
47
+ let exception = match exception {
48
+ Some ( id) => & * * id as * const Object as * mut objc_object ,
49
+ None => ptr:: null_mut ( ) ,
50
+ } ;
51
+ objc_exception_throw ( exception)
46
52
}
47
53
48
54
unsafe fn try_no_ret < F : FnOnce ( ) > ( closure : F ) -> Result < ( ) , Option < Id < Object , Shared > > > {
@@ -64,6 +70,15 @@ unsafe fn try_no_ret<F: FnOnce()>(closure: F) -> Result<(), Option<Id<Object, Sh
64
70
if success == 0 {
65
71
Ok ( ( ) )
66
72
} else {
73
+ // SAFETY:
74
+ // The exception is always a valid object (or NULL, but that has been
75
+ // checked).
76
+ //
77
+ // The ownership is safe as Shared; Objective-C code throwing an
78
+ // exception knows that they don't hold sole access to that exception
79
+ // instance any more, and Rust code is forbidden by requiring a Shared
80
+ // Id in `throw` (instead of just a shared reference, which could have
81
+ // come from an Owned Id).
67
82
Err ( NonNull :: new ( exception as * mut Object ) . map ( |e| Id :: new ( e) ) )
68
83
}
69
84
}
@@ -77,7 +92,8 @@ unsafe fn try_no_ret<F: FnOnce()>(closure: F) -> Result<(), Option<Id<Object, Sh
77
92
///
78
93
/// # Safety
79
94
///
80
- /// The given closure must not panic.
95
+ /// The given closure must not panic (e.g. normal Rust unwinding into this
96
+ /// causes undefined behaviour).
81
97
///
82
98
/// Additionally, this unwinds through the closure from Objective-C, which is
83
99
/// undefined behaviour until `C-unwind` is stabilized, see [RFC-2945].
@@ -98,9 +114,8 @@ pub unsafe fn catch<R>(closure: impl FnOnce() -> R) -> Result<R, Option<Id<Objec
98
114
#[ cfg( test) ]
99
115
mod tests {
100
116
use alloc:: string:: ToString ;
101
- use core:: ptr;
102
117
103
- use super :: { catch , throw } ;
118
+ use super :: * ;
104
119
105
120
#[ test]
106
121
fn test_catch ( ) {
@@ -115,16 +130,26 @@ mod tests {
115
130
}
116
131
117
132
#[ test]
118
- fn test_throw_catch ( ) {
133
+ fn test_throw_catch_none ( ) {
119
134
let s = "Hello" . to_string ( ) ;
120
135
let result = unsafe {
121
136
catch ( move || {
122
137
if !s. is_empty ( ) {
123
- throw ( ptr :: null_mut ( ) ) ;
138
+ throw ( None ) ;
124
139
}
125
140
s. len ( )
126
141
} )
127
142
} ;
128
143
assert ! ( result. unwrap_err( ) . is_none( ) ) ;
129
144
}
145
+
146
+ #[ test]
147
+ fn test_throw_catch_object ( ) {
148
+ let obj: Id < Object , Shared > = unsafe { Id :: new ( msg_send ! [ class!( NSObject ) , new] ) } ;
149
+
150
+ let result = unsafe { catch ( || throw ( Some ( & obj) ) ) } ;
151
+ let e = result. unwrap_err ( ) . unwrap ( ) ;
152
+ // Compare pointers
153
+ assert_eq ! ( & * e as * const Object , & * obj as * const Object ) ;
154
+ }
130
155
}
0 commit comments