Skip to content

Commit 8baf5d9

Browse files
committed
Refactor __invoke method on argument tuples
1 parent 06b56d3 commit 8baf5d9

File tree

4 files changed

+174
-48
lines changed

4 files changed

+174
-48
lines changed

crates/objc2/src/encode.rs

Lines changed: 159 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ use core::sync::atomic;
8484
#[doc(inline)]
8585
pub use objc2_encode::{Encoding, EncodingBox, ParseError};
8686

87+
use crate::runtime::{AnyObject, Imp, Sel};
88+
8789
/// Types that have an Objective-C type-encoding.
8890
///
8991
/// Usually you will want to implement [`RefEncode`] as well.
@@ -364,45 +366,180 @@ mod args_private {
364366
/// argument has an Objective-C type-encoding, or can be converted from one.
365367
///
366368
/// This is implemented for tuples of up to 16 arguments, where each argument
367-
/// implements [`EncodeArgument`]. It is primarily used to make generic
368-
/// code a bit easier.
369+
/// implements [`EncodeArgument`]. It is a sealed trait, and should not need
370+
/// to be implemented manually - it is primarily used to make generic code a
371+
/// bit easier to read and understand.
369372
///
370373
/// Note that tuples themselves don't implement [`Encode`] directly, because
371374
/// they're not FFI-safe!
372375
pub trait EncodeArguments: args_private::Sealed {
373376
/// The encodings for the arguments.
374377
const ENCODINGS: &'static [Encoding];
378+
379+
/// Invoke a message sending function with the given object, selector,
380+
/// and arguments.
381+
///
382+
/// Implementation-wise, this is a bit ugly, but simply just easiest to
383+
/// have the method on this trait, since inside `MessageReceiver` we only
384+
/// want to publicly require `EncodeArguments`, and not another private
385+
/// trait.
386+
#[doc(hidden)]
387+
unsafe fn __invoke<R: EncodeReturn>(
388+
msg_send_fn: Imp,
389+
receiver: *mut AnyObject,
390+
sel: Sel,
391+
args: Self,
392+
) -> R;
375393
}
376394

377395
macro_rules! encode_args_impl {
378-
($($Arg: ident),*) => {
379-
impl<$($Arg: EncodeArgument),*> args_private::Sealed for ($($Arg,)*) {}
396+
($($a:ident: $T: ident),*) => {
397+
impl<$($T: EncodeArgument),*> args_private::Sealed for ($($T,)*) {}
380398

381-
impl<$($Arg: EncodeArgument),*> EncodeArguments for ($($Arg,)*) {
399+
impl<$($T: EncodeArgument),*> EncodeArguments for ($($T,)*) {
382400
const ENCODINGS: &'static [Encoding] = &[
383-
$($Arg::ENCODING_ARGUMENT),*
401+
$($T::ENCODING_ARGUMENT),*
384402
];
403+
404+
#[inline]
405+
unsafe fn __invoke<R: EncodeReturn>(msg_send_fn: Imp, receiver: *mut AnyObject, sel: Sel, ($($a,)*): Self) -> R {
406+
// Message sending works by passing the receiver as the first
407+
// argument, the selector as the second argument, and the rest
408+
// of the arguments after that.
409+
//
410+
// The imp must be cast to the appropriate function pointer
411+
// type before being called; contrary to how the headers and
412+
// documentation describe them, the msgSend functions are not
413+
// parametric on all platforms, instead they "trampoline" to
414+
// the actual method implementations.
415+
//
416+
// SAFETY: We're transmuting an `unsafe` function pointer to
417+
// another `unsafe` function pointer.
418+
#[cfg(not(feature = "unstable-c-unwind"))]
419+
let msg_send_fn: unsafe extern "C" fn(*mut AnyObject, Sel $(, $T)*) -> R = unsafe {
420+
mem::transmute(msg_send_fn)
421+
};
422+
#[cfg(feature = "unstable-c-unwind")]
423+
let msg_send_fn: unsafe extern "C-unwind" fn(*mut AnyObject, Sel $(, $T)*) -> R = unsafe {
424+
mem::transmute(msg_send_fn)
425+
};
426+
427+
// SAFETY: Caller upholds that the imp is safe to call with
428+
// the given receiver, selector and arguments.
429+
//
430+
// TODO: On x86_64 it would be more efficient to use a GOT
431+
// entry here (e.g. adding `nonlazybind` in LLVM).
432+
// Same can be said of e.g. `objc_retain` and `objc_release`.
433+
unsafe { msg_send_fn(receiver, sel $(, $a)*) }
434+
}
385435
}
386436
};
387437
}
388438

389439
encode_args_impl!();
390-
encode_args_impl!(A);
391-
encode_args_impl!(A, B);
392-
encode_args_impl!(A, B, C);
393-
encode_args_impl!(A, B, C, D);
394-
encode_args_impl!(A, B, C, D, E);
395-
encode_args_impl!(A, B, C, D, E, F);
396-
encode_args_impl!(A, B, C, D, E, F, G);
397-
encode_args_impl!(A, B, C, D, E, F, G, H);
398-
encode_args_impl!(A, B, C, D, E, F, G, H, I);
399-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J);
400-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K);
401-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
402-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M);
403-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
404-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
405-
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
440+
encode_args_impl!(a: A);
441+
encode_args_impl!(a: A, b: B);
442+
encode_args_impl!(a: A, b: B, c: C);
443+
encode_args_impl!(a: A, b: B, c: C, d: D);
444+
encode_args_impl!(a: A, b: B, c: C, d: D, e: E);
445+
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
446+
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
447+
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
448+
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
449+
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
450+
encode_args_impl!(
451+
a: A,
452+
b: B,
453+
c: C,
454+
d: D,
455+
e: E,
456+
f: F,
457+
g: G,
458+
h: H,
459+
i: I,
460+
j: J,
461+
k: K
462+
);
463+
encode_args_impl!(
464+
a: A,
465+
b: B,
466+
c: C,
467+
d: D,
468+
e: E,
469+
f: F,
470+
g: G,
471+
h: H,
472+
i: I,
473+
j: J,
474+
k: K,
475+
l: L
476+
);
477+
encode_args_impl!(
478+
a: A,
479+
b: B,
480+
c: C,
481+
d: D,
482+
e: E,
483+
f: F,
484+
g: G,
485+
h: H,
486+
i: I,
487+
j: J,
488+
k: K,
489+
l: L,
490+
m: M
491+
);
492+
encode_args_impl!(
493+
a: A,
494+
b: B,
495+
c: C,
496+
d: D,
497+
e: E,
498+
f: F,
499+
g: G,
500+
h: H,
501+
i: I,
502+
j: J,
503+
k: K,
504+
l: L,
505+
m: M,
506+
n: N
507+
);
508+
encode_args_impl!(
509+
a: A,
510+
b: B,
511+
c: C,
512+
d: D,
513+
e: E,
514+
f: F,
515+
g: G,
516+
h: H,
517+
i: I,
518+
j: J,
519+
k: K,
520+
l: L,
521+
m: M,
522+
n: N,
523+
o: O
524+
);
525+
encode_args_impl!(
526+
a: A,
527+
b: B,
528+
c: C,
529+
d: D,
530+
e: E,
531+
f: F,
532+
g: G,
533+
h: H,
534+
i: I,
535+
j: J,
536+
k: K,
537+
l: L,
538+
m: M,
539+
n: N,
540+
o: O,
541+
p: P
542+
);
406543

