Skip to content

Commit

Permalink
Merge pull request #497 from madsmtm/refactor-encodings
Browse files Browse the repository at this point in the history
Add `EncodeReturn`, `EncodeArgument` and `EncodeArguments`
  • Loading branch information
madsmtm authored Sep 3, 2023
2 parents 567a970 + e03b060 commit c4dfb53
Show file tree
Hide file tree
Showing 30 changed files with 476 additions and 411 deletions.
7 changes: 3 additions & 4 deletions crates/block2/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use core::marker::PhantomData;
use core::mem;

use objc2::encode::__unstable::EncodeReturn;
use objc2::encode::{Encode, Encoding, RefEncode};
use objc2::encode::{EncodeArgument, EncodeReturn, Encoding, RefEncode};

use crate::ffi;

/// Types that may be used as the arguments of an Objective-C block.
///
/// This is implemented for tuples of up to 12 arguments, where each argument
/// implements [`Encode`].
/// implements [`EncodeArgument`].
///
///
/// # Safety
Expand All @@ -28,7 +27,7 @@ pub unsafe trait BlockArguments: Sized {

macro_rules! block_args_impl {
($($a:ident: $t:ident),*) => (
unsafe impl<$($t: Encode),*> BlockArguments for ($($t,)*) {
unsafe impl<$($t: EncodeArgument),*> BlockArguments for ($($t,)*) {
#[inline]
unsafe fn __call_block<R: EncodeReturn>(
invoke: unsafe extern "C" fn(),
Expand Down
10 changes: 5 additions & 5 deletions crates/block2/src/concrete_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use core::ops::Deref;
use core::ptr;
use std::os::raw::c_ulong;

use objc2::encode::__unstable::EncodeReturn;
use objc2::encode::{Encode, Encoding, RefEncode};
use objc2::encode::{EncodeArgument, EncodeReturn, Encoding, RefEncode};

use crate::{ffi, Block, BlockArguments, RcBlock};

Expand All @@ -17,7 +16,8 @@ mod private {
/// Types that may be converted into a [`ConcreteBlock`].
///
/// This is implemented for [`Fn`] closures of up to 12 arguments, where each
/// argument and the return type implements [`Encode`].
/// argument implements [`EncodeArgument`] and the return type implements
/// [`EncodeReturn`].
///
///
/// # Safety
Expand All @@ -37,12 +37,12 @@ macro_rules! concrete_block_impl {
concrete_block_impl!($f,);
);
($f:ident, $($a:ident : $t:ident),*) => (
impl<$($t: Encode,)* R: EncodeReturn, X> private::Sealed<($($t,)*)> for X
impl<$($t: EncodeArgument,)* R: EncodeReturn, X> private::Sealed<($($t,)*)> for X
where
X: Fn($($t,)*) -> R,
{}

unsafe impl<$($t: Encode,)* R: EncodeReturn, X> IntoConcreteBlock<($($t,)*)> for X
unsafe impl<$($t: EncodeArgument,)* R: EncodeReturn, X> IntoConcreteBlock<($($t,)*)> for X
where
X: Fn($($t,)*) -> R,
{
Expand Down
2 changes: 1 addition & 1 deletion crates/block2/src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::ops::Deref;
use core::ptr;
use std::os::raw::c_ulong;

use objc2::encode::__unstable::EncodeReturn;
use objc2::encode::EncodeReturn;

use super::{ffi, Block};
use crate::BlockArguments;
Expand Down
6 changes: 6 additions & 0 deletions crates/objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added
* Added `mutability::IsMainThreadOnly`.
* Added new `encode` traits `EncodeReturn`, `EncodeArgument` and
`EncodeArguments`.

### Changed
* **BREAKING**: `AnyClass::verify_sel` now take more well-defined types
`EncodeArguments` and `EncodeReturn`.


## 0.4.1 - 2023-07-31
Expand Down
168 changes: 168 additions & 0 deletions crates/objc2/src/__macro_helpers/convert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use crate::encode::{EncodeArgument, EncodeReturn};
use crate::rc::Id;
use crate::runtime::Bool;
use crate::Message;

mod argument_private {
pub trait Sealed {}
}

/// Represents types that can be converted to/from an [`EncodeArgument`] type.
///
/// This is implemented specially for [`bool`] to allow using that as
/// Objective-C `BOOL`, where it would otherwise not be allowed (since they
/// are not ABI compatible).
///
/// This is also done specially for `&mut Id<_>`-like arguments, to allow
/// using those as "out" parameters.
pub trait ConvertArgument: argument_private::Sealed {
/// The inner type that this can be converted to and from.
#[doc(hidden)]
type __Inner: EncodeArgument;

/// A helper type for out parameters.
#[doc(hidden)]
type __StoredBeforeMessage: Sized;

#[doc(hidden)]
fn __from_declared_param(inner: Self::__Inner) -> Self;

#[doc(hidden)]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage);

#[doc(hidden)]
unsafe fn __process_after_message_send(_stored: Self::__StoredBeforeMessage) {}
}

// Implemented in writeback.rs
impl<T: Message> argument_private::Sealed for &mut Id<T> {}
impl<T: Message> argument_private::Sealed for Option<&mut Id<T>> {}
impl<T: Message> argument_private::Sealed for &mut Option<Id<T>> {}
impl<T: Message> argument_private::Sealed for Option<&mut Option<Id<T>>> {}

impl<T: EncodeArgument> argument_private::Sealed for T {}
impl<T: EncodeArgument> ConvertArgument for T {
type __Inner = Self;

type __StoredBeforeMessage = ();

#[inline]
fn __from_declared_param(inner: Self::__Inner) -> Self {
inner
}

#[inline]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
(self, ())
}
}

impl argument_private::Sealed for bool {}
impl ConvertArgument for bool {
type __Inner = Bool;

type __StoredBeforeMessage = ();

#[inline]
fn __from_declared_param(inner: Self::__Inner) -> Self {
inner.as_bool()
}

#[inline]
fn __into_argument(self) -> (Self::__Inner, Self::__StoredBeforeMessage) {
(Bool::new(self), ())
}
}

mod return_private {
pub trait Sealed {}
}

/// Same as [`ConvertArgument`], but for return types.
pub trait ConvertReturn: return_private::Sealed {
/// The inner type that this can be converted to and from.
#[doc(hidden)]
type __Inner: EncodeReturn;

#[doc(hidden)]
fn __into_declared_return(self) -> Self::__Inner;

#[doc(hidden)]
fn __from_return(inner: Self::__Inner) -> Self;
}

impl<T: EncodeReturn> return_private::Sealed for T {}
impl<T: EncodeReturn> ConvertReturn for T {
type __Inner = Self;

#[inline]
fn __into_declared_return(self) -> Self::__Inner {
self
}

#[inline]
fn __from_return(inner: Self::__Inner) -> Self {
inner
}
}

impl return_private::Sealed for bool {}
impl ConvertReturn for bool {
type __Inner = Bool;

#[inline]
fn __into_declared_return(self) -> Self::__Inner {
Bool::new(self)
}

#[inline]
fn __from_return(inner: Self::__Inner) -> Self {
inner.as_bool()
}
}

#[cfg(test)]
mod tests {
use super::*;

use core::any::TypeId;

#[test]
fn convert_normally_noop() {
assert_eq!(
TypeId::of::<<i32 as ConvertArgument>::__Inner>(),
TypeId::of::<i32>()
);
assert_eq!(<i32 as ConvertArgument>::__from_declared_param(42), 42);
assert_eq!(ConvertArgument::__into_argument(42i32).0, 42);
}

#[test]
fn convert_i8() {
assert_eq!(
TypeId::of::<<i8 as ConvertArgument>::__Inner>(),
TypeId::of::<i8>()
);
assert_eq!(<i8 as ConvertArgument>::__from_declared_param(-3), -3);
assert_eq!(ConvertArgument::__into_argument(-3i32).0, -3);
}

#[test]
fn convert_bool() {
assert!(!<bool as ConvertArgument>::__from_declared_param(Bool::NO));
assert!(<bool as ConvertArgument>::__from_declared_param(Bool::YES));
assert!(!<bool as ConvertReturn>::__from_return(Bool::NO));
assert!(<bool as ConvertReturn>::__from_return(Bool::YES));

assert!(!ConvertArgument::__into_argument(false).0.as_bool());
assert!(ConvertArgument::__into_argument(true).0.as_bool());
assert!(!ConvertReturn::__into_declared_return(false).as_bool());
assert!(ConvertReturn::__into_declared_return(true).as_bool());

#[cfg(all(feature = "apple", target_os = "macos", target_arch = "x86_64"))]
assert_eq!(
<bool as ConvertArgument>::__Inner::ENCODING_ARGUMENT,
crate::encode::Encoding::Char,
);
}
}
7 changes: 5 additions & 2 deletions crates/objc2/src/__macro_helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ pub use std::sync::Once;

mod cache;
mod common_selectors;
mod convert;
mod declare_class;
mod writeback;

pub use self::cache::{CachedClass, CachedSel};
pub use self::common_selectors::{alloc_sel, dealloc_sel, init_sel, new_sel};
pub use self::declare_class::{
assert_mutability_matches_superclass_mutability, MaybeOptionId, MessageRecieveId,
ValidSubclassMutability,
};
pub use convert::{ConvertArgument, ConvertReturn};

/// Helper for specifying the retain semantics for a given selector family.
///
Expand Down Expand Up @@ -125,8 +128,8 @@ pub trait MsgSendId<T, U> {
} else {
// In this case, the error has very likely been created, but has
// been autoreleased (as is common for "out parameters", see
// `src/rc/writeback.rs`). Hence we need to retain it if we want
// it to live across autorelease pools.
// `src/__macro_helpers/writeback.rs`). Hence we need to retain it
// if we want it to live across autorelease pools.
//
// SAFETY: The message send is guaranteed to populate the error
// object, or leave it as NULL. The error is shared, and all
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
use core::mem::ManuallyDrop;
use core::ptr::NonNull;

use crate::encode::__unstable::EncodeConvertArgument;
use super::ConvertArgument;
use crate::rc::Id;
use crate::Message;

// Note the `'static` bound here - this may not be necessary, but I'm unsure
// of the exact requirements, so we better keep it for now.
impl<T: Message + 'static> EncodeConvertArgument for &mut Id<T> {
impl<T: Message + 'static> ConvertArgument for &mut Id<T> {
// We use `*mut T` as the inner value instead of `NonNull<T>`, since we
// want to do debug checking that the value hasn't unexpectedly been
// overwritten to contain NULL (which is clear UB, but the user might have
Expand Down Expand Up @@ -109,7 +109,7 @@ impl<T: Message + 'static> EncodeConvertArgument for &mut Id<T> {
}
}

impl<T: Message + 'static> EncodeConvertArgument for &mut Option<Id<T>> {
impl<T: Message + 'static> ConvertArgument for &mut Option<Id<T>> {
type __Inner = NonNull<*mut T>;

type __StoredBeforeMessage = (Self::__Inner, *mut T);
Expand Down Expand Up @@ -156,7 +156,7 @@ impl<T: Message + 'static> EncodeConvertArgument for &mut Option<Id<T>> {
// known at compile-time, and for the `None` case it would be detrimental to
// have extra `retain/release` calls here.

impl<T: Message + 'static> EncodeConvertArgument for Option<&mut Id<T>> {
impl<T: Message + 'static> ConvertArgument for Option<&mut Id<T>> {
type __Inner = Option<NonNull<*mut T>>;

type __StoredBeforeMessage = Option<(NonNull<*mut T>, NonNull<T>)>;
Expand Down Expand Up @@ -185,7 +185,7 @@ impl<T: Message + 'static> EncodeConvertArgument for Option<&mut Id<T>> {
}
}

impl<T: Message + 'static> EncodeConvertArgument for Option<&mut Option<Id<T>>> {
impl<T: Message + 'static> ConvertArgument for Option<&mut Option<Id<T>>> {
type __Inner = Option<NonNull<*mut T>>;

type __StoredBeforeMessage = Option<(NonNull<*mut T>, *mut T)>;
Expand Down
15 changes: 7 additions & 8 deletions crates/objc2/src/declare/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ use core::ptr;
use core::ptr::NonNull;
use std::ffi::CString;

use crate::encode::__unstable::{EncodeArguments, EncodeReturn};
use crate::encode::{Encode, Encoding, RefEncode};
use crate::encode::{Encode, EncodeArgument, EncodeArguments, EncodeReturn, Encoding, RefEncode};
use crate::ffi;
use crate::mutability::IsMutable;
use crate::rc::Allocated;
Expand Down Expand Up @@ -171,14 +170,14 @@ macro_rules! method_decl_impl {
where
T: ?Sized + $t_bound,
$r: EncodeReturn,
$($t: Encode,)*
$($t: EncodeArgument,)*
{}

impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f
where
T: ?Sized + $t_bound,
$r: EncodeReturn,
$($t: Encode,)*
$($t: EncodeArgument,)*
{
type Callee = T;
type Ret = $r;
Expand All @@ -193,13 +192,13 @@ macro_rules! method_decl_impl {
impl<$($l,)* $r, $($t),*> private::Sealed for $f
where
$r: EncodeReturn,
$($t: Encode,)*
$($t: EncodeArgument,)*
{}

impl<$($l,)* $r, $($t),*> MethodImplementation for $f
where
$r: EncodeReturn,
$($t: Encode,)*
$($t: EncodeArgument,)*
{
type Callee = $callee;
type Ret = $r;
Expand All @@ -215,14 +214,14 @@ macro_rules! method_decl_impl {
impl<T, $($t),*> private::Sealed for $f
where
T: ?Sized + Message,
$($t: Encode,)*
$($t: EncodeArgument,)*
{}

#[doc(hidden)]
impl<T, $($t),*> MethodImplementation for $f
where
T: ?Sized + Message,
$($t: Encode,)*
$($t: EncodeArgument,)*
{
type Callee = T;
type Ret = __IdReturnValue;
Expand Down
Loading

0 comments on commit c4dfb53

Please sign in to comment.