Skip to content

Commit 9c74cc3

Browse files
committed
Fix unwinding while using writeback parameters
1 parent 193d1bc commit 9c74cc3

File tree

5 files changed

+198
-101
lines changed

5 files changed

+198
-101
lines changed

crates/objc2/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
235235
nullable argument.
236236
* Fixed handling of methods that return NULL errors. This affected for example
237237
`-[MTLBinaryArchive serializeToURL:error:]`.
238+
* Fixed unwinding while using writeback / error parameters.
238239

239240

240241
## 0.5.2 - 2024-05-21

crates/objc2/src/__macro_helpers/convert.rs

+30-31
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,34 @@ mod argument_private {
1313
/// Objective-C `BOOL`, where it would otherwise not be allowed (since they
1414
/// are not ABI compatible).
1515
///
16-
/// This is also done specially for `&mut Retained<_>`-like arguments, to allow
17-
/// using those as "out" parameters.
16+
/// This is also done specially for `&mut Retained<_>`-like arguments, to
17+
/// allow using those as "out" / pass-by-writeback parameters.
1818
pub trait ConvertArgument: argument_private::Sealed {
1919
/// The inner type that this can be converted to and from.
2020
#[doc(hidden)]
2121
type __Inner: EncodeArgument;
2222

2323
/// A helper type for out parameters.
24+
///
25+
/// When dropped, this will process any necessary change to the
26+
/// parameters.
2427
#[doc(hidden)]
25-
type __StoredBeforeMessage: Sized;
28+
type __WritebackOnDrop: Sized;
2629

2730
#[doc(hidden)]
2831
fn __from_defined_param(inner: Self::__Inner) -> Self;
2932

33+
/// # Safety
34+
///
35+
/// The `__WritebackOnDrop` return type must not be leaked, and the
36+
/// `__Inner` pointer must not be used after the `__WritebackOnDrop` has
37+
/// dropped.
38+
///
39+
/// NOTE: The standard way to ensure such a thing is with closures, but
40+
/// using those would interact poorly with backtraces of the message send,
41+
/// so we're forced to ensure this out of band.
3042
#[doc(hidden)]
31-
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage);
32-
33-
#[doc(hidden)]
34-
#[inline]
35-
unsafe fn __process_after_message_send(_stored: Self::__StoredBeforeMessage) {}
43+
unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop);
3644
}
3745

3846
// Implemented in writeback.rs
@@ -45,15 +53,15 @@ impl<T: EncodeArgument> argument_private::Sealed for T {}
4553
impl<T: EncodeArgument> ConvertArgument for T {
4654
type __Inner = Self;
4755

48-
type __StoredBeforeMessage = ();
56+
type __WritebackOnDrop = ();
4957

5058
#[inline]
5159
fn __from_defined_param(inner: Self::__Inner) -> Self {
5260
inner
5361
}
5462

5563
#[inline]
56-
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
64+
unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
5765
(self, ())
5866
}
5967
}
@@ -62,15 +70,15 @@ impl argument_private::Sealed for bool {}
6270
impl ConvertArgument for bool {
6371
type __Inner = Bool;
6472

65-
type __StoredBeforeMessage = ();
73+
type __WritebackOnDrop = ();
6674

6775
#[inline]
6876
fn __from_defined_param(inner: Self::__Inner) -> Self {
6977
inner.as_bool()
7078
}
7179

7280
#[inline]
73-
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
81+
unsafe fn __into_argument(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
7482
(Bool::new(self), ())
7583
}
7684
}
@@ -127,13 +135,10 @@ pub trait ConvertArguments {
127135
type __Inner: EncodeArguments;
128136

129137
#[doc(hidden)]
130-
type __StoredBeforeMessage: Sized;
131-
132-
#[doc(hidden)]
133-
fn __into_arguments(self) -> (Self::__Inner, Self::__StoredBeforeMessage);
138+
type __WritebackOnDrop: Sized;
134139

135140
#[doc(hidden)]
136-
unsafe fn __process_after_message_send(_stored: Self::__StoredBeforeMessage);
141+
unsafe fn __into_arguments(self) -> (Self::__Inner, Self::__WritebackOnDrop);
137142
}
138143

