Skip to content

Commit

Permalink
TMP
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Feb 13, 2023
1 parent 3730c60 commit 974e14a
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 59 deletions.
105 changes: 63 additions & 42 deletions crates/icrate/src/Foundation/additions/copying.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,70 @@
use objc2::rc::{Id, Owned, Ownership};
use objc2::{msg_send_id, Message};
use crate::common::*;

pub unsafe trait NSCopying: Message {
/// Indicates whether the type is mutable or immutable.
///
/// This can be [`Owned`] if and only if `copy` creates a new instance,
/// see the following example:
///
/// ```ignore
/// let x: Id<MyObject, _> = MyObject::new();
/// // This is valid only if `y` is a new instance. Otherwise `x` and `y`
/// // would be able to create aliasing mutable references!
/// let y: Id<MyObject, Owned> = x.copy();
/// ```
///
/// Note that for the same reason, you should be careful when defining
/// `new` methods on your object; e.g. immutable types like [`NSString`]
/// don't return `Id<NSString, Owned>`, because that would allow this
/// trait to create an aliasing `Id<NSString, Shared>` (since sending the
/// `copy` message (and others) does not create a new instance, but
/// instead just retains the instance).
///
/// [`NSString`]: crate::Foundation::NSString
type Ownership: Ownership;
mod private {
use super::*;
use objc2::class::{
Immutable, ImmutableWithMutableSubclass, InteriorMutable, MainThreadOnly, Mutable, Unknown,
};

/// The output type.
///
/// This is usually `Self`, but e.g. `NSMutableString` returns `NSString`.
type Output: Message;

fn copy(&self) -> Id<Self::Output, Self::Ownership> {
unsafe { msg_send_id![self, copy] }
pub trait Copyhelper<T: ?Sized> {
type CopyOutput: ?Sized + Message;
type MutableCopyOutput: ?Sized + Message;
}
impl<T: ?Sized + Message> Copyhelper<T> for Unknown {
type CopyOutput = T;
type MutableCopyOutput = T;
}
impl<T: ?Sized + Message> Copyhelper<T> for Immutable {
type CopyOutput = T;
type MutableCopyOutput = T;
}
impl<T: ?Sized + Message, MutableSubclass: ?Sized + Message> Copyhelper<T>
for ImmutableWithMutableSubclass<MutableSubclass>
{
type CopyOutput = T;
type MutableCopyOutput = MutableSubclass;
}
impl<T: ?Sized + Message, ImmutableSuperclass: ?Sized + Message> Copyhelper<T>
for Mutable<ImmutableSuperclass>
{
type CopyOutput = ImmutableSuperclass;
type MutableCopyOutput = T;
}
impl<T: ?Sized + Message> Copyhelper<T> for InteriorMutable {
type CopyOutput = T;
type MutableCopyOutput = T;
}
impl<T: ?Sized + Message> Copyhelper<T> for MainThreadOnly {
type CopyOutput = T;
type MutableCopyOutput = T;
}
}

/// TODO
///
/// Note that the `mutableCopy` selector must return an owned object!
pub unsafe trait NSMutableCopying: Message {
/// The output type.
///
/// This is usually `Self`, but e.g. `NSString` returns `NSMutableString`.
type Output: Message;
extern_protocol!(
pub unsafe trait NSCopying {
#[method_id(copy)]
fn copy(&self) -> Id<<Self::Kind as private::Copyhelper<Self>>::CopyOutput>
where
Self: ClassType,
Self::Kind: private::Copyhelper<Self>;
}

fn mutable_copy(&self) -> Id<Self::Output, Owned> {
unsafe { msg_send_id![self, mutableCopy] }
unsafe impl ProtocolType for dyn NSCopying {}
);

extern_protocol!(
/// TODO
///
/// Note that the `mutableCopy` selector must return an owned object!
pub unsafe trait NSMutableCopying {
#[method_id(mutableCopy)]
fn mutableCopy(
&self,
) -> Id<<Self::Kind as private::Copyhelper<Self>>::MutableCopyOutput, Owned>
where
Self: ClassType,
Self::Kind: private::Copyhelper<Self>;
}
}

unsafe impl ProtocolType for dyn NSMutableCopying {}
);
4 changes: 4 additions & 0 deletions crates/objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased - YYYY-MM-DD

### Added
* Preliminary support for specifying `where` bounds on methods inside
`extern_protocol!`.

### Changed
* Made the default ownership in `Id` be `Shared`. This means that you can now
write `Id<NSString>`, and it'll mean `Id<NSString, Shared>`.
Expand Down
53 changes: 46 additions & 7 deletions crates/objc2/src/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ unsafe impl Retainable for Immutable {}
impl ClassKind for Immutable {}

/// Immutable, but disallows `Id::retain`
pub struct ImmutableWithMutableSubclass;
impl private::Sealed for ImmutableWithMutableSubclass {}
impl ClassKind for ImmutableWithMutableSubclass {}
pub struct ImmutableWithMutableSubclass<MutableSubclass: ?Sized>(MutableSubclass);
impl<MutableSubclass: ?Sized> private::Sealed for ImmutableWithMutableSubclass<MutableSubclass> {}
impl<MutableSubclass: ?Sized> ClassKind for ImmutableWithMutableSubclass<MutableSubclass> {}

/// Mutable, mutable methods use `&mut self`. `Id::retain` is not safe
pub struct Mutable;
impl private::Sealed for Mutable {}
impl ClassKind for Mutable {}
pub struct Mutable<ImmutableSuperclass: ?Sized>(ImmutableSuperclass);
impl<ImmutableSuperclass: ?Sized> private::Sealed for Mutable<ImmutableSuperclass> {}
impl<ImmutableSuperclass: ?Sized> ClassKind for Mutable<ImmutableSuperclass> {}

/// Mutable, mutable methods use `&self`. Not Send + Sync. `Id::retain` is safe
pub struct InteriorMutable;
Expand Down Expand Up @@ -111,6 +111,28 @@ pub unsafe trait ClassType: Message {
/// [`runtime::Object`]: crate::runtime::Object
type Super: Message;

/// TODO
///
/// Indicates whether the type is mutable or immutable.
///
/// This can be [`Owned`] if and only if `copy` creates a new instance,
/// see the following example:
///
/// ```ignore
/// let x: Id<MyObject, _> = MyObject::new();
/// // This is valid only if `y` is a new instance. Otherwise `x` and `y`
/// // would be able to create aliasing mutable references!
/// let y: Id<MyObject, Owned> = x.copy();
/// ```
///
/// Note that for the same reason, you should be careful when defining
/// `new` methods on your object; e.g. immutable types like [`NSString`]
/// don't return `Id<NSString, Owned>`, because that would allow this
/// trait to create an aliasing `Id<NSString, Shared>` (since sending the
/// `copy` message (and others) does not create a new instance, but
/// instead just retains the instance).
///
/// [`NSString`]: crate::Foundation::NSString
type Kind: ClassKind;

/// The name of the Objective-C class that this type represents.
Expand Down Expand Up @@ -148,13 +170,30 @@ pub unsafe trait ClassType: Message {
// - It is always safe to (attempt to) allocate an object.
// - The object is of the correct type, since we've used the class
// from `Self::class`.
//
// Wrt. main thread safety: It is usually not safe to call `dealloc`
// (and by extension, `release`) on e.g. `NSWindow` or `NSView` - but
// if the object has only been allocated, and not actually
// initialized, its instance variables are basically all `nil`, and
// there's not really anything for the `dealloc` method to do, so we
// can roughly assume that it is safe on the other thread.
//
// At least, Xcode's Main Thread Checker doesn't report `alloc` and
// `dealloc` as unsafe from other threads.
//
// Note: These assumptions are a bit sketchy and probably not entirely
// correct! We should consider adding something like the following if
// we can get `MainThreadMarker` to live inside `objc2`:
// ```
// fn alloc_on_main(mtm: MainThreadMarker) -> ...;
// ```
unsafe { msg_send_id![Self::class(), alloc] }
}

fn retain(&self) -> Id<Self>
where
Self::Kind: Retainable,
Self: 'static,
Self: Sized + 'static,
{
let ptr: *const Self = self;
let ptr: *mut Self = ptr as _;
Expand Down
4 changes: 2 additions & 2 deletions crates/objc2/src/declare/declare_class_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ fn test_subclass_duplicate_ivar() {

unsafe impl ClassType for Cls {
type Super = NSObject;
type Kind = Mutable;
type Kind = Mutable<Self>;
const NAME: &'static str = "DeclareClassDuplicateIvarSuperclass";
}
);
Expand All @@ -414,7 +414,7 @@ fn test_subclass_duplicate_ivar() {

unsafe impl ClassType for SubCls {
type Super = Cls;
type Kind = Mutable;
type Kind = Mutable<Self>;
const NAME: &'static str = "DeclareClassDuplicateIvarSubclass";
}
);
Expand Down
2 changes: 1 addition & 1 deletion crates/objc2/src/declare/ivar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ mod tests {

unsafe impl ClassType for CustomDrop {
type Super = NSObject;
type Kind = Mutable;
type Kind = Mutable<Self>;
const NAME: &'static str = "CustomDrop";
}
);
Expand Down
4 changes: 2 additions & 2 deletions crates/objc2/src/declare/ivar_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ mod tests {

unsafe impl ClassType for IvarTester {
type Super = NSObject;
type Kind = Mutable;
type Kind = Mutable<Self>;
const NAME: &'static str = "IvarTester";
}

Expand Down Expand Up @@ -284,7 +284,7 @@ mod tests {

unsafe impl ClassType for IvarTesterSubclass {
type Super = IvarTester;
type Kind = Mutable;
type Kind = Mutable<Self>;
const NAME: &'static str = "IvarTesterSubclass";
}

Expand Down
2 changes: 1 addition & 1 deletion crates/objc2/src/macros/declare_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
///
/// unsafe impl ClassType for MyCustomObject {
/// type Super = NSObject;
/// type Kind = Mutable;
/// type Kind = Mutable<Self>;
/// const NAME: &'static str = "MyCustomObject";
/// }
///
Expand Down
18 changes: 16 additions & 2 deletions crates/objc2/src/macros/extern_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ macro_rules! __extern_protocol_rewrite_methods {
// Unsafe variant
{
$(#[$($m:tt)*])*
$v:vis unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)?;
$v:vis unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)?
// TODO: Handle where bounds better
$(where $($where:ty : $bound:path),+ $(,)?)?;

$($rest:tt)*
} => {
Expand All @@ -213,6 +215,7 @@ macro_rules! __extern_protocol_rewrite_methods {

($crate::__extern_protocol_method_out)
($v unsafe fn $name($($args)*) $(-> $ret)?)
($($($where : $bound ,)+)?)
}

$crate::__extern_protocol_rewrite_methods! {
Expand All @@ -223,7 +226,9 @@ macro_rules! __extern_protocol_rewrite_methods {
// Safe variant
{
$(#[$($m:tt)*])*
$v:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?;
$v:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
// TODO: Handle where bounds better
$(where $($where:ty : $bound:path),+ $(,)?)?;

$($rest:tt)*
} => {
Expand All @@ -236,6 +241,7 @@ macro_rules! __extern_protocol_rewrite_methods {

($crate::__extern_protocol_method_out)
($v fn $name($($args)*) $(-> $ret)?)
($($($where : $bound ,)+)?)
}

$crate::__extern_protocol_rewrite_methods! {
Expand All @@ -250,6 +256,7 @@ macro_rules! __extern_protocol_method_out {
// Instance #[method(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)

(add_method)
($receiver:expr)
Expand All @@ -265,6 +272,7 @@ macro_rules! __extern_protocol_method_out {
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::Message
$(, $where : $bound)*
{
#[allow(unused_unsafe)]
unsafe {
Expand All @@ -283,6 +291,7 @@ macro_rules! __extern_protocol_method_out {
// Instance #[method_id(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)

(add_method)
($receiver:expr)
Expand All @@ -298,6 +307,7 @@ macro_rules! __extern_protocol_method_out {
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::Message
$(, $where : $bound)*
{
#[allow(unused_unsafe)]
unsafe {
Expand All @@ -317,6 +327,7 @@ macro_rules! __extern_protocol_method_out {
// Class #[method(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)

(add_class_method)
($receiver:expr)
Expand All @@ -332,6 +343,7 @@ macro_rules! __extern_protocol_method_out {
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::ClassType
$(, $where : $bound)*
{
#[allow(unused_unsafe)]
unsafe {
Expand All @@ -350,6 +362,7 @@ macro_rules! __extern_protocol_method_out {
// Class #[method_id(...)]
{
($($function_start:tt)*)
($($where:ty : $bound:path ,)*)

(add_class_method)
($receiver:expr)
Expand All @@ -365,6 +378,7 @@ macro_rules! __extern_protocol_method_out {
$($function_start)*
where
Self: $crate::__macro_helpers::Sized + $crate::ClassType
$(, $where : $bound)*
{
#[allow(unused_unsafe)]
unsafe {
Expand Down
2 changes: 1 addition & 1 deletion crates/objc2/tests/declare_class_self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ declare_class!(

unsafe impl ClassType for MyTestObject {
type Super = NSObject;
type Kind = Mutable;
type Kind = Mutable<Self>;

const NAME: &'static str = "MyTestObject";
}
Expand Down
4 changes: 3 additions & 1 deletion crates/objc2/tests/use_macros.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use objc2::class::{ClassType, Immutable};
use objc2::runtime::{Class, NSObject, Object};
use objc2::{class, declare_class, msg_send, sel, ClassType};
use objc2::{class, declare_class, msg_send, sel};

declare_class!(
struct MyObject;

unsafe impl ClassType for MyObject {
type Super = NSObject;
type Kind = Immutable;
const NAME: &'static str = "MyObject";
}
);
Expand Down

0 comments on commit 974e14a

Please sign in to comment.