Skip to content

Commit bc3d434

Browse files
committed
Merge message sending primitives into one file to simplify maintenance
1 parent e70519b commit bc3d434

13 files changed

+244
-265
lines changed

crates/objc2/src/message/mod.rs renamed to crates/objc2/src/message.rs

Lines changed: 228 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use core::mem;
2-
use core::mem::ManuallyDrop;
1+
use core::mem::{self, ManuallyDrop};
32
use core::ptr::{self, NonNull};
43

54
use crate::__macro_helpers::{ConvertArgument, ConvertReturn};
@@ -39,6 +38,225 @@ macro_rules! conditional_try {
3938
}};
4039
}
4140

41+
#[cfg(feature = "apple")]
42+
mod msg_send_primitive {
43+
#[allow(unused_imports)]
44+
use core::mem;
45+
46+
use super::MessageArguments;
47+
use crate::encode::EncodeReturn;
48+
#[allow(unused_imports)]
49+
use crate::encode::Encoding;
50+
use crate::ffi;
51+
use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
52+
53+
/// On the below architectures we can statically find the correct method to
54+
/// call from the return type, by looking at its `EncodeReturn` impl.
55+
#[allow(clippy::missing_safety_doc)]
56+
unsafe trait MsgSendFn: EncodeReturn {
57+
const MSG_SEND: Imp;
58+
const MSG_SEND_SUPER: Imp;
59+
}
60+
61+
#[cfg(target_arch = "aarch64")]
62+
/// `objc_msgSend_stret` is not even available in arm64.
63+
///
64+
/// <https://twitter.com/gparker/status/378079715824660480>
65+
unsafe impl<T: EncodeReturn> MsgSendFn for T {
66+
const MSG_SEND: Imp = ffi::objc_msgSend;
67+
const MSG_SEND_SUPER: Imp = ffi::objc_msgSendSuper;
68+
}
69+
70+
#[cfg(target_arch = "arm")]
71+
/// Double-word sized fundamental data types don't use stret, but any
72+
/// composite type larger than 4 bytes does.
73+
///
74+
/// <https://web.archive.org/web/20191016000656/http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf>
75+
/// <https://developer.arm.com/documentation/ihi0042/latest>
76+
unsafe impl<T: EncodeReturn> MsgSendFn for T {
77+
const MSG_SEND: Imp = {
78+
if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN
79+
{
80+
ffi::objc_msgSend
81+
} else if mem::size_of::<T>() <= 4 {
82+
ffi::objc_msgSend
83+
} else {
84+
ffi::objc_msgSend_stret
85+
}
86+
};
87+
const MSG_SEND_SUPER: Imp = {
88+
if let Encoding::LongLong | Encoding::ULongLong | Encoding::Double = T::ENCODING_RETURN
89+
{
90+
ffi::objc_msgSendSuper
91+
} else if mem::size_of::<T>() <= 4 {
92+
ffi::objc_msgSendSuper
93+
} else {
94+
ffi::objc_msgSendSuper_stret
95+
}
96+
};
97+
}
98+
99+
#[cfg(target_arch = "x86")]
100+
/// Structures 1 or 2 bytes in size are placed in EAX.
101+
/// Structures 4 or 8 bytes in size are placed in: EAX and EDX.
102+
/// Structures of other sizes are placed at the address supplied by the caller.
103+
///
104+
/// <https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html>
105+
unsafe impl<T: EncodeReturn> MsgSendFn for T {
106+
const MSG_SEND: Imp = {
107+
// See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172
108+
if let Encoding::Float | Encoding::Double | Encoding::LongDouble = T::ENCODING_RETURN {
109+
ffi::objc_msgSend_fpret
110+
} else if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
111+
ffi::objc_msgSend
112+
} else {
113+
ffi::objc_msgSend_stret
114+
}
115+
};
116+
const MSG_SEND_SUPER: Imp = {
117+
if let 0 | 1 | 2 | 4 | 8 = mem::size_of::<T>() {
118+
ffi::objc_msgSendSuper
119+
} else {
120+
ffi::objc_msgSendSuper_stret
121+
}
122+
};
123+
}
124+
125+
#[cfg(target_arch = "x86_64")]
126+
/// If the size of an object is larger than two eightbytes, it has class
127+
/// MEMORY. If the type has class MEMORY, then the caller provides space for
128+
/// the return value and passes the address of this storage.
129+
///
130+
/// <https://www.uclibc.org/docs/psABI-x86_64.pdf>
131+
unsafe impl<T: EncodeReturn> MsgSendFn for T {
132+
const MSG_SEND: Imp = {
133+
// See https://github.com/apple-oss-distributions/objc4/blob/objc4-818.2/runtime/message.h#L156-L172
134+
if let Encoding::LongDouble = T::ENCODING_RETURN {
135+
ffi::objc_msgSend_fpret
136+
} else if let Encoding::LongDoubleComplex = T::ENCODING_RETURN {
137+
ffi::objc_msgSend_fp2ret
138+
} else if mem::size_of::<T>() <= 16 {
139+
ffi::objc_msgSend
140+
} else {
141+
ffi::objc_msgSend_stret
142+
}
143+
};
144+
const MSG_SEND_SUPER: Imp = {
145+
if mem::size_of::<T>() <= 16 {
146+
ffi::objc_msgSendSuper
147+
} else {
148+
ffi::objc_msgSendSuper_stret
149+
}
150+
};
151+
}
152+
153+
#[inline]
154+
#[track_caller]
155+
pub(crate) unsafe fn send_unverified<A, R>(receiver: *mut AnyObject, sel: Sel, args: A) -> R
156+
where
157+
A: MessageArguments,
158+
R: EncodeReturn,
159+
{
160+
let msg_send_fn = R::MSG_SEND;
161+
unsafe { conditional_try!(|| A::__invoke(msg_send_fn, receiver, sel, args)) }
162+
}
163+
164+
#[inline]
165+
#[track_caller]
166+
pub(crate) unsafe fn send_super_unverified<A, R>(
167+
receiver: *mut AnyObject,
168+
superclass: &AnyClass,
169+
sel: Sel,
170+
args: A,
171+
) -> R
172+
where
173+
A: MessageArguments,
174+
R: EncodeReturn,
175+
{
176+
let superclass: *const AnyClass = superclass;
177+
let mut sup = ffi::objc_super {
178+
receiver: receiver.cast(),
179+
super_class: superclass.cast(),
180+
};
181+
let receiver: *mut ffi::objc_super = &mut sup;
182+
let receiver = receiver.cast();
183+
184+
let msg_send_fn = R::MSG_SEND_SUPER;
185+
unsafe { conditional_try!(|| A::__invoke(msg_send_fn, receiver, sel, args)) }
186+
}
187+
}
188+
189+
#[cfg(feature = "gnustep-1-7")]
190+
mod msg_send_primitive {
191+
use core::mem;
192+
193+
use super::MessageArguments;
194+
use crate::encode::EncodeReturn;
195+
use crate::ffi;
196+
use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
197+
198+
#[inline]
199+
fn unwrap_msg_send_fn(msg_send_fn: Option<Imp>) -> Imp {
200+
match msg_send_fn {
201+
Some(msg_send_fn) => msg_send_fn,
202+
None => {
203+
// SAFETY: This will never be NULL, even if the selector is not
204+
// found a callable function pointer will still be returned!
205+
//
206+
// `clang` doesn't insert a NULL check here either.
207+
unsafe { core::hint::unreachable_unchecked() }
208+
}
209+
}
210+
}
211+
212+
#[track_caller]
213+
pub(crate) unsafe fn send_unverified<A, R>(receiver: *mut AnyObject, sel: Sel, args: A) -> R
214+
where
215+
A: MessageArguments,
216+
R: EncodeReturn,
217+
{
218+
// If `receiver` is NULL, objc_msg_lookup will return a standard C-method
219+
// taking two arguments, the receiver and the selector. Transmuting and
220+
// calling such a function with multiple parameters is UB, so instead we
221+
// return NULL directly.
222+
if receiver.is_null() {
223+
// SAFETY: Caller guarantees that messages to NULL-receivers only
224+
// return pointers, and a mem::zeroed pointer is just a NULL-pointer.
225+
return unsafe { mem::zeroed() };
226+
}
227+
228+
let msg_send_fn = unsafe { ffi::objc_msg_lookup(receiver.cast(), sel.as_ptr()) };
229+
let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
230+
unsafe { conditional_try!(|| A::__invoke(msg_send_fn, receiver, sel, args)) }
231+
}
232+
233+
#[track_caller]
234+
pub(crate) unsafe fn send_super_unverified<A, R>(
235+
receiver: *mut AnyObject,
236+
superclass: &AnyClass,
237+
sel: Sel,
238+
args: A,
239+
) -> R
240+
where
241+
A: MessageArguments,
242+
R: EncodeReturn,
243+
{
244+
if receiver.is_null() {
245+
// SAFETY: Same as in `send_unverified`.
246+
return unsafe { mem::zeroed() };
247+
}
248+
249+
let superclass: *const AnyClass = superclass;
250+
let sup = ffi::objc_super {
251+
receiver: receiver.cast(),
252+
super_class: superclass.cast(),
253+
};
254+
let msg_send_fn = unsafe { ffi::objc_msg_lookup_super(&sup, sel.as_ptr()) };
255+
let msg_send_fn = unwrap_msg_send_fn(msg_send_fn);
256+
unsafe { conditional_try!(|| A::__invoke(msg_send_fn, receiver, sel, args)) }
257+
}
258+
}
259+
42260
/// Help with monomorphizing in `icrate`
43261
#[cfg(debug_assertions)]
44262
#[track_caller]
@@ -95,15 +313,6 @@ fn panic_verify(cls: &AnyClass, sel: Sel, err: &crate::runtime::VerificationErro
95313
)
96314
}
97315