139144
pub trait TupleExtender<T> {
@@ -148,22 +153,16 @@ macro_rules! args_impl {
148153
impl<$($t: ConvertArgument),*> ConvertArguments for ($($t,)*) {
149154
type __Inner = ($($t::__Inner,)*);
150155

151-
type __StoredBeforeMessage = ($($t::__StoredBeforeMessage,)*);
156+
type __WritebackOnDrop = ($($t::__WritebackOnDrop,)*);
152157

153158
#[inline]
154-
fn __into_arguments(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
159+
unsafe fn __into_arguments(self) -> (Self::__Inner, Self::__WritebackOnDrop) {
155160
let ($($a,)*) = self;
156-
$(let $a = ConvertArgument::__into_argument($a);)*
161+
// SAFETY: Upheld by caller
162+
$(let $a = unsafe { ConvertArgument::__into_argument($a) };)*
157163

158164
(($($a.0,)*), ($($a.1,)*))
159165
}
160-
161-
#[inline]
162-
unsafe fn __process_after_message_send(($($a,)*): Self::__StoredBeforeMessage) {
163-
$(
164-
unsafe { <$t as ConvertArgument>::__process_after_message_send($a) };
165-
)*
166-
}
167166
}
168167

169168
impl<$($t,)* T> TupleExtender<T> for ($($t,)*) {
@@ -296,7 +295,7 @@ mod tests {
296295
TypeId::of::<i32>()
297296
);
298297
assert_eq!(<i32 as ConvertArgument>::__from_defined_param(42), 42);
299-
assert_eq!(ConvertArgument::__into_argument(42i32).0, 42);
298+
assert_eq!(unsafe { ConvertArgument::__into_argument(42i32).0 }, 42);
300299
}
301300

302301
#[test]
@@ -306,7 +305,7 @@ mod tests {
306305
TypeId::of::<i8>()
307306
);
308307
assert_eq!(<i8 as ConvertArgument>::__from_defined_param(-3), -3);
309-
assert_eq!(ConvertArgument::__into_argument(-3i32).0, -3);
308+
assert_eq!(unsafe { ConvertArgument::__into_argument(-3i32).0 }, -3);
310309
}
311310

312311
#[test]
@@ -316,8 +315,8 @@ mod tests {
316315
assert!(!<bool as ConvertReturn>::__from_return(Bool::NO));
317316
assert!(<bool as ConvertReturn>::__from_return(Bool::YES));
318317

319-
assert!(!ConvertArgument::__into_argument(false).0.as_bool());
320-
assert!(ConvertArgument::__into_argument(true).0.as_bool());
318+
assert!(!unsafe { ConvertArgument::__into_argument(false).0 }.as_bool());
319+
assert!(unsafe { ConvertArgument::__into_argument(true).0 }.as_bool());
321320
assert!(!ConvertReturn::__into_defined_return(false).as_bool());
322321
assert!(ConvertReturn::__into_defined_return(true).as_bool());
323322

crates/objc2/src/__macro_helpers/msg_send.rs

+12-17
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,17 @@ pub trait MsgSend: Sized {
2121
A: ConvertArguments,
2222
R: ConvertReturn,
2323
{
24-
let (args, stored) = A::__into_arguments(args);
24+
// SAFETY: The writeback helper is not leaked (it is dropped at the
25+
// end of this scope).
26+
//
27+
// TODO: If we want `objc_retainAutoreleasedReturnValue` to work, we
28+
// must not do any work before it has been run; so somehow, we should
29+
// do that _before_ the helper is dropped!
30+
let (args, _helper) = unsafe { A::__into_arguments(args) };
2531

26-
// SAFETY: Upheld by caller
32+
// SAFETY: Upheld by caller.
2733
let result = unsafe { MessageReceiver::send_message(self.into_raw_receiver(), sel, args) };
2834

29-
// TODO: If we want `objc_retainAutoreleasedReturnValue` to
30-
// work, we must not do any work before it has been run; so
31-
// somehow, we should do that _before_ this call!
32-
//
33-
// SAFETY: The argument was passed to the message sending
34-
// function, and the stored values are only processed this
35-
// once. See `src/__macro_helpers/writeback.rs` for
36-
// details.
37-
unsafe { A::__process_after_message_send(stored) };
38-
3935
R::__from_return(result)
4036
}
4137

@@ -46,16 +42,15 @@ pub trait MsgSend: Sized {
4642
A: ConvertArguments,
4743
R: ConvertReturn,
4844
{
49-
let (args, stored) = A::__into_arguments(args);
45+
// SAFETY: The writeback helper is not leaked (it is dropped at the
46+
// end of this scope).
47+
let (args, _helper) = unsafe { A::__into_arguments(args) };
5048

51-
// SAFETY: Upheld by caller
49+
// SAFETY: Upheld by caller.
5250
let result = unsafe {
5351
MessageReceiver::send_super_message(self.into_raw_receiver(), superclass, sel, args)
5452
};
5553

56-
// SAFETY: Same as in send_message above.
57-
unsafe { A::__process_after_message_send(stored) };
58-
5954
R::__from_return(result)
6055
}
6156

0 commit comments

Comments
 (0)