Skip to content

Commit

Permalink
Add extern_protocol! macro and ProtocolType trait
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Sep 23, 2022
1 parent 4b7e392 commit 904650e
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 8 deletions.
1 change: 1 addition & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added
* Allow directly specifying class name in `extern_class!` macro.
* Added `extern_protocol!` macro and `ProtocolType` trait.

### Changed
* Allow other types than `&Class` as the receiver in `msg_send_id!` methods
Expand Down
12 changes: 10 additions & 2 deletions objc2/src/foundation/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use core::hash;

use super::NSString;
use crate::rc::{DefaultId, Id, Owned, Shared};
use crate::runtime::{Class, Object};
use crate::{ClassType, __inner_extern_class, class, extern_methods, msg_send_id};
use crate::runtime::{Class, Object, Protocol};
use crate::{ClassType, ProtocolType, __inner_extern_class, class, extern_methods, msg_send_id};

__inner_extern_class! {
@__inner
Expand Down Expand Up @@ -33,6 +33,14 @@ unsafe impl ClassType for NSObject {
}
}

unsafe impl ProtocolType for NSObject {
const NAME: &'static str = "NSObject";

fn protocol() -> &'static Protocol {
Protocol::get(<Self as ProtocolType>::NAME).expect("Could not find NSObject protocol")
}
}