98-
#[cfg(feature = "apple")]
99-
#[path = "apple/mod.rs"]
100-
mod platform;
101-
#[cfg(feature = "gnustep-1-7")]
102-
#[path = "gnustep.rs"]
103-
mod platform;
104-
105-
use self::platform::{send_super_unverified, send_unverified};
106-
107316
/// Types that can be sent Objective-C messages.
108317
///
109318
/// Implementing this provides [`MessageReceiver`] implementations for common
@@ -239,7 +448,9 @@ pub unsafe trait MessageReceiver: private::Sealed + Sized {
239448
let obj = unsafe { this.as_ref() };
240449
msg_send_check(obj, sel, A::__ENCODINGS, &R::__Inner::ENCODING_RETURN);
241450
}
242-
unsafe { ConvertReturn::__from_return(send_unverified(this, sel, args)) }
451+
unsafe {
452+
ConvertReturn::__from_return(msg_send_primitive::send_unverified(this, sel, args))
453+
}
243454
}
244455

245456
/// Sends a message to a specific superclass with the given selector and
@@ -284,7 +495,11 @@ pub unsafe trait MessageReceiver: private::Sealed + Sized {
284495
&R::__Inner::ENCODING_RETURN,
285496
);
286497
}
287-
unsafe { ConvertReturn::__from_return(send_super_unverified(this, superclass, sel, args)) }
498+
unsafe {
499+
ConvertReturn::__from_return(msg_send_primitive::send_super_unverified(
500+
this, superclass, sel, args,
501+
))
502+
}
288503
}
289504

290505
#[inline]

crates/objc2/src/message/apple/arm.rs

Lines changed: 0 additions & 32 deletions
This file was deleted.

crates/objc2/src/message/apple/arm64.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.

crates/objc2/src/message/apple/mod.rs

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)