Skip to content

Commit 284f988

Browse files
committed
Make MessageReceiver simpler, and add internal trait MsgSend
There is value in having a simpler `MessageReceiver` which is understandable by end users, and that doesn't possibly incur extra costs depending on which arguments/return type you pass it.
1 parent 829dc2f commit 284f988

33 files changed

+501
-379
lines changed

LAYERED_SAFETY.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,14 @@ Doing the Rust equivalent of Objective-C's `NSUInteger hash_code = [obj hash];`.
5252

5353
```rust
5454
let obj: *const c_void = ...;
55-
let sel = unsafe { sel_registerName(b"hash\0".as_ptr() as *const c_char) };
56-
let fnptr = unsafe {
55+
let sel = unsafe { ffi::sel_registerName(b"hash\0".as_ptr() as *const c_char) };
56+
let msg_send_fn = unsafe {
5757
mem::transmute::<
58-
extern "C" fn(*const c_void, SEL) -> NSUInteger,
59-
extern "C" fn(),
60-
>(objc_msgSend)
58+
unsafe extern "C" fn(),
59+
unsafe extern "C" fn(*const c_void, SEL) -> NSUInteger,
60+
>(ffi::objc_msgSend)
6161
};
62-
let hash_code = unsafe { fnptr(obj, sel) };
62+
let hash_code = unsafe { msg_send_fn(obj, sel) };
6363
```
6464

6565

@@ -68,8 +68,7 @@ let hash_code = unsafe { fnptr(obj, sel) };
6868
We can improve on this using [`MessageReceiver::send_message`], which
6969
abstracts away the calling convention details, as well as adding an `Encode`
7070
bound on all the involved types. This ensures that we don't accidentally try
71-
to pass e.g. a `Vec<T>`, which does not have a stable memory layout. It also
72-
handles details surrounding Objective-C's `BOOL` type.
71+
to pass e.g. a `Vec<T>`, which does not have a stable memory layout.
7372