extern_methods!(
unsafe impl NSObject {
pub fn new() -> Id<Self, Owned> {
Expand Down
2 changes: 2 additions & 0 deletions objc2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ pub use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode};

pub use crate::class_type::ClassType;
pub use crate::message::{Message, MessageArguments, MessageReceiver};
pub use crate::protocol::{ProtocolImpl, ProtocolType};
#[cfg(feature = "malloc")]
pub use crate::verify::VerificationError;

Expand All @@ -224,6 +225,7 @@ pub mod exception;
pub mod foundation;
mod macros;
mod message;
mod protocol;
pub mod rc;
pub mod runtime;
#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions objc2/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod __rewrite_self_arg;
mod declare_class;
mod extern_class;
mod extern_methods;
mod extern_protocol;

/// Gets a reference to a [`Class`] from the given name.
///
Expand Down
6 changes: 3 additions & 3 deletions objc2/src/macros/declare_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,10 +626,10 @@ macro_rules! declare_class {

REGISTER_CLASS.call_once(|| {
let superclass = <$superclass as $crate::ClassType>::class();
let mut builder = $crate::declare::ClassBuilder::new(Self::NAME, superclass).unwrap_or_else(|| {
let mut builder = $crate::declare::ClassBuilder::new(<Self as ClassType>::NAME, superclass).unwrap_or_else(|| {
$crate::__macro_helpers::panic!(
"could not create new class {}. Perhaps a class with that name already exists?",
Self::NAME,
<Self as ClassType>::NAME,
)
});

Expand Down Expand Up @@ -660,7 +660,7 @@ macro_rules! declare_class {
});

// We just registered the class, so it should be available
$crate::runtime::Class::get(Self::NAME).unwrap()
$crate::runtime::Class::get(<Self as ClassType>::NAME).unwrap()
}

#[inline]
Expand Down
50 changes: 50 additions & 0 deletions objc2/src/macros/extern_protocol.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/// TODO
#[doc(alias = "@protocol")]
#[macro_export]
macro_rules! extern_protocol {
(
$(#[$m:meta])*
$v:vis struct $name:ident;

unsafe impl ProtocolType for $for:ty {
$(const NAME: &'static str = $name_const:literal;)?
}
) => {
$crate::__inner_extern_class!(
@__inner

$(#[$m])*
$v struct ($name) {}

unsafe impl () for $for {
INHERITS = [$crate::runtime::Object];
}
);

// SAFETY: TODO
unsafe impl ProtocolType for $for {
const NAME: &'static str = $crate::__select_name!($name; $($name_const)?);

#[inline]
fn protocol() -> &'static $crate::runtime::Protocol {
$crate::runtime::Protocol::get(<Self as ProtocolType>::NAME)
.unwrap_or_else(|| {
$crate::__macro_helpers::panic!(
"could not find protocol {}",
<Self as ProtocolType>::NAME,
)
})
}
}

const _: () = {
if $crate::__macro_helpers::size_of::<$name>() != 0 {
panic!(concat!(
"the struct ",
stringify!($name),
" is not zero-sized!",
))
}
};
};
}
65 changes: 65 additions & 0 deletions objc2/src/protocol.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use core::ptr::NonNull;

use crate::runtime::Protocol;
use crate::Message;

/// Marks types that represent specific protocols.
///
/// TODO.
///
///
/// # Safety
///
/// Same as [`ClassType`], TODO.
///
/// [`ClassType`]: crate::ClassType
pub unsafe trait ProtocolType: Message {
/// The name of the Objective-C protocol that this type represents.
const NAME: &'static str;

/// Get a reference to the Objective-C protocol that this type represents.
///
/// May register the protocol with the runtime if it wasn't already.
///
///
/// # Panics
///
/// This may panic if something went wrong with getting or declaring the
/// protocol, e.g. if the program is not properly linked to the framework
/// that defines the protocol.
fn protocol() -> &'static Protocol;
}

/// TODO
///
/// # Safety
///
/// TODO
pub unsafe trait ProtocolImpl<P: ProtocolType> {
/// Get an immutable reference to a type representing the protocol.
fn as_protocol(&self) -> &P {
let ptr: NonNull<Self> = NonNull::from(self);
let ptr: NonNull<P> = ptr.cast();
// SAFETY: TODO
unsafe { ptr.as_ref() }
}

/// Get a mutable reference to a type representing the protocol.
fn as_protocol_mut(&mut self) -> &mut P {
let ptr: NonNull<Self> = NonNull::from(self);
let mut ptr: NonNull<P> = ptr.cast();
// SAFETY: TODO
unsafe { ptr.as_mut() }
}
}

// SAFETY: Trivial
unsafe impl<P: ProtocolType> ProtocolImpl<P> for P {
fn as_protocol(&self) -> &P {
self
}

fn as_protocol_mut(&mut self) -> &mut P {
self
}
}
24 changes: 21 additions & 3 deletions objc2/src/rc/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::Allocated;
use super::AutoreleasePool;
use super::{Owned, Ownership, Shared};
use crate::ffi;
use crate::{ClassType, Message};
use crate::{ClassType, Message, ProtocolImpl, ProtocolType};

/// An pointer for Objective-C reference counted objects.
///
Expand Down Expand Up @@ -264,7 +264,8 @@ impl<T: Message, O: Ownership> Id<T, O> {
///
/// This is equivalent to a `cast` between two pointers.
///
/// See [`Id::into_super`] for a safe alternative.
/// See [`Id::into_super`] and [`Id::into_protocol`] for safe
/// alternatives.
///
/// This is common to do when you know that an object is a subclass of
/// a specific class (e.g. casting an instance of `NSString` to `NSObject`
Expand Down Expand Up @@ -639,7 +640,7 @@ impl<T: ClassType + 'static, O: Ownership> Id<T, O>
where
T::Super: 'static,
{
/// Convert the object into it's superclass.
/// Convert the object into its superclass.
#[inline]
pub fn into_super(this: Self) -> Id<T::Super, O> {
// SAFETY:
Expand All @@ -650,6 +651,23 @@ where
}
}

impl<T: Message, O: Ownership> Id<T, O> {
/// Convert the object into an object representing the specified protocol.
#[inline]
pub fn into_protocol<P>(this: Self) -> Id<P, O>
where
P: ProtocolType + 'static,
T: ProtocolImpl<P> + 'static,
{
// SAFETY:
// - The type can be represented as the casted-to type, since
// `T: ProtocolImpl` guarantees that it implements the protocol.
// - Both types are `'static` (this could maybe be relaxed a bit, but
// let's just be on the safe side)!
unsafe { Self::cast::<P>(this) }
}
}

impl<T: Message> From<Id<T, Owned>> for Id<T, Shared> {
/// Convert an owned to a shared [`Id`], allowing it to be cloned.
///
Expand Down

0 comments on commit 904650e

Please sign in to comment.