407544
// TODO: Implement for `PhantomData` and `PhantomPinned`?
408545

crates/objc2/src/runtime/message.rs

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use core::mem::{self, ManuallyDrop};
1+
use core::mem::ManuallyDrop;
22
use core::ptr::{self, NonNull};
33

44
use crate::__macro_helpers::{ConvertArgument, ConvertReturn};
5-
use crate::encode::{Encode, EncodeArgument, EncodeReturn, Encoding};
5+
use crate::encode::{Encode, EncodeArguments, EncodeReturn};
66
use crate::mutability::IsMutable;
77
use crate::rc::Id;
88
use crate::runtime::{AnyClass, AnyObject, Imp, Sel};
@@ -372,7 +372,12 @@ pub unsafe trait MessageReceiver: private::Sealed + Sized {
372372
{
373373
// SAFETY: Caller ensures only valid or NULL pointers.
374374
let obj = unsafe { this.as_ref() };
375-
msg_send_check(obj, sel, A::__ENCODINGS, &R::__Inner::ENCODING_RETURN);
375+
msg_send_check(
376+
obj,
377+
sel,
378+
A::__Inner::ENCODINGS,
379+
&R::__Inner::ENCODING_RETURN,
380+
);
376381
}
377382
unsafe {
378383
ConvertReturn::__from_return(conditional_try!(|| msg_send_primitive::send_unverified(
@@ -419,7 +424,7 @@ pub unsafe trait MessageReceiver: private::Sealed + Sized {
419424
msg_send_check_class(
420425
superclass,
421426
sel,
422-
A::__ENCODINGS,
427+
A::__Inner::ENCODINGS,
423428
&R::__Inner::ENCODING_RETURN,
424429
);
425430
}
@@ -659,7 +664,7 @@ mod message_args_private {
659664
/// issue if you know a use-case where this restrition should be lifted!
660665
pub unsafe trait MessageArguments: message_args_private::Sealed {
661666
#[doc(hidden)]
662-
const __ENCODINGS: &'static [Encoding];
667+
type __Inner: EncodeArguments;
663668

664669
/// Invoke an [`Imp`] with the given object, selector, and arguments.
665670
///
@@ -682,30 +687,13 @@ macro_rules! message_args_impl {
682687
impl<$($t: ConvertArgument),*> message_args_private::Sealed for ($($t,)*) {}
683688

684689
unsafe impl<$($t: ConvertArgument),*> MessageArguments for ($($t,)*) {
685-
const __ENCODINGS: &'static [Encoding] = &[
686-
$($t::__Inner::ENCODING_ARGUMENT),*
687-
];
690+
type __Inner = ($($t::__Inner,)*);
688691

689692
#[inline]
690693
unsafe fn __invoke<R: EncodeReturn>(imp: Imp, obj: *mut AnyObject, sel: Sel, ($($a,)*): Self) -> R {
691694
$(let $a = ConvertArgument::__into_argument($a);)*
692695

693-
// The imp must be cast to the appropriate function pointer
694-
// type before being called; the msgSend functions are not
695-
// parametric, but instead "trampolines" to the actual
696-
// method implementations.
697-
#[cfg(not(feature = "unstable-c-unwind"))]
698-
let imp: unsafe extern "C" fn(*mut AnyObject, Sel $(, $t::__Inner)*) -> R = unsafe {
699-
mem::transmute(imp)
700-
};
701-
#[cfg(feature = "unstable-c-unwind")]
702-
let imp: unsafe extern "C-unwind" fn(*mut AnyObject, Sel $(, $t::__Inner)*) -> R = unsafe {
703-
mem::transmute(imp)
704-
};
705-
// TODO: On x86_64 it would be more efficient to use a GOT
706-
// entry here (e.g. adding `nonlazybind` in LLVM).
707-
// Same can be said of e.g. `objc_retain` and `objc_release`.
708-
let result = unsafe { imp(obj, sel $(, $a.0)*) };
696+
let result = unsafe { <Self::__Inner as EncodeArguments>::__invoke(imp, obj, sel, ($($a.0,)*)) };
709697

710698
// TODO: If we want `objc_retainAutoreleasedReturnValue` to
711699
// work, we must not do any work before it has been run; so
@@ -717,6 +705,7 @@ macro_rules! message_args_impl {
717705
// details.
718706
unsafe { <$t as ConvertArgument>::__process_after_message_send($a.1) };
719707
)*
708+
720709
result
721710
}
722711
}

crates/test-ui/ui/invalid_option_encode_impl.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0080]: evaluation of `<std::option::Option<MyType> as objc2::Encode>::ENC
22
--> $WORKSPACE/crates/objc2/src/encode.rs
33
|
44
| panic!("invalid OptionEncode + Encode implementation");
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'invalid OptionEncode + Encode implementation', $WORKSPACE/crates/objc2/src/encode.rs:274:13
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'invalid OptionEncode + Encode implementation', $WORKSPACE/crates/objc2/src/encode.rs:276:13
66
|
77
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
88

crates/test-ui/ui/invalid_option_encode_impl_ref.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0080]: evaluation of `<std::option::Option<MyType> as objc2::RefEncode>::
22
--> $WORKSPACE/crates/objc2/src/encode.rs
33
|
44
| panic!("invalid OptionEncode + RefEncode implementation");
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'invalid OptionEncode + RefEncode implementation', $WORKSPACE/crates/objc2/src/encode.rs:284:13
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'invalid OptionEncode + RefEncode implementation', $WORKSPACE/crates/objc2/src/encode.rs:286:13
66
|
77
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
88

0 commit comments

Comments
 (0)