7473
Additionally, when `debug_assertions` are enabled, the types involved in the
7574
message send are compared to the types exposed in the Objective-C runtime.
@@ -97,7 +96,8 @@ let hash_code: NSUInteger = unsafe {
9796

9897
Introducing macros: [`msg_send!`] can abstract away the tediousness of writing
9998
the selector expression, as well as ensuring that the number of arguments to
100-
the method is correct.
99+
the method is correct. It also handles details surrounding Objective-C's
100+
`BOOL` type.
101101

102102
[`msg_send!`]: https://docs.rs/objc2/0.3.0-beta.4/objc2/macro.msg_send.html
103103

crates/objc2/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2929
* **BREAKING**: Moved the `MethodImplementation` trait from the `declare`
3030
module to the `runtime` module.
3131
* **BREAKING**: Moved the `MessageReceiver` trait to the `runtime` module.
32+
* **BREAKING**: Make the `MessageReceiver` trait no longer implemented for
33+
references to `Id`. Dereference the `Id` yourself.
34+
35+
Note: Passing `&Id` in `msg_send!` is still supported.
36+
* **BREAKING**: `MessageReceiver::send_message` and
37+
`MessageReceiver::send_super_message` now take `EncodeArguments` and return
38+
`EncodeReturn`, instead of internal traits.
39+
40+
This is done to make `MessageReceiver` more straightforward to understand,
41+
although it now also has slightly less functionality than `msg_send!`.
42+
43+
In particular automatic conversion of `bool` is not supported in
44+
`MessageReceiver`.
3245

3346
### Deprecated
3447
* Soft deprecated using `msg_send!` without a comma between arguments (i.e.

crates/objc2/src/__macro_helpers/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ mod common_selectors;
2727
mod convert;
2828
mod declare_class;
2929
mod method_family;
30+
mod msg_send;
3031
mod msg_send_id;
3132
mod writeback;
3233

@@ -40,6 +41,7 @@ pub use self::declare_class::{
4041
pub use self::method_family::{
4142
retain_semantics, Alloc, CopyOrMutCopy, Init, New, Other, RetainSemantics,
4243
};
44+
pub use self::msg_send::MsgSend;
4345
pub use self::msg_send_id::{MaybeUnwrap, MsgSendId};
4446

4547
/// Helper struct for emitting the module info that macOS 32-bit requires.
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
use core::mem::ManuallyDrop;
2+
use core::ptr;
3+
4+
use crate::encode::RefEncode;
5+
use crate::mutability::IsMutable;
6+
use crate::rc::Id;
7+
use crate::runtime::{AnyClass, AnyObject, MessageReceiver, Sel};
8+
use crate::{ClassType, Encode, Message};
9+
10+
use super::{ConvertArguments, ConvertReturn, TupleExtender};
11+
12+
pub trait MsgSend: Sized {
13+
type Inner: ?Sized + RefEncode;
14+
15+
fn into_raw_receiver(self) -> *mut AnyObject;
16+
17+
#[inline]
18+
#[track_caller]
19+
unsafe fn send_message<A, R>(self, sel: Sel, args: A) -> R
20+
where
21+
A: ConvertArguments,
22+
R: ConvertReturn,
23+
{
24+
let (args, stored) = A::__into_arguments(args);
25+
26+
// SAFETY: Upheld by caller
27+
let result = unsafe { MessageReceiver::send_message(self.into_raw_receiver(), sel, args) };
28+
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+
39+
R::__from_return(result)
40+
}
41+
42+
#[inline]
43+
#[track_caller]
44+
unsafe fn send_super_message<A, R>(self, superclass: &AnyClass, sel: Sel, args: A) -> R
45+
where
46+
A: ConvertArguments,
47+
R: ConvertReturn,
48+
{
49+
let (args, stored) = A::__into_arguments(args);
50+
51+
// SAFETY: Upheld by caller
52+
let result = unsafe {
53+
MessageReceiver::send_super_message(self.into_raw_receiver(), superclass, sel, args)
54+
};
55+
56+
// SAFETY: Same as in send_message above.
57+
unsafe { A::__process_after_message_send(stored) };
58+
59+
R::__from_return(result)
60+
}
61+
62+
#[inline]
63+
#[track_caller]
64+
unsafe fn send_super_message_static<A, R>(self, sel: Sel, args: A) -> R
65+
where
66+
Self::Inner: ClassType,
67+
<Self::Inner as ClassType>::Super: ClassType,
68+
A: ConvertArguments,
69+
R: ConvertReturn,
70+
{
71+
unsafe { self.send_super_message(<Self::Inner as ClassType>::Super::class(), sel, args) }
72+
}
73+
74+
// Error functions below. See MsgSendId::send_message_id_error for further
75+
// details.
76+
//
77+
// Some of this could be abstracted away using closures, but that would
78+
// interfere with `#[track_caller]`, so we avoid doing that.
79+
80+
#[inline]
81+
#[track_caller]
82+
unsafe fn send_message_error<A, E>(self, sel: Sel, args: A) -> Result<(), Id<E>>
83+
where
84+
*mut *mut E: Encode,
85+
A: TupleExtender<*mut *mut E>,
86+
<A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
87+
E: Message,
88+
{
89+
let mut err: *mut E = ptr::null_mut();
90+
let args = args.add_argument(&mut err);
91+
let res: bool = unsafe { self.send_message(sel, args) };
92+
if res {
93+
Ok(())
94+
} else {
95+
Err(unsafe { encountered_error(err) })
96+
}
97+
}
98+
99+
#[inline]
100+
#[track_caller]
101+
unsafe fn send_super_message_error<A, E>(
102+
self,
103+
superclass: &AnyClass,
104+
sel: Sel,
105+
args: A,
106+
) -> Result<(), Id<E>>
107+
where
108+
*mut *mut E: Encode,
109+
A: TupleExtender<*mut *mut E>,
110+
<A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
111+
E: Message,
112+
{
113+
let mut err: *mut E = ptr::null_mut();
114+
let args = args.add_argument(&mut err);
115+
let res: bool = unsafe { self.send_super_message(superclass, sel, args) };
116+
if res {
117+
Ok(())
118+
} else {
119+
Err(unsafe { encountered_error(err) })
120+
}
121+
}
122+
123+
#[inline]
124+
#[track_caller]
125+
unsafe fn send_super_message_static_error<A, E>(self, sel: Sel, args: A) -> Result<(), Id<E>>
126+
where
127+
Self::Inner: ClassType,
128+
<Self::Inner as ClassType>::Super: ClassType,
129+
*mut *mut E: Encode,
130+
A: TupleExtender<*mut *mut E>,
131+
<A as TupleExtender<*mut *mut E>>::PlusOneArgument: ConvertArguments,
132+
E: Message,
133+
{
134+
let mut err: *mut E = ptr::null_mut();
135+
let args = args.add_argument(&mut err);
136+
let res: bool = unsafe { self.send_super_message_static(sel, args) };
137+
if res {
138+
Ok(())
139+
} else {
140+
Err(unsafe { encountered_error(err) })
141+
}
142+
}
143+
}
144+
145+
#[cold]
146+
#[track_caller]
147+
unsafe fn encountered_error<E: Message>(err: *mut E) -> Id<E> {
148+
// SAFETY: Ensured by caller
149+
unsafe { Id::retain(err) }.expect("error parameter should be set if the method returns NO")
150+
}
151+
152+
impl<T: ?Sized + MessageReceiver> MsgSend for T {
153+
type Inner = T::__Inner;
154+
155+
#[inline]
156+
fn into_raw_receiver(self) -> *mut AnyObject {
157+
MessageReceiver::__as_raw_receiver(self)
158+
}
159+
}
160+
161+
impl<'a, T: ?Sized + Message> MsgSend for &'a Id<T> {
162+
type Inner = T;
163+
164+
#[inline]
165+
fn into_raw_receiver(self) -> *mut AnyObject {
166+
(Id::as_ptr(self) as *mut T).cast()
167+
}
168+
}
169+
170+
impl<'a, T: ?Sized + Message + IsMutable> MsgSend for &'a mut Id<T> {
171+
type Inner = T;
172+
173+
#[inline]
174+
fn into_raw_receiver(self) -> *mut AnyObject {
175+
Id::as_mut_ptr(self).cast()
176+
}
177+
}
178+
179+
impl<T: ?Sized + Message> MsgSend for ManuallyDrop<Id<T>> {
180+
type Inner = T;
181+
182+
#[inline]
183+
fn into_raw_receiver(self) -> *mut AnyObject {
184+
Id::consume_as_ptr(ManuallyDrop::into_inner(self)).cast()
185+
}
186+
}
187+
188+
#[cfg(test)]
189+
mod tests {
190+
use crate::msg_send;
191+
use crate::test_utils;
192+
193+
use super::*;
194+
195+
#[test]
196+
fn test_send_message_manuallydrop() {
197+
let obj = ManuallyDrop::new(test_utils::custom_object());
198+
unsafe {
199+
let _: () = msg_send![obj, release];
200+
};
201+
// `obj` is consumed, can't use here
202+
}
203+
}

crates/objc2/src/__macro_helpers/msg_send_id.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use core::ptr;
22

33
use crate::encode::Encode;
44
use crate::rc::{Allocated, Id};
5-
use crate::runtime::{AnyClass, AnyObject, MessageReceiver, Sel};
5+
use crate::runtime::{AnyClass, AnyObject, Sel};
66
use crate::{sel, Message};
77

8-
use super::{Alloc, ConvertArguments, CopyOrMutCopy, Init, New, Other, TupleExtender};
8+
use super::{Alloc, ConvertArguments, CopyOrMutCopy, Init, MsgSend, New, Other, TupleExtender};
99

1010
pub trait MsgSendId<T, U> {
1111
#[track_caller]
@@ -66,16 +66,16 @@ unsafe fn encountered_error<E: Message>(err: *mut E) -> Id<E> {
6666
unsafe { Id::retain(err) }.expect("error parameter should be set if the method returns NULL")
6767
}
6868

69-
impl<T: MessageReceiver, U: ?Sized + Message> MsgSendId<T, Id<U>> for New {
69+
impl<T: MsgSend, U: ?Sized + Message> MsgSendId<T, Id<U>> for New {
7070
#[inline]
7171
unsafe fn send_message_id<A: ConvertArguments, R: MaybeUnwrap<Input = Id<U>>>(
7272
obj: T,
7373
sel: Sel,
7474
args: A,
7575
) -> R {
76-
let ptr = obj.__as_raw_receiver();
76+
let ptr = obj.into_raw_receiver();
7777
// SAFETY: Checked by caller
78-
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
78+
let obj = unsafe { MsgSend::send_message(ptr, sel, args) };
7979
// SAFETY: The selector is `new`, so this has +1 retain count
8080
let obj = unsafe { Id::new(obj) };
8181

@@ -93,7 +93,7 @@ impl<T: ?Sized + Message> MsgSendId<&'_ AnyClass, Allocated<T>> for Alloc {
9393
args: A,
9494
) -> R {
9595
// SAFETY: Checked by caller
96-
let obj = unsafe { MessageReceiver::send_message(cls, sel, args) };
96+
let obj = unsafe { MsgSend::send_message(cls, sel, args) };
9797
// SAFETY: The selector is `alloc`, so this has +1 retain count
9898
let obj = unsafe { Allocated::new(obj) };
9999
R::maybe_unwrap::<Self>(obj, (cls, sel))
@@ -114,39 +114,39 @@ impl<T: ?Sized + Message> MsgSendId<Option<Allocated<T>>, Id<T>> for Init {
114114
//
115115
// We do this for efficiency, to avoid having a branch that the user
116116
// did not intend after every `alloc`.
117-
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
117+
let obj = unsafe { MsgSend::send_message(ptr, sel, args) };
118118
// SAFETY: The selector is `init`, so this has +1 retain count
119119
let obj = unsafe { Id::new(obj) };
120120
R::maybe_unwrap::<Self>(obj, (ptr.cast(), sel))
121121
}
122122
}
123123

124-
impl<T: MessageReceiver, U: ?Sized + Message> MsgSendId<T, Id<U>> for CopyOrMutCopy {
124+
impl<T: MsgSend, U: ?Sized + Message> MsgSendId<T, Id<U>> for CopyOrMutCopy {
125125
#[inline]
126126
unsafe fn send_message_id<A: ConvertArguments, R: MaybeUnwrap<Input = Id<U>>>(
127127
obj: T,
128128
sel: Sel,
129129
args: A,
130130
) -> R {
131131
// SAFETY: Checked by caller
132-
let obj = unsafe { MessageReceiver::send_message(obj, sel, args) };
132+
let obj = unsafe { MsgSend::send_message(obj, sel, args) };
133133
// SAFETY: The selector is `copy` or `mutableCopy`, so this has +1
134134
// retain count
135135
let obj = unsafe { Id::new(obj) };
136136
R::maybe_unwrap::<Self>(obj, ())
137137
}
138138
}
139139

140-
impl<T: MessageReceiver, U: Message> MsgSendId<T, Id<U>> for Other {
140+
impl<T: MsgSend, U: Message> MsgSendId<T, Id<U>> for Other {
141141
#[inline]
142142
unsafe fn send_message_id<A: ConvertArguments, R: MaybeUnwrap<Input = Id<U>>>(
143143
obj: T,
144144
sel: Sel,
145145
args: A,
146146
) -> R {
147-
let ptr = obj.__as_raw_receiver();
147+
let ptr = obj.into_raw_receiver();
148148
// SAFETY: Checked by caller
149-
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
149+
let obj = unsafe { MsgSend::send_message(ptr, sel, args) };
150150
// All code between the message send and the `retain_autoreleased`
151151
// must be able to be optimized away for this to work.
152152

crates/objc2/src/macros/__method_msg_send.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/// Forward selector and arguments to `MessageReceiver::send_message[_error]`.
1+
/// Forward selector and arguments to `MsgSend::send_message[_error]`.
22
///
33
/// Note: We can't forward to `msg_send!` since that doesn't support selectors
44
/// with space between.
@@ -115,7 +115,7 @@ macro_rules! __method_msg_send {
115115
$crate::__msg_send_helper! {
116116
($receiver)
117117
// Use error method
118-
(__send_message_error)
118+
(send_message_error)
119119
($($sel_parsed)* $sel :)
120120
($($arg_parsed)*)
121121
}

crates/objc2/src/macros/__msg_send_parse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ macro_rules! __comma_between_args {
140140
#[cfg(feature = "unstable-msg-send-always-comma")]
141141
macro_rules! __comma_between_args {
142142
(
143-
(__send_super_message_static)
143+
(send_super_message_static)
144144
($($args:tt)*)
145145
($obj:expr)
146146
) => {

0 commit comments

Comments
 (0)