From d5caed7a0bcb17dac8b5e7815c15aaf3e4e869f2 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 10 Sep 2023 17:27:28 +0200 Subject: [PATCH 1/5] Refactor CopyHelper and NS[Mutable]Copying setup Add CounterpartOrSelf instead of CopyHelper, which lives in objc2 and is much nicer to use. This also allows us to move NSCopying and NSMutableCopying back to reside in Foundation (note: We might have to at some point implement a small hack for these traits acting as if they contain the `copy` method, while it's actually `NSObject` that does). --- Cargo.lock | 1 + crates/icrate/CHANGELOG.md | 3 + .../src/additions/Foundation/dictionary.rs | 10 +- crates/icrate/src/additions/Foundation/mod.rs | 4 - crates/icrate/src/fixes/Foundation/copying.rs | 89 ++++++++++ crates/icrate/src/fixes/Foundation/mod.rs | 2 + crates/icrate/tests/mutable_string.rs | 41 ++++- crates/objc2/CHANGELOG.md | 1 + crates/objc2/src/declare/mod.rs | 157 +---------------- crates/objc2/src/macros/declare_class.rs | 20 ++- crates/objc2/src/macros/extern_class.rs | 10 +- crates/objc2/src/mutability.rs | 88 ++++++++++ crates/objc2/src/runtime/mod.rs | 9 +- crates/objc2/src/runtime/nscopying.rs | 159 ------------------ .../crates/test_declare_class/Cargo.toml | 19 ++- .../expected/apple-aarch64.s | 118 ++++++------- .../test_declare_class/expected/apple-armv7.s | 118 ++++++------- .../expected/apple-armv7s.s | 118 ++++++------- .../expected/apple-old-x86.s | 104 ++++++------ .../test_declare_class/expected/apple-x86.s | 104 ++++++------ .../expected/apple-x86_64.s | 104 ++++++------ .../crates/test_declare_class/lib.rs | 5 +- crates/tests/Cargo.toml | 1 + crates/tests/src/lib.rs | 2 + .../tests/src/test_declare_class_protocol.rs | 159 ++++++++++++++++++ 25 files changed, 761 insertions(+), 685 deletions(-) create mode 100644 crates/icrate/src/fixes/Foundation/copying.rs delete mode 100644 crates/objc2/src/runtime/nscopying.rs create mode 100644 crates/tests/src/test_declare_class_protocol.rs diff --git a/Cargo.lock b/Cargo.lock index 0a11d7763..112412047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,6 +500,7 @@ dependencies = [ name = "test_declare_class" version = "0.1.0" dependencies = [ + "icrate", "objc2", ] diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index 58cd68d27..524e2b037 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `Send` and `Sync` implementations for a bunch more types (same as the ones Swift marks as `@Sendable`). * Made some common methods in `AppKit` safe. +* Added missing `NSCopying` and `NSMutableCopying` zone methods. ### Changed * Moved the `ns_string!` macro to `icrate::Foundation::ns_string`. The old @@ -45,6 +46,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed * **BREAKING**: Removed the `MainThreadMarker` argument from the closure passed to `MainThreadBound::get_on_main`. +* **BREAKING**: Removed `Foundation::CopyHelper` since it is superseded by + `objc2::mutability::CounterpartOrSelf`. ## icrate 0.0.4 - 2023-07-31 diff --git a/crates/icrate/src/additions/Foundation/dictionary.rs b/crates/icrate/src/additions/Foundation/dictionary.rs index c0526f43d..1d752b3da 100644 --- a/crates/icrate/src/additions/Foundation/dictionary.rs +++ b/crates/icrate/src/additions/Foundation/dictionary.rs @@ -8,20 +8,19 @@ use core::ops::{Index, IndexMut}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr::{self, NonNull}; -use objc2::mutability::IsMutable; +use objc2::mutability::{CounterpartOrSelf, IsMutable}; use super::iter; use super::util; use crate::common::*; #[cfg(feature = "Foundation_NSMutableDictionary")] use crate::Foundation::NSMutableDictionary; -use crate::Foundation::{self, Copyhelper, NSCopying, NSDictionary}; +use crate::Foundation::{self, NSCopying, NSDictionary}; impl NSDictionary { pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id where - T: ClassType + NSCopying, - T::Mutability: Copyhelper, + T: ClassType + NSCopying + CounterpartOrSelf, { let count = min(keys.len(), vals.len()); @@ -37,8 +36,7 @@ impl NSDictionary { impl NSMutableDictionary { pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id where - T: ClassType + NSCopying, - T::Mutability: Copyhelper, + T: ClassType + NSCopying + CounterpartOrSelf, { let count = min(keys.len(), vals.len()); diff --git a/crates/icrate/src/additions/Foundation/mod.rs b/crates/icrate/src/additions/Foundation/mod.rs index 76c06be22..f87cf1e7c 100644 --- a/crates/icrate/src/additions/Foundation/mod.rs +++ b/crates/icrate/src/additions/Foundation/mod.rs @@ -49,7 +49,3 @@ pub use objc2::ffi::{NSInteger, NSUInteger}; #[cfg(feature = "Foundation_NSProxy")] pub use objc2::runtime::__NSProxy as NSProxy; pub use objc2::runtime::{NSObject, NSObjectProtocol, NSZone}; -#[doc(inline)] -pub use objc2::runtime::{ - __Copyhelper as Copyhelper, __NSCopying as NSCopying, __NSMutableCopying as NSMutableCopying, -}; diff --git a/crates/icrate/src/fixes/Foundation/copying.rs b/crates/icrate/src/fixes/Foundation/copying.rs new file mode 100644 index 000000000..7cf70cfa0 --- /dev/null +++ b/crates/icrate/src/fixes/Foundation/copying.rs @@ -0,0 +1,89 @@ +use objc2::mutability::CounterpartOrSelf; +use objc2::rc::Id; +use objc2::runtime::NSZone; +use objc2::{extern_protocol, ProtocolType}; + +extern_protocol!( + /// A protocol to provide functional copies of objects. + /// + /// This is similar to Rust's [`Clone`] trait, along with sharing a few + /// similarities to the [`std::borrow::ToOwned`] trait with regards to the + /// output type. + /// + /// See [Apple's documentation][apple-doc] for details. + /// + /// [apple-doc]: https://developer.apple.com/documentation/foundation/nscopying + pub unsafe trait NSCopying { + /// Returns a new instance that's a copy of the receiver. + /// + /// The output type is the immutable counterpart of the object, which is + /// usually `Self`, but e.g. `NSMutableString` returns `NSString`. + #[method_id(copy)] + #[optional] + fn copy(&self) -> Id + where + Self: Sized, + Self: CounterpartOrSelf; + + /// Returns a new instance that's a copy of the receiver. + /// + /// This is only used when implementing `NSCopying`, use + /// [`copy`][NSCopying::copy] instead. + /// + /// + /// # Safety + /// + /// The zone pointer must be valid or NULL. + #[method_id(copyWithZone:)] + unsafe fn copyWithZone(&self, zone: *mut NSZone) -> Id + where + Self: Sized, + Self: CounterpartOrSelf; + } + + unsafe impl ProtocolType for dyn NSCopying { + const NAME: &'static str = "NSCopying"; + } +); + +extern_protocol!( + /// A protocol to provide mutable copies of objects. + /// + /// Only classes that have an “immutable vs. mutable” distinction should adopt + /// this protocol. + /// + /// See [Apple's documentation][apple-doc] for details. + /// + /// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutablecopying + pub unsafe trait NSMutableCopying { + /// Returns a new instance that's a mutable copy of the receiver. + /// + /// The output type is the mutable counterpart of the object. E.g. both + /// `NSString` and `NSMutableString` return `NSMutableString`. + #[method_id(mutableCopy)] + #[optional] + fn mutableCopy(&self) -> Id + where + Self: Sized, + Self: CounterpartOrSelf; + + /// Returns a new instance that's a mutable copy of the receiver. + /// + /// This is only used when implementing `NSMutableCopying`, use + /// [`mutableCopy`][NSMutableCopying::mutableCopy] instead. + /// + /// + /// # Safety + /// + /// The zone pointer must be valid or NULL. + #[method_id(mutableCopyWithZone:)] + unsafe fn mutableCopyWithZone(&self, zone: *mut NSZone) -> Id + where + Self: Sized, + Self: CounterpartOrSelf; + } + + unsafe impl ProtocolType for dyn NSMutableCopying { + const NAME: &'static str = "NSMutableCopying"; + } +); diff --git a/crates/icrate/src/fixes/Foundation/mod.rs b/crates/icrate/src/fixes/Foundation/mod.rs index b6945a2ca..7849f379d 100644 --- a/crates/icrate/src/fixes/Foundation/mod.rs +++ b/crates/icrate/src/fixes/Foundation/mod.rs @@ -4,6 +4,7 @@ mod __NSDecimal; #[path = "NSNotFound.rs"] mod __NSNotFound; mod copy; +mod copying; mod debug; mod enumerator; mod exception; @@ -13,6 +14,7 @@ mod ns_consumed; pub use self::__NSDecimal::NSDecimal; pub use self::__NSNotFound::NSNotFound; +pub use self::copying::{NSCopying, NSMutableCopying}; pub use self::enumerator::NSFastEnumerationState; pub use self::generics::*; #[cfg(feature = "Foundation_NSMapTable")] diff --git a/crates/icrate/tests/mutable_string.rs b/crates/icrate/tests/mutable_string.rs index 4e00ba9e2..9ea2d52ae 100644 --- a/crates/icrate/tests/mutable_string.rs +++ b/crates/icrate/tests/mutable_string.rs @@ -1,7 +1,13 @@ #![cfg(feature = "Foundation_NSMutableString")] +use core::any::TypeId; +use std::ptr; + +use objc2::mutability::CounterpartOrSelf; use objc2::rc::Id; -use icrate::Foundation::{self, NSMutableString, NSObjectProtocol, NSString}; +use icrate::Foundation::{ + self, NSCopying, NSMutableCopying, NSMutableString, NSObjectProtocol, NSString, +}; #[test] fn display_debug() { @@ -53,3 +59,36 @@ fn test_copy() { assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3)); assert!(s3.is_kind_of::()); } + +#[test] +fn counterpart() { + assert_eq!( + TypeId::of::<::Immutable>(), + TypeId::of::(), + ); + assert_eq!( + TypeId::of::<::Mutable>(), + TypeId::of::(), + ); + + assert_eq!( + TypeId::of::<::Immutable>(), + TypeId::of::(), + ); + assert_eq!( + TypeId::of::<::Mutable>(), + TypeId::of::(), + ); +} + +#[test] +fn test_copy_with_zone() { + let s1 = NSString::from_str("abc"); + let s2 = unsafe { s1.copyWithZone(ptr::null_mut()) }; + assert_eq!(Id::as_ptr(&s1), Id::as_ptr(&s2)); + assert!(s2.is_kind_of::()); + + let s3 = unsafe { s1.mutableCopyWithZone(ptr::null_mut()) }; + assert_ne!(Id::as_ptr(&s1).cast::(), Id::as_ptr(&s3)); + assert!(s3.is_kind_of::()); +} diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index 085d76cd0..b2c01daca 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added * Added `mutability::IsMainThreadOnly`. +* Added `mutability::CounterpartOrSelf`. * Added new `encode` traits `EncodeReturn`, `EncodeArgument` and `EncodeArguments`. diff --git a/crates/objc2/src/declare/mod.rs b/crates/objc2/src/declare/mod.rs index 14bca38a4..6f1cde4c5 100644 --- a/crates/objc2/src/declare/mod.rs +++ b/crates/objc2/src/declare/mod.rs @@ -769,8 +769,8 @@ mod tests { use super::*; use crate::mutability::Immutable; use crate::rc::Id; - use crate::runtime::{NSObject, NSObjectProtocol, NSZone, __NSCopying as NSCopying}; - use crate::{declare_class, extern_methods, msg_send, test_utils, ClassType, ProtocolType}; + use crate::runtime::{NSObject, NSObjectProtocol}; + use crate::{declare_class, extern_methods, msg_send, test_utils, ClassType}; #[test] fn test_alignment() { @@ -962,159 +962,6 @@ mod tests { assert_eq!(result, 7); } - #[test] - #[should_panic = "could not create new class TestDeclareClassDuplicate. Perhaps a class with that name already exists?"] - fn test_declare_class_duplicate() { - declare_class!( - struct Custom1; - - unsafe impl ClassType for Custom1 { - type Super = NSObject; - type Mutability = Immutable; - const NAME: &'static str = "TestDeclareClassDuplicate"; - } - ); - - declare_class!( - struct Custom2; - - unsafe impl ClassType for Custom2 { - type Super = NSObject; - type Mutability = Immutable; - const NAME: &'static str = "TestDeclareClassDuplicate"; - } - ); - - let _cls = Custom1::class(); - // Should panic - let _cls = Custom2::class(); - } - - #[test] - fn test_declare_class_protocol() { - declare_class!( - struct Custom; - - unsafe impl ClassType for Custom { - type Super = NSObject; - type Mutability = Immutable; - const NAME: &'static str = "TestDeclareClassProtocolNotFound"; - } - - unsafe impl NSCopying for Custom { - #[method_id(copyWithZone:)] - fn copy_with_zone(&self, _zone: *const NSZone) -> Id { - unimplemented!() - } - } - ); - - let cls = Custom::class(); - assert!(cls.conforms_to(::protocol().unwrap())); - } - - #[test] - #[cfg_attr( - debug_assertions, - should_panic = "declared invalid method -[TestDeclareClassInvalidMethod description]: expected return to have type code '@', but found 'v'" - )] - fn test_declare_class_invalid_method() { - declare_class!( - struct Custom; - - unsafe impl ClassType for Custom { - type Super = NSObject; - type Mutability = Immutable; - const NAME: &'static str = "TestDeclareClassInvalidMethod"; - } - - unsafe impl Custom { - // Override `description` with a bad return type - #[method(description)] - fn description(&self) {} - } - ); - - let _cls = Custom::class(); - } - - #[test] - #[cfg_attr( - all(debug_assertions, feature = "verify"), - should_panic = "must implement required protocol method -[NSCopying copyWithZone:]" - )] - fn test_declare_class_missing_protocol_method() { - declare_class!( - struct Custom; - - unsafe impl ClassType for Custom { - type Super = NSObject; - type Mutability = Immutable; - const NAME: &'static str = "TestDeclareClassMissingProtocolMethod"; - } - - unsafe impl NSCopying for Custom { - // Missing required method - } - ); - - let _cls = Custom::class(); - } - - #[test] - // #[cfg_attr(all(debug_assertions, feature = "verify"), should_panic = "...")] - fn test_declare_class_invalid_protocol_method() { - declare_class!( - struct Custom; - - unsafe impl ClassType for Custom { - type Super = NSObject; - type Mutability = Immutable; - const NAME: &'static str = "TestDeclareClassInvalidProtocolMethod"; - } - - unsafe impl NSCopying for Custom { - // Override with a bad return type - #[method(copyWithZone:)] - fn copy_with_zone(&self, _zone: *const NSZone) -> u8 { - 42 - } - } - ); - - let _cls = Custom::class(); - } - - #[test] - #[cfg_attr( - all(debug_assertions, feature = "verify"), - should_panic = "failed overriding protocol method -[NSCopying someOtherMethod]: method not found" - )] - fn test_declare_class_extra_protocol_method() { - declare_class!( - struct Custom; - - unsafe impl ClassType for Custom { - type Super = NSObject; - type Mutability = Immutable; - const NAME: &'static str = "TestDeclareClassExtraProtocolMethod"; - } - - unsafe impl NSCopying for Custom { - #[method_id(copyWithZone:)] - fn copy_with_zone(&self, _zone: *const NSZone) -> Id { - unimplemented!() - } - - // This doesn't exist on the protocol - #[method(someOtherMethod)] - fn some_other_method(&self) {} - } - ); - - let _cls = Custom::class(); - } - // Proof-of-concept how we could make declare_class! accept generic types. #[test] fn test_generic() { diff --git a/crates/objc2/src/macros/declare_class.rs b/crates/objc2/src/macros/declare_class.rs index 9a05c3154..126dd94e6 100644 --- a/crates/objc2/src/macros/declare_class.rs +++ b/crates/objc2/src/macros/declare_class.rs @@ -186,8 +186,8 @@ /// ``` /// use std::os::raw::c_int; /// -/// # use objc2::runtime::{__NSCopying as NSCopying, NSObject, NSObjectProtocol, NSZone}; -/// # #[cfg(available_elsewhere)] +/// # use objc2::runtime::{NSObject, NSObjectProtocol, NSZone}; +/// # #[cfg(available_in_icrate)] /// use icrate::Foundation::{NSCopying, NSObject, NSObjectProtocol, NSZone}; /// use objc2::declare::{Ivar, IvarDrop, IvarEncode}; /// use objc2::rc::Id; @@ -250,11 +250,19 @@ /// fn __my_class_method() -> bool { /// true /// } +/// # +/// # #[method_id(copyWithZone:)] +/// # fn copyWithZone(&self, _zone: *const NSZone) -> Id { +/// # let mut obj = Self::new(*self.foo); +/// # *obj.bar = *self.bar; +/// # obj +/// # } /// } /// +/// # #[cfg(available_in_icrate)] /// unsafe impl NSCopying for MyCustomObject { /// #[method_id(copyWithZone:)] -/// fn copy_with_zone(&self, _zone: *const NSZone) -> Id { +/// fn copyWithZone(&self, _zone: *const NSZone) -> Id { /// let mut obj = Self::new(*self.foo); /// *obj.bar = *self.bar; /// obj @@ -293,9 +301,9 @@ /// assert_eq!(*obj.bar, 42); /// assert!(obj.object.is_kind_of::()); /// -/// let obj: Id = unsafe { -/// msg_send_id![&obj, copy] -/// }; // Or obj.copy() with `icrate` +/// # let obj: Id = unsafe { msg_send_id![&obj, copy] }; +/// # #[cfg(available_in_icrate)] +/// let obj = obj.copy(); /// /// assert_eq!(obj.get_foo(), 3); /// assert!(obj.get_object().is_kind_of::()); diff --git a/crates/objc2/src/macros/extern_class.rs b/crates/objc2/src/macros/extern_class.rs index 51f14023c..90a95856a 100644 --- a/crates/objc2/src/macros/extern_class.rs +++ b/crates/objc2/src/macros/extern_class.rs @@ -93,7 +93,7 @@ /// ``` /// # #[cfg(not_available)] /// use icrate::Foundation::{NSCoding, NSCopying, NSObjectProtocol}; -/// # use objc2::runtime::{NSObjectProtocol, __NSCopying as NSCopying}; +/// # use objc2::runtime::NSObjectProtocol; /// use objc2::rc::Id; /// use objc2::runtime::NSObject; /// use objc2::{extern_class, msg_send_id, mutability, ClassType}; @@ -117,6 +117,7 @@ /// // Note: We have to specify the protocols for the superclasses as well, /// // since Rust doesn't do inheritance. /// unsafe impl NSObjectProtocol for NSFormatter {} +/// # #[cfg(not_available)] /// unsafe impl NSCopying for NSFormatter {} /// # #[cfg(not_available)] /// unsafe impl NSCoding for NSFormatter {} @@ -136,7 +137,7 @@ /// ``` /// # #[cfg(not_available)] /// use icrate::Foundation::{NSCoding, NSCopying, NSObjectProtocol}; -/// # use objc2::runtime::{NSObjectProtocol, __NSCopying as NSCopying}; +/// # use objc2::runtime::NSObjectProtocol; /// use objc2::runtime::NSObject; /// use objc2::{extern_class, mutability, ClassType}; /// # @@ -464,8 +465,9 @@ macro_rules! __extern_class_impl_traits { // same object, and would violate aliasing rules. // // But `&mut NSMutableString` -> `&mut NSString` safe, since the - // `NSCopying` implementation of `NSMutableString` is used, and that - // is guaranteed to return a different object. + // `NSCopying` implementation of `NSMutableString` is still used on + // the `&mut NSString`, and that is guaranteed to return a different + // object. $(#[$impl_m])* impl<$($t)*> $crate::__macro_helpers::DerefMut for $for { #[inline] diff --git a/crates/objc2/src/mutability.rs b/crates/objc2/src/mutability.rs index b90f39eb4..0138a1e21 100644 --- a/crates/objc2/src/mutability.rs +++ b/crates/objc2/src/mutability.rs @@ -253,6 +253,49 @@ mod private { pub trait MutabilityIsMainThreadOnly: Mutability {} impl MutabilityIsMainThreadOnly for MainThreadOnly {} + pub trait MutabilityCounterpartOrSelf: Mutability { + type Immutable: ?Sized + ClassType; + type Mutable: ?Sized + ClassType; + } + impl> MutabilityCounterpartOrSelf for Root { + type Immutable = T; + type Mutable = T; + } + impl> MutabilityCounterpartOrSelf for Immutable { + type Immutable = T; + type Mutable = T; + } + impl> MutabilityCounterpartOrSelf for Mutable { + type Immutable = T; + type Mutable = T; + } + impl MutabilityCounterpartOrSelf for ImmutableWithMutableSubclass + where + T: ClassType>, + S: ClassType>, + { + type Immutable = T; + type Mutable = S; + } + impl MutabilityCounterpartOrSelf for MutableWithImmutableSuperclass + where + T: ClassType>, + S: ClassType>, + { + type Immutable = S; + type Mutable = T; + } + impl> MutabilityCounterpartOrSelf + for InteriorMutable + { + type Immutable = T; + type Mutable = T; + } + impl> MutabilityCounterpartOrSelf for MainThreadOnly { + type Immutable = T; + type Mutable = T; + } + // TODO: Trait for objects whose `hash` is guaranteed to never change, // which allows it to be used as a key in `NSDictionary`. } @@ -349,10 +392,43 @@ impl IsMainThreadOnly for T where { } +/// Retrieve the immutable/mutable counterpart class, and fall back to `Self` +/// if not applicable. +/// +/// This is mostly used for describing the return type of `NSCopying` and +/// `NSMutableCopying`, since due to Rust trait limitations, those two can't +/// have associated types themselves (at least not since we want to use them +/// in `ProtocolObject`). +pub trait CounterpartOrSelf: ClassType { + /// The immutable counterpart of the type, or `Self` if the type has no + /// immutable counterpart. + /// + /// The implementation for `NSString` has itself (`NSString`) here, while + /// `NSMutableString` instead has `NSString`. + type Immutable: ?Sized + ClassType; + + /// The mutable counterpart of the type, or `Self` if the type has no + /// mutable counterpart. + /// + /// The implementation for `NSString` has `NSMutableString` here, while + /// `NSMutableString` has itself (`NSMutableString`). + type Mutable: ?Sized + ClassType; +} +impl CounterpartOrSelf for T +where + T::Mutability: private::MutabilityCounterpartOrSelf, +{ + type Immutable = >::Immutable; + type Mutable = >::Mutable; +} + #[cfg(test)] mod tests { + use crate::runtime::NSObject; + use super::*; + use core::any::TypeId; use core::fmt; use core::hash; @@ -379,4 +455,16 @@ mod tests { assert_sized::(); } } + + #[test] + fn counterpart_root() { + assert_eq!( + TypeId::of::(), + TypeId::of::<::Immutable>(), + ); + assert_eq!( + TypeId::of::(), + TypeId::of::<::Mutable>(), + ); + } } diff --git a/crates/objc2/src/runtime/mod.rs b/crates/objc2/src/runtime/mod.rs index a3bc0c2fd..f7b8de1df 100644 --- a/crates/objc2/src/runtime/mod.rs +++ b/crates/objc2/src/runtime/mod.rs @@ -33,7 +33,6 @@ use std::os::raw::c_uint; pub mod __nsstring; mod bool; mod method_encoding_iter; -mod nscopying; mod nsobject; mod nsproxy; mod nszone; @@ -46,12 +45,8 @@ use crate::encode::{Encode, EncodeArguments, EncodeReturn, Encoding, OptionEncod use crate::verify::{verify_method_signature, Inner}; use crate::{ffi, Message}; -// Note: While these are not public, they are still a breaking change to -// remove, since `icrate` relies on them. -#[doc(hidden)] -pub use self::nscopying::{ - Copyhelper as __Copyhelper, NSCopying as __NSCopying, NSMutableCopying as __NSMutableCopying, -}; +// Note: While this is not public, it is still a breaking change to remove, +// since `icrate` relies on it. #[doc(hidden)] pub use self::nsproxy::NSProxy as __NSProxy; diff --git a/crates/objc2/src/runtime/nscopying.rs b/crates/objc2/src/runtime/nscopying.rs deleted file mode 100644 index 0ff20bb9d..000000000 --- a/crates/objc2/src/runtime/nscopying.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::mutability::{ - Immutable, ImmutableWithMutableSubclass, InteriorMutable, MainThreadOnly, Mutability, Mutable, - MutableWithImmutableSuperclass, Root, -}; -use crate::rc::Id; -use crate::{msg_send_id, ClassType, ProtocolType}; - -/// Helper trait for [`NSCopying`] and [`NSMutableCopying`]. -/// -/// This is needed since those two can't have associated types themselves (at -/// least not if they want to be usable as `ProtocolObject`), -/// and because the return type of those differ if the class has a mutable or -/// an immutable counterpart (as is the case for `NSString` and -/// `NSMutableString`). -// -// Note: This trait is intentionally not object safe. -pub trait Copyhelper: Mutability { - /// The output type of [`NSCopying`] for the given `T`. - type CopyOutput: ?Sized + ClassType; - /// The output type of [`NSMutableCopying`] for the given `T`. - type MutableCopyOutput: ?Sized + ClassType; - - // TODO: Use this to autogenerate `ToOwned` impls - #[doc(hidden)] - fn __do_copy(t: &T) -> Id; -} -impl> Copyhelper for Root { - type CopyOutput = T; - type MutableCopyOutput = T; - - #[inline] - fn __do_copy(t: &T) -> Id { - t.copy() - } -} -impl> Copyhelper for Immutable { - type CopyOutput = T; - type MutableCopyOutput = T; - - #[inline] - fn __do_copy(t: &T) -> Id { - t.retain() - } -} -impl> Copyhelper for Mutable { - type CopyOutput = T; - type MutableCopyOutput = T; - - #[inline] - fn __do_copy(t: &T) -> Id { - t.copy() - } -} -impl Copyhelper for ImmutableWithMutableSubclass -where - T: NSCopying + ClassType>, - S: ClassType>, -{ - type CopyOutput = T; - type MutableCopyOutput = S; - - #[inline] - fn __do_copy(t: &T) -> Id { - t.copy() - } -} -impl Copyhelper for MutableWithImmutableSuperclass -where - T: NSMutableCopying + ClassType>, - S: ClassType>, -{ - type CopyOutput = S; - type MutableCopyOutput = T; - - #[inline] - fn __do_copy(t: &T) -> Id { - t.mutableCopy() - } -} -impl> Copyhelper for InteriorMutable { - type CopyOutput = T; - type MutableCopyOutput = T; - - #[inline] - fn __do_copy(t: &T) -> Id { - t.retain() - } -} -impl> Copyhelper for MainThreadOnly { - type CopyOutput = T; - type MutableCopyOutput = T; - - #[inline] - fn __do_copy(t: &T) -> Id { - t.retain() - } -} - -/// A protocol to provide functional copies of objects. -/// -/// This is similar to Rust's [`Clone`] trait, along with sharing a few -/// similarities to the [`std::borrow::ToOwned`] trait with regards to the -/// output type. -/// -/// See [Apple's documentation][apple-doc] for details. -/// -/// [apple-doc]: https://developer.apple.com/documentation/foundation/nscopying -#[allow(clippy::missing_safety_doc)] // Same as all other traits -pub unsafe trait NSCopying { - /// Returns a new instance that's a copy of the receiver. - /// - /// The output type is usually `Self`, but e.g. `NSMutableString` returns - /// `NSString`. - fn copy(&self) -> Id<>::CopyOutput> - where - Self: Sized + ClassType, - Self::Mutability: Copyhelper, - { - unsafe { msg_send_id![self, copy] } - } -} - -crate::__inner_extern_protocol!( - () - (NSCopying) - (dyn NSCopying) - ("NSCopying") -); - -/// A protocol to provide mutable copies of objects. -/// -/// Only classes that have an “immutable vs. mutable” distinction should adopt -/// this protocol. -/// -/// See [Apple's documentation][apple-doc] for details. -/// -/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutablecopying -#[allow(clippy::missing_safety_doc)] // Same as all other traits -pub unsafe trait NSMutableCopying { - /// Returns a new instance that's a mutable copy of the receiver. - /// - /// The output type is the mutable counterpart of the object. E.g. both - /// `NSString` and `NSMutableString` return `NSMutableString`. - #[allow(non_snake_case)] - fn mutableCopy(&self) -> Id<>::MutableCopyOutput> - where - Self: Sized + ClassType, - Self::Mutability: Copyhelper, - { - unsafe { msg_send_id![self, mutableCopy] } - } -} - -crate::__inner_extern_protocol!( - () - (NSMutableCopying) - (dyn NSMutableCopying) - ("NSMutableCopying") -); diff --git a/crates/test-assembly/crates/test_declare_class/Cargo.toml b/crates/test-assembly/crates/test_declare_class/Cargo.toml index bc46cbaf0..7e42d6c45 100644 --- a/crates/test-assembly/crates/test_declare_class/Cargo.toml +++ b/crates/test-assembly/crates/test_declare_class/Cargo.toml @@ -9,17 +9,20 @@ path = "lib.rs" [dependencies] objc2 = { path = "../../../objc2", default-features = false, optional = true } +icrate = { path = "../../../icrate", default-features = false, optional = true } [features] -default = ["apple", "std"] -std = ["objc2?/std"] +default = ["apple", "std", "Foundation"] +std = ["icrate?/std", "icrate?/std"] # Runtime -apple = ["objc2", "objc2?/apple"] -gnustep-1-7 = ["objc2?/gnustep-1-7"] -gnustep-1-8 = ["gnustep-1-7", "objc2?/gnustep-1-8"] -gnustep-1-9 = ["gnustep-1-8", "objc2?/gnustep-1-9"] -gnustep-2-0 = ["gnustep-1-9", "objc2?/gnustep-2-0"] -gnustep-2-1 = ["gnustep-2-0", "objc2?/gnustep-2-1"] +apple = ["objc2", "icrate", "icrate?/apple"] +gnustep-1-7 = ["icrate?/gnustep-1-7"] +gnustep-1-8 = ["gnustep-1-7", "icrate?/gnustep-1-8"] +gnustep-1-9 = ["gnustep-1-8", "icrate?/gnustep-1-9"] +gnustep-2-0 = ["gnustep-1-9", "icrate?/gnustep-2-0"] +gnustep-2-1 = ["gnustep-2-0", "icrate?/gnustep-2-1"] + +Foundation = ["icrate?/Foundation"] # Hack to prevent the feature flag from being enabled in the entire project assembly-features = ["objc2?/unstable-static-sel-inlined", "objc2?/unstable-static-class-inlined"] diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s b/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s index 59304b69b..3423c421c 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-aarch64.s @@ -94,9 +94,9 @@ Lloh26: mov x4, x19 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) Lloh27: - adrp x8, L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2@PAGE Lloh28: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc@PAGEOFF] + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2@PAGEOFF] Lloh29: adrp x5, _class_method@PAGE Lloh30: @@ -107,9 +107,9 @@ Lloh30: mov x4, x21 bl SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) Lloh31: - adrp x8, L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc@PAGE Lloh32: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5@PAGEOFF] + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc@PAGEOFF] Lloh33: adrp x5, _method@PAGE Lloh34: @@ -120,9 +120,9 @@ Lloh34: mov x4, x21 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) Lloh35: - adrp x8, L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5@PAGE Lloh36: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6@PAGEOFF] + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5@PAGEOFF] Lloh37: adrp x21, l_anon.[ID].4@PAGE Lloh38: @@ -137,9 +137,9 @@ Lloh40: mov x4, x21 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) Lloh41: - adrp x8, L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce@PAGE Lloh42: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498@PAGEOFF] + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce@PAGEOFF] Lloh43: adrp x5, _method_id@PAGE Lloh44: @@ -150,9 +150,9 @@ Lloh44: mov x4, x19 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) Lloh45: - adrp x8, L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f@PAGE Lloh46: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8@PAGEOFF] + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f@PAGEOFF] Lloh47: adrp x5, _method_id_with_param@PAGE Lloh48: @@ -172,17 +172,17 @@ Lloh50: mov x0, sp bl SYM(objc2::__macro_helpers::::__add_protocol_methods::GENERATED_ID, 0) Lloh51: - adrp x8, L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9@PAGE + adrp x8, L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166@PAGE Lloh52: - ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9@PAGEOFF] + ldr x1, [x8, L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166@PAGEOFF] Lloh53: adrp x2, l_anon.[ID].7@PAGE Lloh54: add x2, x2, l_anon.[ID].7@PAGEOFF Lloh55: - adrp x5, _copy_with_zone@PAGE + adrp x5, _copyWithZone@PAGE Lloh56: - add x5, x5, _copy_with_zone@PAGEOFF + add x5, x5, _copyWithZone@PAGEOFF mov w3, #1 mov x4, x19 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) @@ -685,9 +685,9 @@ LBB14_5: .loh AdrpAdd Lloh139, Lloh140 .loh AdrpAdd Lloh137, Lloh138 - .globl _copy_with_zone + .globl _copyWithZone .p2align 2 -_copy_with_zone: +_copyWithZone: stp x24, x23, [sp, #-64]! stp x22, x21, [sp, #16] stp x20, x19, [sp, #32] @@ -849,7 +849,7 @@ l_anon.[ID].14: .p2align 3, 0x0 l_anon.[ID].15: .quad l_anon.[ID].14 - .asciz "5\000\000\000\000\000\000\000\013\000\000\000\001\000\000" + .asciz "5\000\000\000\000\000\000\000\f\000\000\000\001\000\000" .section __TEXT,__const l_anon.[ID].16: @@ -879,105 +879,105 @@ l_anon.[ID].21: .asciz "\017\000\000\000\000\000\000" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc + .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc: +L_OBJC_IMAGE_INFO_d874ee9262978be2: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc -L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc: + .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 +L_OBJC_METH_VAR_NAME_d874ee9262978be2: .asciz "classMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc + .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc: - .quad L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc +L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: + .quad L_OBJC_METH_VAR_NAME_d874ee9262978be2 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_450db9db0953dff5 + .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_450db9db0953dff5: +L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_450db9db0953dff5 -L_OBJC_METH_VAR_NAME_450db9db0953dff5: + .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc +L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: .asciz "method" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5 + .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5: - .quad L_OBJC_METH_VAR_NAME_450db9db0953dff5 +L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: + .quad L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_783b35bc45c6e4a6 + .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_783b35bc45c6e4a6: +L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 -L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6: + .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 +L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: .asciz "methodBool:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6 + .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6: - .quad L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 +L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: + .quad L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_828e9fbc6d0b4498 + .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_828e9fbc6d0b4498: +L_OBJC_IMAGE_INFO_f7f521670860b0ce: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 -L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498: + .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce +L_OBJC_METH_VAR_NAME_f7f521670860b0ce: .asciz "methodId" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498 + .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498: - .quad L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 +L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: + .quad L_OBJC_METH_VAR_NAME_f7f521670860b0ce .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_788cc14ba6a28eb8 + .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_788cc14ba6a28eb8: +L_OBJC_IMAGE_INFO_6addfcf634c6232f: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 -L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8: + .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f +L_OBJC_METH_VAR_NAME_6addfcf634c6232f: .asciz "methodIdWithParam:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8 + .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8: - .quad L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 +L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: + .quad L_OBJC_METH_VAR_NAME_6addfcf634c6232f .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f058a81939de2cb9 + .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f058a81939de2cb9: +L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f058a81939de2cb9 -L_OBJC_METH_VAR_NAME_f058a81939de2cb9: + .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 +L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: .asciz "copyWithZone:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9 + .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9: - .quad L_OBJC_METH_VAR_NAME_f058a81939de2cb9 +L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: + .quad L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-armv7.s b/crates/test-assembly/crates/test_declare_class/expected/apple-armv7.s index c380efdab..5d17ff828 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-armv7.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-armv7.s @@ -97,9 +97,9 @@ LPC1_11: add r9, pc, r9 strd r8, r9, [sp] bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc-(LPC1_12+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-(LPC1_12+8)) mov r0, r4 - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc-(LPC1_12+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-(LPC1_12+8)) mov r2, r5 LPC1_12: ldr r1, [pc, r1] @@ -110,9 +110,9 @@ LPC1_13: add r11, pc, r11 strd r10, r11, [sp] bl SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5-(LPC1_14+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-(LPC1_14+8)) mov r0, r4 - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5-(LPC1_14+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-(LPC1_14+8)) mov r2, r5 LPC1_14: ldr r1, [pc, r1] @@ -123,9 +123,9 @@ LPC1_15: add r11, pc, r11 strd r10, r11, [sp] bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6-(LPC1_16+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-(LPC1_16+8)) mov r0, r4 - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6-(LPC1_16+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-(LPC1_16+8)) LPC1_16: ldr r1, [pc, r1] movw r3, :lower16:(_method_bool-(LPC1_17+8)) @@ -141,9 +141,9 @@ LPC1_18: mov r3, #1 mov r2, r10 bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498-(LPC1_19+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-(LPC1_19+8)) mov r0, r4 - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498-(LPC1_19+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-(LPC1_19+8)) mov r2, r5 LPC1_19: ldr r1, [pc, r1] @@ -154,9 +154,9 @@ LPC1_20: add r9, pc, r9 strd r8, r9, [sp] bl SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8-(LPC1_21+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-(LPC1_21+8)) mov r0, r4 - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8-(LPC1_21+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-(LPC1_21+8)) mov r2, r10 LPC1_21: ldr r1, [pc, r1] @@ -179,14 +179,14 @@ LPC1_23: movw r2, :lower16:(l_anon.[ID].7-(LPC1_24+8)) mov r3, #1 movt r2, :upper16:(l_anon.[ID].7-(LPC1_24+8)) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9-(LPC1_25+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9-(LPC1_25+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-(LPC1_25+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-(LPC1_25+8)) LPC1_24: add r2, pc, r2 LPC1_25: ldr r1, [pc, r1] - movw r9, :lower16:(_copy_with_zone-(LPC1_26+8)) - movt r9, :upper16:(_copy_with_zone-(LPC1_26+8)) + movw r9, :lower16:(_copyWithZone-(LPC1_26+8)) + movt r9, :upper16:(_copyWithZone-(LPC1_26+8)) LPC1_26: add r9, pc, r9 strd r8, r9, [sp] @@ -621,10 +621,10 @@ LBB14_5: pop {r4, r5, r6, r7, lr} b _objc_autoreleaseReturnValue - .globl _copy_with_zone + .globl _copyWithZone .p2align 2 .code 32 -_copy_with_zone: +_copyWithZone: push {r4, r5, r6, r7, lr} add r7, sp, #12 push {r8, r10} @@ -777,7 +777,7 @@ l_anon.[ID].14: .p2align 2, 0x0 l_anon.[ID].15: .long l_anon.[ID].14 - .asciz "5\000\000\000\013\000\000\000\001\000\000" + .asciz "5\000\000\000\f\000\000\000\001\000\000" .section __TEXT,__const l_anon.[ID].16: @@ -807,106 +807,106 @@ l_anon.[ID].21: .asciz "\017\000\000" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc + .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc: +L_OBJC_IMAGE_INFO_d874ee9262978be2: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc -L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc: + .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 +L_OBJC_METH_VAR_NAME_d874ee9262978be2: .asciz "classMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc + .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc: - .long L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc +L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: + .long L_OBJC_METH_VAR_NAME_d874ee9262978be2 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_450db9db0953dff5 + .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_450db9db0953dff5: +L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_450db9db0953dff5 -L_OBJC_METH_VAR_NAME_450db9db0953dff5: + .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc +L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: .asciz "method" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5 + .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5: - .long L_OBJC_METH_VAR_NAME_450db9db0953dff5 +L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: + .long L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_783b35bc45c6e4a6 + .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_783b35bc45c6e4a6: +L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 -L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6: + .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 +L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: .asciz "methodBool:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6 + .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6: - .long L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 +L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: + .long L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_828e9fbc6d0b4498 + .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_828e9fbc6d0b4498: +L_OBJC_IMAGE_INFO_f7f521670860b0ce: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 -L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498: + .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce +L_OBJC_METH_VAR_NAME_f7f521670860b0ce: .asciz "methodId" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498 + .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498: - .long L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 +L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: + .long L_OBJC_METH_VAR_NAME_f7f521670860b0ce .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_788cc14ba6a28eb8 + .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_788cc14ba6a28eb8: +L_OBJC_IMAGE_INFO_6addfcf634c6232f: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 -L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8: + .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f +L_OBJC_METH_VAR_NAME_6addfcf634c6232f: .asciz "methodIdWithParam:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8 + .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8: - .long L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 +L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: + .long L_OBJC_METH_VAR_NAME_6addfcf634c6232f .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f058a81939de2cb9 + .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f058a81939de2cb9: +L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f058a81939de2cb9 -L_OBJC_METH_VAR_NAME_f058a81939de2cb9: + .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 +L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: .asciz "copyWithZone:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9 + .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9: - .long L_OBJC_METH_VAR_NAME_f058a81939de2cb9 +L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: + .long L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 .section __DATA,__nl_symbol_ptr,non_lazy_symbol_pointers .p2align 2, 0x0 diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-armv7s.s b/crates/test-assembly/crates/test_declare_class/expected/apple-armv7s.s index da99dfe97..3fbd8f431 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-armv7s.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-armv7s.s @@ -100,8 +100,8 @@ LPC1_11: movw r11, :lower16:(_class_method-(LPC1_12+8)) mov r0, r4 movt r11, :upper16:(_class_method-(LPC1_12+8)) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc-(LPC1_13+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc-(LPC1_13+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-(LPC1_13+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-(LPC1_13+8)) LPC1_12: add r11, pc, r11 LPC1_13: @@ -113,8 +113,8 @@ LPC1_13: movw r11, :lower16:(_method-(LPC1_14+8)) mov r0, r4 movt r11, :upper16:(_method-(LPC1_14+8)) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5-(LPC1_15+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5-(LPC1_15+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-(LPC1_15+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-(LPC1_15+8)) LPC1_14: add r11, pc, r11 LPC1_15: @@ -126,8 +126,8 @@ LPC1_15: movw r3, :lower16:(_method_bool-(LPC1_16+8)) mov r0, r4 movt r3, :upper16:(_method_bool-(LPC1_16+8)) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6-(LPC1_17+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6-(LPC1_17+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-(LPC1_17+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-(LPC1_17+8)) LPC1_16: add r3, pc, r3 LPC1_17: @@ -144,8 +144,8 @@ LPC1_18: movw r9, :lower16:(_method_id-(LPC1_19+8)) mov r0, r4 movt r9, :upper16:(_method_id-(LPC1_19+8)) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498-(LPC1_20+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498-(LPC1_20+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-(LPC1_20+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-(LPC1_20+8)) LPC1_19: add r9, pc, r9 LPC1_20: @@ -157,8 +157,8 @@ LPC1_20: movw r9, :lower16:(_method_id_with_param-(LPC1_21+8)) mov r0, r4 movt r9, :upper16:(_method_id_with_param-(LPC1_21+8)) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8-(LPC1_22+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8-(LPC1_22+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-(LPC1_22+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-(LPC1_22+8)) LPC1_21: add r9, pc, r9 LPC1_22: @@ -176,11 +176,11 @@ LPC1_23: mov r1, r0 mov r0, r4 bl SYM(objc2::__macro_helpers::::__add_protocol_methods::GENERATED_ID, 0) - movw r9, :lower16:(_copy_with_zone-(LPC1_24+8)) + movw r9, :lower16:(_copyWithZone-(LPC1_24+8)) mov r3, #1 - movt r9, :upper16:(_copy_with_zone-(LPC1_24+8)) - movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9-(LPC1_25+8)) - movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9-(LPC1_25+8)) + movt r9, :upper16:(_copyWithZone-(LPC1_24+8)) + movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-(LPC1_25+8)) + movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-(LPC1_25+8)) LPC1_24: add r9, pc, r9 LPC1_25: @@ -624,10 +624,10 @@ LBB14_5: bl _objc_autoreleaseReturnValue pop {r4, r5, r6, r7, pc} - .globl _copy_with_zone + .globl _copyWithZone .p2align 2 .code 32 -_copy_with_zone: +_copyWithZone: push {r4, r5, r6, r7, lr} add r7, sp, #12 push {r8, r10} @@ -780,7 +780,7 @@ l_anon.[ID].14: .p2align 2, 0x0 l_anon.[ID].15: .long l_anon.[ID].14 - .asciz "5\000\000\000\013\000\000\000\001\000\000" + .asciz "5\000\000\000\f\000\000\000\001\000\000" .section __TEXT,__const l_anon.[ID].16: @@ -810,106 +810,106 @@ l_anon.[ID].21: .asciz "\017\000\000" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc + .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc: +L_OBJC_IMAGE_INFO_d874ee9262978be2: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc -L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc: + .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 +L_OBJC_METH_VAR_NAME_d874ee9262978be2: .asciz "classMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc + .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc: - .long L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc +L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: + .long L_OBJC_METH_VAR_NAME_d874ee9262978be2 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_450db9db0953dff5 + .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_450db9db0953dff5: +L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_450db9db0953dff5 -L_OBJC_METH_VAR_NAME_450db9db0953dff5: + .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc +L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: .asciz "method" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5 + .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5: - .long L_OBJC_METH_VAR_NAME_450db9db0953dff5 +L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: + .long L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_783b35bc45c6e4a6 + .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_783b35bc45c6e4a6: +L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 -L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6: + .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 +L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: .asciz "methodBool:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6 + .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6: - .long L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 +L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: + .long L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_828e9fbc6d0b4498 + .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_828e9fbc6d0b4498: +L_OBJC_IMAGE_INFO_f7f521670860b0ce: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 -L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498: + .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce +L_OBJC_METH_VAR_NAME_f7f521670860b0ce: .asciz "methodId" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498 + .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498: - .long L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 +L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: + .long L_OBJC_METH_VAR_NAME_f7f521670860b0ce .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_788cc14ba6a28eb8 + .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_788cc14ba6a28eb8: +L_OBJC_IMAGE_INFO_6addfcf634c6232f: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 -L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8: + .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f +L_OBJC_METH_VAR_NAME_6addfcf634c6232f: .asciz "methodIdWithParam:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8 + .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8: - .long L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 +L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: + .long L_OBJC_METH_VAR_NAME_6addfcf634c6232f .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f058a81939de2cb9 + .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f058a81939de2cb9: +L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f058a81939de2cb9 -L_OBJC_METH_VAR_NAME_f058a81939de2cb9: + .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 +L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: .asciz "copyWithZone:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9 + .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9: - .long L_OBJC_METH_VAR_NAME_f058a81939de2cb9 +L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: + .long L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 .section __DATA,__nl_symbol_ptr,non_lazy_symbol_pointers .p2align 2, 0x0 diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-old-x86.s b/crates/test-assembly/crates/test_declare_class/expected/apple-old-x86.s index 8226e0df6..ee414d41d 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-old-x86.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-old-x86.s @@ -88,7 +88,7 @@ L1$pb: push eax push 0 push edi - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) add esp, 24 @@ -98,7 +98,7 @@ L1$pb: push eax push 0 push edi - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 24 @@ -108,7 +108,7 @@ L1$pb: push ecx push 1 push ecx - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 24 @@ -119,7 +119,7 @@ L1$pb: push 0 lea eax, [esi + l_anon.[ID].1-L1$pb] push eax - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 24 @@ -129,7 +129,7 @@ L1$pb: push 1 lea eax, [esi + l_anon.[ID].4-L1$pb] push eax - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 24 @@ -142,13 +142,13 @@ L1$pb: push ebx call SYM(objc2::__macro_helpers::::__add_protocol_methods::GENERATED_ID, 0) add esp, 8 - lea ecx, [esi + _copy_with_zone-L1$pb] + lea ecx, [esi + _copyWithZone-L1$pb] lea edx, [esi + l_anon.[ID].7-L1$pb] push ecx push edi push 1 push edx - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-L1$pb] push eax call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 20 @@ -629,9 +629,9 @@ LBB14_5: pop ebp ret - .globl _copy_with_zone + .globl _copyWithZone .p2align 4, 0x90 -_copy_with_zone: +_copyWithZone: push ebp mov ebp, esp push ebx @@ -802,7 +802,7 @@ l_anon.[ID].14: .p2align 2, 0x0 l_anon.[ID].15: .long l_anon.[ID].14 - .asciz "5\000\000\000\013\000\000\000\001\000\000" + .asciz "5\000\000\000\f\000\000\000\001\000\000" .section __TEXT,__const l_anon.[ID].16: @@ -832,106 +832,106 @@ l_anon.[ID].21: .asciz "\017\000\000" .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc + .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc: +L_OBJC_IMAGE_INFO_d874ee9262978be2: .asciz "\000\000\000\000@\000\000" .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc -L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc: + .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 +L_OBJC_METH_VAR_NAME_d874ee9262978be2: .asciz "classMethod" .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc + .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc: - .long L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc +L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: + .long L_OBJC_METH_VAR_NAME_d874ee9262978be2 .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_450db9db0953dff5 + .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_450db9db0953dff5: +L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: .asciz "\000\000\000\000@\000\000" .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_450db9db0953dff5 -L_OBJC_METH_VAR_NAME_450db9db0953dff5: + .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc +L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: .asciz "method" .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5 + .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5: - .long L_OBJC_METH_VAR_NAME_450db9db0953dff5 +L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: + .long L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_783b35bc45c6e4a6 + .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_783b35bc45c6e4a6: +L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: .asciz "\000\000\000\000@\000\000" .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 -L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6: + .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 +L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: .asciz "methodBool:" .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6 + .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6: - .long L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 +L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: + .long L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_828e9fbc6d0b4498 + .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_828e9fbc6d0b4498: +L_OBJC_IMAGE_INFO_f7f521670860b0ce: .asciz "\000\000\000\000@\000\000" .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 -L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498: + .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce +L_OBJC_METH_VAR_NAME_f7f521670860b0ce: .asciz "methodId" .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498 + .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498: - .long L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 +L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: + .long L_OBJC_METH_VAR_NAME_f7f521670860b0ce .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_788cc14ba6a28eb8 + .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_788cc14ba6a28eb8: +L_OBJC_IMAGE_INFO_6addfcf634c6232f: .asciz "\000\000\000\000@\000\000" .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 -L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8: + .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f +L_OBJC_METH_VAR_NAME_6addfcf634c6232f: .asciz "methodIdWithParam:" .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8 + .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8: - .long L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 +L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: + .long L_OBJC_METH_VAR_NAME_6addfcf634c6232f .section __OBJC,__image_info - .globl L_OBJC_IMAGE_INFO_f058a81939de2cb9 + .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f058a81939de2cb9: +L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: .asciz "\000\000\000\000@\000\000" .section __TEXT,__cstring,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f058a81939de2cb9 -L_OBJC_METH_VAR_NAME_f058a81939de2cb9: + .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 +L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: .asciz "copyWithZone:" .section __OBJC,__message_refs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9 + .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9: - .long L_OBJC_METH_VAR_NAME_f058a81939de2cb9 +L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: + .long L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 .section __IMPORT,__pointers,non_lazy_symbol_pointers LL_OBJC_CLASS_REFERENCES_NSObject$non_lazy_ptr: diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-x86.s b/crates/test-assembly/crates/test_declare_class/expected/apple-x86.s index 79f70b9c1..fecce8d74 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-x86.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-x86.s @@ -88,7 +88,7 @@ L1$pb: push eax push 0 push edi - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_class_method_inner::GENERATED_ID, 0) add esp, 24 @@ -98,7 +98,7 @@ L1$pb: push eax push 0 push edi - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 24 @@ -108,7 +108,7 @@ L1$pb: push ecx push 1 push ecx - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 24 @@ -119,7 +119,7 @@ L1$pb: push 0 lea eax, [esi + l_anon.[ID].1-L1$pb] push eax - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 24 @@ -129,7 +129,7 @@ L1$pb: push 1 lea eax, [esi + l_anon.[ID].4-L1$pb] push eax - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f-L1$pb] push ebx call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 24 @@ -142,13 +142,13 @@ L1$pb: push ebx call SYM(objc2::__macro_helpers::::__add_protocol_methods::GENERATED_ID, 0) add esp, 8 - lea ecx, [esi + _copy_with_zone-L1$pb] + lea ecx, [esi + _copyWithZone-L1$pb] lea edx, [esi + l_anon.[ID].7-L1$pb] push ecx push edi push 1 push edx - push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9-L1$pb] + push dword ptr [esi + L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166-L1$pb] push eax call SYM(objc2::declare::ClassBuilder::add_method_inner::GENERATED_ID, 0) add esp, 20 @@ -629,9 +629,9 @@ LBB14_5: pop ebp ret - .globl _copy_with_zone + .globl _copyWithZone .p2align 4, 0x90 -_copy_with_zone: +_copyWithZone: push ebp mov ebp, esp push ebx @@ -802,7 +802,7 @@ l_anon.[ID].14: .p2align 2, 0x0 l_anon.[ID].15: .long l_anon.[ID].14 - .asciz "5\000\000\000\013\000\000\000\001\000\000" + .asciz "5\000\000\000\f\000\000\000\001\000\000" .section __TEXT,__const l_anon.[ID].16: @@ -832,106 +832,106 @@ l_anon.[ID].21: .asciz "\017\000\000" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc + .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc: +L_OBJC_IMAGE_INFO_d874ee9262978be2: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc -L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc: + .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 +L_OBJC_METH_VAR_NAME_d874ee9262978be2: .asciz "classMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc + .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc: - .long L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc +L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: + .long L_OBJC_METH_VAR_NAME_d874ee9262978be2 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_450db9db0953dff5 + .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_450db9db0953dff5: +L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_450db9db0953dff5 -L_OBJC_METH_VAR_NAME_450db9db0953dff5: + .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc +L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: .asciz "method" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5 + .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5: - .long L_OBJC_METH_VAR_NAME_450db9db0953dff5 +L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: + .long L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_783b35bc45c6e4a6 + .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_783b35bc45c6e4a6: +L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 -L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6: + .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 +L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: .asciz "methodBool:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6 + .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6: - .long L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 +L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: + .long L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_828e9fbc6d0b4498 + .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_828e9fbc6d0b4498: +L_OBJC_IMAGE_INFO_f7f521670860b0ce: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 -L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498: + .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce +L_OBJC_METH_VAR_NAME_f7f521670860b0ce: .asciz "methodId" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498 + .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498: - .long L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 +L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: + .long L_OBJC_METH_VAR_NAME_f7f521670860b0ce .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_788cc14ba6a28eb8 + .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_788cc14ba6a28eb8: +L_OBJC_IMAGE_INFO_6addfcf634c6232f: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 -L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8: + .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f +L_OBJC_METH_VAR_NAME_6addfcf634c6232f: .asciz "methodIdWithParam:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8 + .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8: - .long L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 +L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: + .long L_OBJC_METH_VAR_NAME_6addfcf634c6232f .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f058a81939de2cb9 + .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f058a81939de2cb9: +L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f058a81939de2cb9 -L_OBJC_METH_VAR_NAME_f058a81939de2cb9: + .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 +L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: .asciz "copyWithZone:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9 + .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9: - .long L_OBJC_METH_VAR_NAME_f058a81939de2cb9 +L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: + .long L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 .section __IMPORT,__pointers,non_lazy_symbol_pointers LL_OBJC_CLASSLIST_REFERENCES_$_NSObject$non_lazy_ptr: diff --git a/crates/test-assembly/crates/test_declare_class/expected/apple-x86_64.s b/crates/test-assembly/crates/test_declare_class/expected/apple-x86_64.s index e0e3d301f..824f146dd 100644 --- a/crates/test-assembly/crates/test_declare_class/expected/apple-x86_64.s +++ b/crates/test-assembly/crates/test_declare_class/expected/apple-x86_64.s @@ -62,21 +62,21 @@ SYM(::call_once::<::call_once::<::call_once::<::__add_protocol_methods::GENERATED_ID, 0) - mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9] + mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166] lea rdx, [rip + l_anon.[ID].7] - lea r9, [rip + _copy_with_zone] + lea r9, [rip + _copyWithZone] mov ecx, 1 mov rdi, rax mov r8, r14 @@ -464,9 +464,9 @@ LBB14_5: pop rbp jmp _objc_autoreleaseReturnValue - .globl _copy_with_zone + .globl _copyWithZone .p2align 4, 0x90 -_copy_with_zone: +_copyWithZone: push rbp mov rbp, rsp push r15 @@ -616,7 +616,7 @@ l_anon.[ID].14: .p2align 3, 0x0 l_anon.[ID].15: .quad l_anon.[ID].14 - .asciz "5\000\000\000\000\000\000\000\013\000\000\000\001\000\000" + .asciz "5\000\000\000\000\000\000\000\f\000\000\000\001\000\000" .section __TEXT,__const l_anon.[ID].16: @@ -646,105 +646,105 @@ l_anon.[ID].21: .asciz "\017\000\000\000\000\000\000" .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc + .globl L_OBJC_IMAGE_INFO_d874ee9262978be2 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_8dd788dbcc16b9bc: +L_OBJC_IMAGE_INFO_d874ee9262978be2: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc -L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc: + .globl L_OBJC_METH_VAR_NAME_d874ee9262978be2 +L_OBJC_METH_VAR_NAME_d874ee9262978be2: .asciz "classMethod" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc + .globl L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_8dd788dbcc16b9bc: - .quad L_OBJC_METH_VAR_NAME_8dd788dbcc16b9bc +L_OBJC_SELECTOR_REFERENCES_d874ee9262978be2: + .quad L_OBJC_METH_VAR_NAME_d874ee9262978be2 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_450db9db0953dff5 + .globl L_OBJC_IMAGE_INFO_4539fd1dbda0cddc .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_450db9db0953dff5: +L_OBJC_IMAGE_INFO_4539fd1dbda0cddc: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_450db9db0953dff5 -L_OBJC_METH_VAR_NAME_450db9db0953dff5: + .globl L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc +L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc: .asciz "method" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5 + .globl L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_450db9db0953dff5: - .quad L_OBJC_METH_VAR_NAME_450db9db0953dff5 +L_OBJC_SELECTOR_REFERENCES_4539fd1dbda0cddc: + .quad L_OBJC_METH_VAR_NAME_4539fd1dbda0cddc .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_783b35bc45c6e4a6 + .globl L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_783b35bc45c6e4a6: +L_OBJC_IMAGE_INFO_2b1b3a94e0ece2e5: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 -L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6: + .globl L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 +L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5: .asciz "methodBool:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6 + .globl L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_783b35bc45c6e4a6: - .quad L_OBJC_METH_VAR_NAME_783b35bc45c6e4a6 +L_OBJC_SELECTOR_REFERENCES_2b1b3a94e0ece2e5: + .quad L_OBJC_METH_VAR_NAME_2b1b3a94e0ece2e5 .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_828e9fbc6d0b4498 + .globl L_OBJC_IMAGE_INFO_f7f521670860b0ce .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_828e9fbc6d0b4498: +L_OBJC_IMAGE_INFO_f7f521670860b0ce: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 -L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498: + .globl L_OBJC_METH_VAR_NAME_f7f521670860b0ce +L_OBJC_METH_VAR_NAME_f7f521670860b0ce: .asciz "methodId" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498 + .globl L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_828e9fbc6d0b4498: - .quad L_OBJC_METH_VAR_NAME_828e9fbc6d0b4498 +L_OBJC_SELECTOR_REFERENCES_f7f521670860b0ce: + .quad L_OBJC_METH_VAR_NAME_f7f521670860b0ce .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_788cc14ba6a28eb8 + .globl L_OBJC_IMAGE_INFO_6addfcf634c6232f .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_788cc14ba6a28eb8: +L_OBJC_IMAGE_INFO_6addfcf634c6232f: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 -L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8: + .globl L_OBJC_METH_VAR_NAME_6addfcf634c6232f +L_OBJC_METH_VAR_NAME_6addfcf634c6232f: .asciz "methodIdWithParam:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8 + .globl L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_788cc14ba6a28eb8: - .quad L_OBJC_METH_VAR_NAME_788cc14ba6a28eb8 +L_OBJC_SELECTOR_REFERENCES_6addfcf634c6232f: + .quad L_OBJC_METH_VAR_NAME_6addfcf634c6232f .section __DATA,__objc_imageinfo,regular,no_dead_strip - .globl L_OBJC_IMAGE_INFO_f058a81939de2cb9 + .globl L_OBJC_IMAGE_INFO_4a8c690dbc9d8166 .p2align 2, 0x0 -L_OBJC_IMAGE_INFO_f058a81939de2cb9: +L_OBJC_IMAGE_INFO_4a8c690dbc9d8166: .asciz "\000\000\000\000@\000\000" .section __TEXT,__objc_methname,cstring_literals - .globl L_OBJC_METH_VAR_NAME_f058a81939de2cb9 -L_OBJC_METH_VAR_NAME_f058a81939de2cb9: + .globl L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 +L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166: .asciz "copyWithZone:" .section __DATA,__objc_selrefs,literal_pointers,no_dead_strip - .globl L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9 + .globl L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166 .p2align 3, 0x0 -L_OBJC_SELECTOR_REFERENCES_f058a81939de2cb9: - .quad L_OBJC_METH_VAR_NAME_f058a81939de2cb9 +L_OBJC_SELECTOR_REFERENCES_4a8c690dbc9d8166: + .quad L_OBJC_METH_VAR_NAME_4a8c690dbc9d8166 .subsections_via_symbols diff --git a/crates/test-assembly/crates/test_declare_class/lib.rs b/crates/test-assembly/crates/test_declare_class/lib.rs index cdaf26188..ea181ca05 100644 --- a/crates/test-assembly/crates/test_declare_class/lib.rs +++ b/crates/test-assembly/crates/test_declare_class/lib.rs @@ -3,9 +3,10 @@ #![cfg(feature = "apple")] use core::ptr::{self}; +use icrate::Foundation::{NSCopying, NSObject}; use objc2::declare::{Ivar, IvarDrop, IvarEncode}; use objc2::rc::Id; -use objc2::runtime::{AnyClass, NSObject, NSZone, __NSCopying as NSCopying}; +use objc2::runtime::{AnyClass, NSZone}; use objc2::{declare_class, msg_send, msg_send_id, mutability, ClassType}; declare_class!( @@ -75,7 +76,7 @@ declare_class!( unsafe impl NSCopying for Custom { #[no_mangle] #[method_id(copyWithZone:)] - fn copy_with_zone(&self, _zone: *const NSZone) -> Option> { + fn copyWithZone(&self, _zone: *const NSZone) -> Option> { get_obj().map(|new| { let hack = Id::as_ptr(&new) as *mut Self; let hack = unsafe { &mut *hack }; diff --git a/crates/tests/Cargo.toml b/crates/tests/Cargo.toml index d066da5dd..773354db5 100644 --- a/crates/tests/Cargo.toml +++ b/crates/tests/Cargo.toml @@ -14,6 +14,7 @@ default = ["apple", "std", "Foundation_all"] std = ["block2/std", "objc2/std", "icrate/std"] exception = ["objc2/exception", "Foundation_all"] catch-all = ["objc2/catch-all", "exception"] +verify = ["objc2/verify"] # TODO: Fix this Foundation_all = [ "icrate/Foundation", diff --git a/crates/tests/src/lib.rs b/crates/tests/src/lib.rs index 435876c60..d5c70c559 100644 --- a/crates/tests/src/lib.rs +++ b/crates/tests/src/lib.rs @@ -11,6 +11,8 @@ extern crate std; mod exception; pub mod ffi; #[cfg(test)] +mod test_declare_class_protocol; +#[cfg(test)] mod test_encode_utils; #[cfg(test)] mod test_object; diff --git a/crates/tests/src/test_declare_class_protocol.rs b/crates/tests/src/test_declare_class_protocol.rs new file mode 100644 index 000000000..e408e296a --- /dev/null +++ b/crates/tests/src/test_declare_class_protocol.rs @@ -0,0 +1,159 @@ +#![cfg(feature = "Foundation_all")] +use icrate::Foundation::NSCopying; +use objc2::mutability::Immutable; +use objc2::rc::Id; +use objc2::runtime::{NSObject, NSZone}; +use objc2::{declare_class, ClassType, ProtocolType}; + +#[test] +#[should_panic = "could not create new class TestDeclareClassDuplicate. Perhaps a class with that name already exists?"] +fn test_declare_class_duplicate() { + declare_class!( + struct Custom1; + + unsafe impl ClassType for Custom1 { + type Super = NSObject; + type Mutability = Immutable; + const NAME: &'static str = "TestDeclareClassDuplicate"; + } + ); + + declare_class!( + struct Custom2; + + unsafe impl ClassType for Custom2 { + type Super = NSObject; + type Mutability = Immutable; + const NAME: &'static str = "TestDeclareClassDuplicate"; + } + ); + + let _cls = Custom1::class(); + // Should panic + let _cls = Custom2::class(); +} + +#[test] +fn test_declare_class_protocol() { + declare_class!( + struct Custom; + + unsafe impl ClassType for Custom { + type Super = NSObject; + type Mutability = Immutable; + const NAME: &'static str = "TestDeclareClassProtocolNotFound"; + } + + unsafe impl NSCopying for Custom { + #[method_id(copyWithZone:)] + fn copy_with_zone(&self, _zone: *const NSZone) -> Id { + unimplemented!() + } + } + ); + + let cls = Custom::class(); + assert!(cls.conforms_to(::protocol().unwrap())); +} + +#[test] +#[cfg_attr( + debug_assertions, + should_panic = "declared invalid method -[TestDeclareClassInvalidMethod description]: expected return to have type code '@', but found 'v'" +)] +fn test_declare_class_invalid_method() { + declare_class!( + struct Custom; + + unsafe impl ClassType for Custom { + type Super = NSObject; + type Mutability = Immutable; + const NAME: &'static str = "TestDeclareClassInvalidMethod"; + } + + unsafe impl Custom { + // Override `description` with a bad return type + #[method(description)] + fn description(&self) {} + } + ); + + let _cls = Custom::class(); +} + +#[test] +#[cfg_attr( + all(debug_assertions, feature = "verify"), + should_panic = "must implement required protocol method -[NSCopying copyWithZone:]" +)] +fn test_declare_class_missing_protocol_method() { + declare_class!( + struct Custom; + + unsafe impl ClassType for Custom { + type Super = NSObject; + type Mutability = Immutable; + const NAME: &'static str = "TestDeclareClassMissingProtocolMethod"; + } + + unsafe impl NSCopying for Custom { + // Missing required method + } + ); + + let _cls = Custom::class(); +} + +#[test] +// #[cfg_attr(all(debug_assertions, feature = "verify"), should_panic = "...")] +fn test_declare_class_invalid_protocol_method() { + declare_class!( + struct Custom; + + unsafe impl ClassType for Custom { + type Super = NSObject; + type Mutability = Immutable; + const NAME: &'static str = "TestDeclareClassInvalidProtocolMethod"; + } + + unsafe impl NSCopying for Custom { + // Override with a bad return type + #[method(copyWithZone:)] + fn copy_with_zone(&self, _zone: *const NSZone) -> u8 { + 42 + } + } + ); + + let _cls = Custom::class(); +} + +#[test] +#[cfg_attr( + all(debug_assertions, feature = "verify"), + should_panic = "failed overriding protocol method -[NSCopying someOtherMethod]: method not found" +)] +fn test_declare_class_extra_protocol_method() { + declare_class!( + struct Custom; + + unsafe impl ClassType for Custom { + type Super = NSObject; + type Mutability = Immutable; + const NAME: &'static str = "TestDeclareClassExtraProtocolMethod"; + } + + unsafe impl NSCopying for Custom { + #[method_id(copyWithZone:)] + fn copy_with_zone(&self, _zone: *const NSZone) -> Id { + unimplemented!() + } + + // This doesn't exist on the protocol + #[method(someOtherMethod)] + fn some_other_method(&self) {} + } + ); + + let _cls = Custom::class(); +} From 3661935ddf9a34da24177f0fd9297ff3b8dd2420 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 7 Sep 2023 10:41:05 +0200 Subject: [PATCH 2/5] Implement Eq and Ord for NSNumber --- crates/icrate/CHANGELOG.md | 2 ++ .../icrate/src/additions/Foundation/number.rs | 22 ++++++++++++++++++- crates/icrate/tests/number.rs | 22 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index 524e2b037..8022cefc0 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -16,6 +16,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ones Swift marks as `@Sendable`). * Made some common methods in `AppKit` safe. * Added missing `NSCopying` and `NSMutableCopying` zone methods. +* Added `Eq` and `Ord` implementations for `NSNumber`, since its + handling of floating point values allows it. ### Changed * Moved the `ns_string!` macro to `icrate::Foundation::ns_string`. The old diff --git a/crates/icrate/src/additions/Foundation/number.rs b/crates/icrate/src/additions/Foundation/number.rs index 8a997e5ea..decc59fff 100644 --- a/crates/icrate/src/additions/Foundation/number.rs +++ b/crates/icrate/src/additions/Foundation/number.rs @@ -217,13 +217,33 @@ impl PartialEq for NSNumber { } } +/// Beware: This uses the Objective-C method "isEqualToNumber:", which has +/// different floating point NaN semantics than Rust! +// +// This is valid since the following pass (i.e. Objective-C says that two NaNs +// are equal): +// ``` +// let nan = NSNumber::from_f32(f32::NAN); +// assert_eq!(nan, nan); +// ``` +impl Eq for NSNumber {} + /// Beware: This uses the Objective-C method "compare:", which has different /// floating point NaN semantics than Rust! impl PartialOrd for NSNumber { #[doc(alias = "compare:")] fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Beware: This uses the Objective-C method "compare:", which has different +/// floating point NaN semantics than Rust! +impl Ord for NSNumber { + #[doc(alias = "compare:")] + fn cmp(&self, other: &Self) -> Ordering { // Use Objective-C semantics for comparison - Some(self.compare(other).into()) + self.compare(other).into() } } diff --git a/crates/icrate/tests/number.rs b/crates/icrate/tests/number.rs index 05ed98710..c1d8dc247 100644 --- a/crates/icrate/tests/number.rs +++ b/crates/icrate/tests/number.rs @@ -88,6 +88,28 @@ fn equality() { assert_ne!(val1, val4); } +#[test] +#[cfg_attr(feature = "gnustep-1-7", ignore = "GNUStep handles NaNs differently")] +fn nan_equality() { + let nan = NSNumber::new_f32(f32::NAN); + let nan2 = NSNumber::new_f32(f32::NAN); + let neg_nan = NSNumber::new_f32(-f32::NAN); + assert_eq!(nan, nan); + assert_eq!(nan, nan2); + assert_eq!(neg_nan, neg_nan); + assert_eq!(nan, neg_nan); +} + +// Ensure that comparisons are made on the number, and not the bits of the floating point value +#[test] +fn float_int_equality() { + let val1 = NSNumber::new_f32(1.0); + let val2 = NSNumber::new_u32(1); + let val3 = NSNumber::new_u32(1.0f32.to_bits()); + assert_eq!(val1, val2); + assert_ne!(val1, val3); +} + #[test] #[cfg(feature = "Foundation_NSString")] fn display_debug() { From b4cb59e593741bc7d20d6a57c6537900785c6b25 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 7 Sep 2023 13:50:09 +0200 Subject: [PATCH 3/5] Fix unsoundness in `NSDictionary` and `NSSet` creation/mutation Almost at least, some generic types like `NSArray` is still incorrectly allowed as a key --- crates/icrate/CHANGELOG.md | 15 + crates/icrate/examples/basic_usage.rs | 4 +- .../src/additions/Foundation/dictionary.rs | 275 +++++++++++++----- crates/icrate/src/additions/Foundation/set.rs | 164 +++++++---- crates/icrate/src/fixes/Foundation/copying.rs | 3 + crates/icrate/tests/attributed_string.rs | 5 +- crates/icrate/tests/dictionary.rs | 19 +- crates/icrate/tests/mutable_dictionary.rs | 73 +++-- crates/icrate/tests/mutable_set.rs | 24 +- crates/icrate/tests/set.rs | 8 +- crates/objc2/CHANGELOG.md | 7 +- crates/objc2/src/mutability.rs | 43 ++- crates/objc2/src/rc/test_object.rs | 2 +- crates/objc2/src/runtime/nsobject.rs | 4 + crates/test-ui/Cargo.toml | 1 + .../test-ui/ui/msg_send_invalid_error.stderr | 2 +- crates/test-ui/ui/nsset_from_nsobject.rs | 6 + crates/test-ui/ui/nsset_from_nsobject.stderr | 22 ++ 18 files changed, 493 insertions(+), 184 deletions(-) create mode 100644 crates/test-ui/ui/nsset_from_nsobject.rs create mode 100644 crates/test-ui/ui/nsset_from_nsobject.stderr diff --git a/crates/icrate/CHANGELOG.md b/crates/icrate/CHANGELOG.md index 8022cefc0..01a0f4da0 100644 --- a/crates/icrate/CHANGELOG.md +++ b/crates/icrate/CHANGELOG.md @@ -18,6 +18,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added missing `NSCopying` and `NSMutableCopying` zone methods. * Added `Eq` and `Ord` implementations for `NSNumber`, since its handling of floating point values allows it. +* Added `NS[Mutable]Dictionary::from_id_slice` and + `NS[Mutable]Dictionary::from_slice`. +* Added `NSMutableDictionary::insert` and `NSMutableSet::insert` which can + be more efficient than the previous insertion methods. ### Changed * Moved the `ns_string!` macro to `icrate::Foundation::ns_string`. The old @@ -44,6 +48,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). // Do something with `app` and `view` ``` * **BREAKING**: Changed the `NSApp` static to be a function taking `MainThreadMarker`. +* **BREAKING**: Renamed `NS[Mutable]Dictionary::from_keys_and_objects` to + `NS[Mutable]Dictionary::from_vec`. +* **BREAKING**: Renamed `NSMutableDictionary::insert` and + `NSMutableSet::insert` to `insert_id`. ### Removed * **BREAKING**: Removed the `MainThreadMarker` argument from the closure @@ -51,6 +59,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * **BREAKING**: Removed `Foundation::CopyHelper` since it is superseded by `objc2::mutability::CounterpartOrSelf`. +### Fixed +* **BREAKING**: Added `Eq + Hash` requirement on most `NSDictionary` and + `NSSet` methods, thereby making sure that the types are actually correct + to use in such hashing collections. +* **BREAKING**: Added `HasStableHash` requirement on `NSDictionary` and + `NSSet` creation methods, fixing a long-standing soundess issue. + ## icrate 0.0.4 - 2023-07-31 diff --git a/crates/icrate/examples/basic_usage.rs b/crates/icrate/examples/basic_usage.rs index 711273a10..7b40c67b4 100644 --- a/crates/icrate/examples/basic_usage.rs +++ b/crates/icrate/examples/basic_usage.rs @@ -36,8 +36,8 @@ fn main() { // Create a dictionary mapping strings to objects let keys = &[string]; - let vals = vec![obj]; - let dict = NSDictionary::from_keys_and_objects(keys, vals); + let objects = &[obj]; + let dict = NSDictionary::from_id_slice(keys, objects); println!("{:?}", dict.get(string)); println!("{}", dict.len()); } diff --git a/crates/icrate/src/additions/Foundation/dictionary.rs b/crates/icrate/src/additions/Foundation/dictionary.rs index 1d752b3da..f2fef8d08 100644 --- a/crates/icrate/src/additions/Foundation/dictionary.rs +++ b/crates/icrate/src/additions/Foundation/dictionary.rs @@ -1,14 +1,16 @@ //! Utilities for the `NSDictionary` and `NSMutableDictionary` classes. #![cfg(feature = "Foundation_NSDictionary")] use alloc::vec::Vec; +use core::borrow::Borrow; use core::cmp::min; use core::fmt; +use core::hash::Hash; use core::mem; use core::ops::{Index, IndexMut}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr::{self, NonNull}; -use objc2::mutability::{CounterpartOrSelf, IsMutable}; +use objc2::mutability::{CounterpartOrSelf, HasStableHash, IsIdCloneable, IsMutable, IsRetainable}; use super::iter; use super::util; @@ -17,47 +19,134 @@ use crate::common::*; use crate::Foundation::NSMutableDictionary; use crate::Foundation::{self, NSCopying, NSDictionary}; -impl NSDictionary { - pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id +impl NSDictionary { + pub fn from_vec(keys: &[&Q], mut objects: Vec>) -> Id + where + Q: Message + NSCopying + CounterpartOrSelf, + { + // Find the minimum of the two provided lengths, to ensure that we + // don't read too far in one of the buffers. + // + // Note: We could also have chosen to just panic here if the buffers have + // different lengths, either would be fine. + let count = min(keys.len(), objects.len()); + + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = keys.cast(); + let objects = util::id_ptr_cast(objects.as_mut_ptr()); + + // SAFETY: + // - The objects are valid, similar reasoning as `NSArray::from_vec`. + // + // - The key must not be mutated, as that may cause the hash value to + // change, which is unsound as stated in: + // https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectMutability/ObjectMutability.html#//apple_ref/doc/uid/TP40010810-CH5-SW69 + // + // The dictionary always copies its keys, which is why we require + // `NSCopying` and use `CounterpartOrSelf` on all input data - we + // want to ensure that it is very clear that it's not actually + // `NSMutableString` that is being stored, but `NSString`. + // + // But that is not by itself enough to verify that the key does not + // still contain interior mutable objects (e.g. if the copy was only + // a shallow copy), which is why we also require `HasStableHash`. + // + // - The length is lower than or equal to the length of the two arrays. + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) } + } + + pub fn from_id_slice(keys: &[&Q], objects: &[Id]) -> Id where - T: ClassType + NSCopying + CounterpartOrSelf, + Q: Message + NSCopying + CounterpartOrSelf, + V: IsIdCloneable, { - let count = min(keys.len(), vals.len()); + let count = min(keys.len(), objects.len()); - let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); let keys: *mut NonNull = keys.cast(); - let vals = util::id_ptr_cast(vals.as_mut_ptr()); + let objects = util::id_ptr_cast_const(objects.as_ptr()); - unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), vals, keys, count) } + // SAFETY: See `NSDictionary::from_vec` and `NSArray::from_id_slice`. + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) } + } + + pub fn from_slice(keys: &[&Q], objects: &[&V]) -> Id + where + Q: Message + NSCopying + CounterpartOrSelf, + V: IsRetainable, + { + let count = min(keys.len(), objects.len()); + + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = keys.cast(); + let objects = util::ref_ptr_cast_const(objects.as_ptr()); + + // SAFETY: See `NSDictionary::from_vec` and `NSArray::from_slice`. + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) } } } #[cfg(feature = "Foundation_NSMutableDictionary")] -impl NSMutableDictionary { - pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id +impl NSMutableDictionary { + pub fn from_vec(keys: &[&Q], mut objects: Vec>) -> Id where - T: ClassType + NSCopying + CounterpartOrSelf, + Q: Message + NSCopying + CounterpartOrSelf, { - let count = min(keys.len(), vals.len()); + let count = min(keys.len(), objects.len()); - let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); let keys: *mut NonNull = keys.cast(); - let vals = util::id_ptr_cast(vals.as_mut_ptr()); + let objects = util::id_ptr_cast(objects.as_mut_ptr()); - unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), vals, keys, count) } + // SAFETY: See `NSDictionary::from_vec` + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) } } -} -extern_methods!( - unsafe impl NSDictionary { - pub fn len(&self) -> usize { - self.count() - } + pub fn from_id_slice(keys: &[&Q], objects: &[Id]) -> Id + where + Q: Message + NSCopying + CounterpartOrSelf, + V: IsIdCloneable, + { + let count = min(keys.len(), objects.len()); - pub fn is_empty(&self) -> bool { - self.len() == 0 - } + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = keys.cast(); + let objects = util::id_ptr_cast_const(objects.as_ptr()); + + // SAFETY: See `NSDictionary::from_vec` and `NSArray::from_id_slice`. + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) } + } + + pub fn from_slice(keys: &[&Q], objects: &[&V]) -> Id + where + Q: Message + NSCopying + CounterpartOrSelf, + V: IsRetainable, + { + let count = min(keys.len(), objects.len()); + + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = keys.cast(); + let objects = util::ref_ptr_cast_const(objects.as_ptr()); + // SAFETY: See `NSDictionary::from_vec` and `NSArray::from_slice`. + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) } + } +} + +// Note: We'd like to make getter methods take `K: Borrow` like +// `std::collections::HashMap`, so that e.g. +// `NSDictionary` could take a `&NSObject` as input, +// and still make that work since `NSString` borrows to `NSObject`. +// +// But we can't really, at least not with extra `unsafe` / an extra +// trait, since we don't control how the comparisons happen. +// +// The most useful alternative would probably be to take +// `impl AsRef`, but objc2 classes deref to their superclass anyhow, so +// let's just use a simple normal reference. + +extern_methods!( + unsafe impl NSDictionary { #[doc(alias = "objectForKey:")] #[method(objectForKey:)] pub fn get(&self, key: &K) -> Option<&V>; @@ -93,7 +182,7 @@ extern_methods!( /// use icrate::Foundation::{ns_string, NSMutableDictionary, NSMutableString, NSString}; /// /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSMutableString::new()); + /// dict.insert_id(ns_string!("one"), NSMutableString::new()); /// println!("{:?}", dict.get_mut(ns_string!("one"))); /// ``` #[doc(alias = "objectForKey:")] @@ -105,6 +194,14 @@ extern_methods!( ); impl NSDictionary { + pub fn len(&self) -> usize { + self.count() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + #[doc(alias = "getObjects:andKeys:")] pub fn keys_vec(&self) -> Vec<&K> { let len = self.len(); @@ -117,8 +214,7 @@ impl NSDictionary { } } - // We don't provide `keys_mut_vec`, since keys are expected to be - // immutable. + // We don't provide `keys_mut_vec`, since keys are immutable. #[doc(alias = "getObjects:andKeys:")] pub fn values_vec(&self) -> Vec<&V> { @@ -150,10 +246,10 @@ impl NSDictionary { )), doc = "```ignore" )] - /// use icrate::Foundation::{NSMutableDictionary, NSMutableString, NSString}; + /// use icrate::Foundation::{ns_string, NSMutableDictionary, NSMutableString, NSString}; /// /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSMutableString::from_str("two")); + /// dict.insert_id(ns_string!("one"), NSMutableString::from_str("two")); /// for val in dict.values_mut() { /// println!("{:?}", val); /// } @@ -186,10 +282,35 @@ impl NSDictionary { (mem::transmute(keys), mem::transmute(objs)) } } + + /// Returns an [`NSArray`] containing the dictionary's values. + /// + /// [`NSArray`]: crate::Foundation::NSArray + /// + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{ns_string, NSMutableDictionary, NSObject, NSString}; + /// + /// let mut dict = NSMutableDictionary::new(); + /// dict.insert_id(ns_string!("one"), NSObject::new()); + /// let array = dict.to_array(); + /// assert_eq!(array.len(), 1); + /// ``` + #[cfg(feature = "Foundation_NSArray")] + pub fn to_array(&self) -> Id> + where + V: IsIdCloneable, + { + // SAFETY: The elements are retainable behind `Id`, so getting + // another reference to them (via. `NSArray`) is sound. + unsafe { self.allValues() } + } } #[cfg(feature = "Foundation_NSMutableDictionary")] -impl NSMutableDictionary { +impl NSMutableDictionary { /// Inserts a key-value pair into the dictionary. /// /// If the dictionary did not have this key present, None is returned. @@ -199,69 +320,93 @@ impl NSMutableDictionary { /// # Examples /// /// ``` - /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; + /// use icrate::Foundation::{NSMutableDictionary, NSObject, ns_string}; /// /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSObject::new()); + /// dict.insert_id(ns_string!("one"), NSObject::new()); /// ``` #[doc(alias = "setObject:forKey:")] - pub fn insert(&mut self, key: Id, value: Id) -> Option> { + pub fn insert_id(&mut self, key: &K, value: Id) -> Option> + where + K: NSCopying + CounterpartOrSelf, + { // SAFETY: We remove the object from the dictionary below let old_obj = self - .get(&key) + .get(key) .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); - // SAFETY: It is always safe to transmute an `Id` to `AnyObject`. - let key: Id = unsafe { Id::cast(key) }; - // SAFETY: We have ownership over both the key and the value. - unsafe { self.setObject_forKey(&value, &key) }; + // SAFETY: It is always safe to transmute an `&T` where `T: Message` + // to `&AnyObject`. + let key: NonNull = NonNull::from(key); + let key: NonNull = key.cast(); + let key: &AnyObject = unsafe { key.as_ref() }; + // SAFETY: The key is NSCopying (see `NSDictionary::from_vec`), and we + // have ownership over the value. + unsafe { self.setObject_forKey(&value, key) }; old_obj } - /// Removes a key from the dictionary, returning the value at the key - /// if the key was previously in the dictionary. + /// Inserts a key-value pair into the dictionary. + /// + /// If the dictionary did not have this key present, None is returned. + /// If the dictionary did have this key present, the value is updated, + /// and the old value is returned. /// /// # Examples /// /// ``` - /// use icrate::Foundation::{ns_string, NSMutableDictionary, NSObject, NSString}; + /// use icrate::Foundation::{ns_string, NSCopying, NSMutableDictionary}; /// /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSObject::new()); - /// dict.remove(ns_string!("one")); - /// assert!(dict.is_empty()); + /// dict.insert_id(ns_string!("key"), ns_string!("value").copy()); /// ``` - #[doc(alias = "removeObjectForKey:")] - pub fn remove(&mut self, key: &K) -> Option> { + #[doc(alias = "setObject:forKey:")] + pub fn insert(&mut self, key: &K, value: &V) -> Option> + where + K: NSCopying + CounterpartOrSelf, + V: IsRetainable, + { // SAFETY: We remove the object from the dictionary below let old_obj = self .get(key) .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); - self.removeObjectForKey(key); + + // SAFETY: It is always safe to transmute an `&T` where `T: Message` + // to `&AnyObject`. + let key: NonNull = NonNull::from(key); + let key: NonNull = key.cast(); + let key: &AnyObject = unsafe { key.as_ref() }; + // SAFETY: The key is NSCopying (see `NSDictionary::from_vec`), and + // the value is `IsRetainable` (and hence safe for the collection to + // retain). + unsafe { self.setObject_forKey(value, key) }; old_obj } - /// Returns an [`NSArray`] containing the dictionary's values, - /// consuming the dictionary. - /// - /// [`NSArray`]: crate::Foundation::NSArray - /// + /// Removes a key from the dictionary, returning the value at the key + /// if the key was previously in the dictionary. /// /// # Examples /// /// ``` - /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; + /// use icrate::Foundation::{ns_string, NSMutableDictionary, NSObject}; /// /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSObject::new()); - /// let array = NSMutableDictionary::into_values_array(dict); - /// println!("{:?}", array); + /// dict.insert_id(ns_string!("one"), NSObject::new()); + /// dict.remove(ns_string!("one")); + /// assert!(dict.is_empty()); /// ``` - #[cfg(feature = "Foundation_NSArray")] - pub fn into_values_array(this: Id) -> Id> { - // SAFETY: We've consumed the dictionary, so getting an array from - // it is safe. - unsafe { this.allValues() } + #[doc(alias = "removeObjectForKey:")] + pub fn remove(&mut self, key: &K) -> Option> + where + K: CounterpartOrSelf, + { + // SAFETY: We remove the object from the dictionary below + let old_obj = self + .get(key) + .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); + self.removeObjectForKey(key); + old_obj } } @@ -418,7 +563,7 @@ mod iter_helpers { #[cfg(feature = "Foundation_NSEnumerator")] pub use self::iter_helpers::*; -impl<'a, K: Message, V: Message> Index<&'a K> for NSDictionary { +impl<'a, K: Message + Eq + Hash, V: Message> Index<&'a K> for NSDictionary { type Output = V; fn index<'s>(&'s self, index: &'a K) -> &'s V { @@ -427,7 +572,7 @@ impl<'a, K: Message, V: Message> Index<&'a K> for NSDictionary { } #[cfg(feature = "Foundation_NSMutableDictionary")] -impl<'a, K: Message, V: Message> Index<&'a K> for NSMutableDictionary { +impl<'a, K: Message + Eq + Hash, V: Message> Index<&'a K> for NSMutableDictionary { type Output = V; fn index<'s>(&'s self, index: &'a K) -> &'s V { @@ -435,14 +580,14 @@ impl<'a, K: Message, V: Message> Index<&'a K> for NSMutableDictionary { } } -impl<'a, K: Message, V: IsMutable> IndexMut<&'a K> for NSDictionary { +impl<'a, K: Message + Eq + Hash, V: IsMutable> IndexMut<&'a K> for NSDictionary { fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { self.get_mut(index).unwrap() } } #[cfg(feature = "Foundation_NSMutableDictionary")] -impl<'a, K: Message, V: IsMutable> IndexMut<&'a K> for NSMutableDictionary { +impl<'a, K: Message + Eq + Hash, V: IsMutable> IndexMut<&'a K> for NSMutableDictionary { fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { self.get_mut(index).unwrap() } diff --git a/crates/icrate/src/additions/Foundation/set.rs b/crates/icrate/src/additions/Foundation/set.rs index 0f3c9f7db..0d338b8fb 100644 --- a/crates/icrate/src/additions/Foundation/set.rs +++ b/crates/icrate/src/additions/Foundation/set.rs @@ -2,9 +2,10 @@ #![cfg(feature = "Foundation_NSSet")] use alloc::vec::Vec; use core::fmt; +use core::hash::Hash; use core::panic::{RefUnwindSafe, UnwindSafe}; -use objc2::mutability::IsRetainable; +use objc2::mutability::{HasStableHash, IsRetainable}; use objc2::rc::IdFromIterator; use super::iter; @@ -15,6 +16,38 @@ use crate::Foundation::NSMutableSet; use crate::Foundation::{self, NSSet}; impl NSSet { + /// Returns the number of elements in the set. + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{NSSet, NSString}; + /// + /// let strs = ["one", "two", "three"].map(NSString::from_str); + /// let set = NSSet::from_id_slice(&strs); + /// assert_eq!(set.len(), 3); + /// ``` + #[doc(alias = "count")] + pub fn len(&self) -> usize { + self.count() + } + + /// Returns `true` if the set contains no elements. + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{NSSet, NSString}; + /// + /// let set = NSSet::::new(); + /// assert!(set.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl NSSet { /// Creates an [`NSSet`] from a vector. /// /// # Examples @@ -25,7 +58,10 @@ impl NSSet { /// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec(); /// let set = NSSet::from_vec(strs); /// ``` - pub fn from_vec(mut vec: Vec>) -> Id { + pub fn from_vec(mut vec: Vec>) -> Id + where + T: HasStableHash, + { let len = vec.len(); let ptr = util::id_ptr_cast(vec.as_mut_ptr()); // SAFETY: Same as `NSArray::from_vec`. @@ -44,7 +80,7 @@ impl NSSet { /// ``` pub fn from_id_slice(slice: &[Id]) -> Id where - T: IsIdCloneable, + T: HasStableHash + IsIdCloneable, { let len = slice.len(); let ptr = util::id_ptr_cast_const(slice.as_ptr()); @@ -54,7 +90,7 @@ impl NSSet { pub fn from_slice(slice: &[&T]) -> Id where - T: IsRetainable, + T: HasStableHash + IsRetainable, { let len = slice.len(); let ptr = util::ref_ptr_cast_const(slice.as_ptr()); @@ -126,7 +162,7 @@ impl NSSet { } #[cfg(feature = "Foundation_NSMutableSet")] -impl NSMutableSet { +impl NSMutableSet { /// Creates an [`NSMutableSet`] from a vector. /// /// # Examples @@ -137,7 +173,10 @@ impl NSMutableSet { /// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec(); /// let set = NSMutableSet::from_vec(strs); /// ``` - pub fn from_vec(mut vec: Vec>) -> Id { + pub fn from_vec(mut vec: Vec>) -> Id + where + T: HasStableHash, + { let len = vec.len(); let ptr = util::id_ptr_cast(vec.as_mut_ptr()); // SAFETY: Same as `NSArray::from_vec`. @@ -156,7 +195,7 @@ impl NSMutableSet { /// ``` pub fn from_id_slice(slice: &[Id]) -> Id where - T: IsIdCloneable, + T: HasStableHash + IsIdCloneable, { let len = slice.len(); let ptr = util::id_ptr_cast_const(slice.as_ptr()); @@ -166,7 +205,7 @@ impl NSMutableSet { pub fn from_slice(slice: &[&T]) -> Id where - T: IsRetainable, + T: HasStableHash + IsRetainable, { let len = slice.len(); let ptr = util::ref_ptr_cast_const(slice.as_ptr()); @@ -197,36 +236,6 @@ impl NSMutableSet { extern_methods!( unsafe impl NSSet { - /// Returns the number of elements in the set. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSSet, NSString}; - /// - /// let strs = ["one", "two", "three"].map(NSString::from_str); - /// let set = NSSet::from_id_slice(&strs); - /// assert_eq!(set.len(), 3); - /// ``` - #[doc(alias = "count")] - pub fn len(&self) -> usize { - self.count() - } - - /// Returns `true` if the set contains no elements. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSSet, NSString}; - /// - /// let set = NSSet::::new(); - /// assert!(set.is_empty()); - /// ``` - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - /// Returns a reference to one of the objects in the set, or `None` if /// the set is empty. /// @@ -245,10 +254,7 @@ extern_methods!( pub fn get_any(&self) -> Option<&T>; } - // We're explicit about `T` being `PartialEq` for these methods because - // the set compares the input value(s) with elements in the set. For - // comparison: Rust's HashSet requires similar methods to be `Hash` + `Eq` - unsafe impl NSSet { + unsafe impl NSSet { /// Returns `true` if the set contains a value. /// /// # Examples @@ -292,7 +298,7 @@ extern_methods!( } // Note: No `get_mut` method exposed on sets, since their objects' - // hashes are supposed to be immutable. + // hashes are immutable. /// Returns `true` if the set is a subset of another, i.e., `other` /// contains at least all the values in `self`. @@ -353,8 +359,34 @@ extern_methods!( ); #[cfg(feature = "Foundation_NSMutableSet")] -impl NSMutableSet { - /// Adds a value to the set. Returns whether the value was +impl NSMutableSet { + /// Add a value to the set. Returns whether the value was + /// newly inserted. + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{NSNumber, NSMutableSet}; + /// + /// let mut set = NSMutableSet::new(); + /// + /// assert_eq!(set.insert(&*NSNumber::new_u32(42)), true); + /// assert_eq!(set.insert(&*NSNumber::new_u32(42)), false); + /// assert_eq!(set.len(), 1); + /// ``` + #[doc(alias = "addObject:")] + pub fn insert(&mut self, value: &T) -> bool + where + T: HasStableHash + IsRetainable, + { + let contains_value = self.contains(value); + // SAFETY: Because of the `T: IsRetainable` bound, it is safe for the + // set to retain the object here. + unsafe { self.addObject(value) }; + !contains_value + } + + /// Add an `Id` to the set. Returns whether the value was /// newly inserted. /// /// # Examples @@ -364,12 +396,15 @@ impl NSMutableSet { /// /// let mut set = NSMutableSet::new(); /// - /// assert_eq!(set.insert(NSString::from_str("one")), true); - /// assert_eq!(set.insert(NSString::from_str("one")), false); + /// assert_eq!(set.insert_id(NSString::from_str("one")), true); + /// assert_eq!(set.insert_id(NSString::from_str("one")), false); /// assert_eq!(set.len(), 1); /// ``` #[doc(alias = "addObject:")] - pub fn insert(&mut self, value: Id) -> bool { + pub fn insert_id(&mut self, value: Id) -> bool + where + T: HasStableHash, + { let contains_value = self.contains(&value); // SAFETY: We've consumed ownership of the object. unsafe { self.addObject(&value) }; @@ -386,18 +421,24 @@ impl NSMutableSet { /// /// let mut set = NSMutableSet::new(); /// - /// set.insert(NSString::from_str("one")); + /// set.insert_id(NSString::from_str("one")); /// assert_eq!(set.remove(ns_string!("one")), true); /// assert_eq!(set.remove(ns_string!("one")), false); /// ``` #[doc(alias = "removeObject:")] - pub fn remove(&mut self, value: &T) -> bool { + pub fn remove(&mut self, value: &T) -> bool + where + T: HasStableHash, + { let contains_value = self.contains(value); unsafe { self.removeObject(value) }; contains_value } } +// Iteration is not supposed to touch the elements, not even do comparisons. +// +// TODO: Verify that this is actually the case. impl NSSet { /// An iterator visiting all elements in arbitrary order. /// @@ -499,32 +540,31 @@ impl fmt::Debug for NSSet { } #[cfg(feature = "Foundation_NSMutableSet")] -impl Extend> for NSMutableSet { +impl Extend> for NSMutableSet { fn extend>>(&mut self, iter: I) { iter.into_iter().for_each(move |item| { - self.insert(item); + self.insert_id(item); }) } } #[cfg(feature = "Foundation_NSMutableSet")] -impl<'a, T: IsRetainable + PartialEq> Extend<&'a T> for NSMutableSet { +impl<'a, T: IsRetainable + Eq + Hash + HasStableHash> Extend<&'a T> for NSMutableSet { fn extend>(&mut self, iter: I) { - // SAFETY: Because of the `T: IsRetainable` bound, it is safe for the - // set to retain the object here. - iter.into_iter() - .for_each(move |item| unsafe { self.addObject(item) }) + iter.into_iter().for_each(move |item| { + self.insert(item); + }) } } -impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSSet { +impl<'a, T: IsRetainable + Eq + Hash + HasStableHash + 'a> IdFromIterator<&'a T> for NSSet { fn id_from_iter>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_slice(&vec) } } -impl IdFromIterator> for NSSet { +impl IdFromIterator> for NSSet { fn id_from_iter>>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_vec(vec) @@ -532,7 +572,9 @@ impl IdFromIterator> for NSSet { } #[cfg(feature = "Foundation_NSMutableSet")] -impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableSet { +impl<'a, T: IsRetainable + Eq + Hash + HasStableHash + 'a> IdFromIterator<&'a T> + for NSMutableSet +{ fn id_from_iter>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_slice(&vec) @@ -540,7 +582,7 @@ impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableSet { } #[cfg(feature = "Foundation_NSMutableSet")] -impl IdFromIterator> for NSMutableSet { +impl IdFromIterator> for NSMutableSet { fn id_from_iter>>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_vec(vec) diff --git a/crates/icrate/src/fixes/Foundation/copying.rs b/crates/icrate/src/fixes/Foundation/copying.rs index 7cf70cfa0..5117ae079 100644 --- a/crates/icrate/src/fixes/Foundation/copying.rs +++ b/crates/icrate/src/fixes/Foundation/copying.rs @@ -46,6 +46,9 @@ extern_protocol!( } ); +// FIXME: Remove this hack which makes NSMutableDictionary tests work +unsafe impl NSCopying for objc2::rc::__RcTestObject {} + extern_protocol!( /// A protocol to provide mutable copies of objects. /// diff --git a/crates/icrate/tests/attributed_string.rs b/crates/icrate/tests/attributed_string.rs index 6cc0acb13..c41a0d745 100644 --- a/crates/icrate/tests/attributed_string.rs +++ b/crates/icrate/tests/attributed_string.rs @@ -60,10 +60,7 @@ fn test_debug() { let s = unsafe { NSAttributedString::new_with_attributes( &NSString::from_str("abc"), - &Foundation::NSDictionary::from_keys_and_objects( - &[&*NSString::from_str("test")], - vec![obj], - ), + &Foundation::NSDictionary::from_vec(&[&*NSString::from_str("test")], vec![obj]), ) }; let expected = if cfg!(feature = "gnustep-1-7") { diff --git a/crates/icrate/tests/dictionary.rs b/crates/icrate/tests/dictionary.rs index e390b13d3..e03a234f0 100644 --- a/crates/icrate/tests/dictionary.rs +++ b/crates/icrate/tests/dictionary.rs @@ -7,7 +7,7 @@ use icrate::Foundation::{NSDictionary, NSObject, NSString}; fn sample_dict(key: &str) -> Id> { let string = NSString::from_str(key); let obj = NSObject::new(); - NSDictionary::from_keys_and_objects(&[&*string], vec![obj]) + NSDictionary::from_vec(&[&*string], vec![obj]) } #[test] @@ -87,7 +87,7 @@ fn test_arrays() { assert_eq!(keys[0].as_str(pool), "abcd"); }); - // let objs = NSDictionary::into_values_array(dict); + // let objs = dict.to_array(); // assert_eq!(objs.len(), 1); } @@ -95,6 +95,19 @@ fn test_arrays() { fn test_debug() { let key = NSString::from_str("a"); let val = NSString::from_str("b"); - let dict = NSDictionary::from_keys_and_objects(&[&*key], vec![val]); + let dict = NSDictionary::from_id_slice(&[&*key], &[val]); assert_eq!(format!("{dict:?}"), r#"{"a": "b"}"#); } + +#[test] +fn new_different_lengths() { + let dict = NSDictionary::from_id_slice( + &[ + &*NSString::from_str("a"), + &*NSString::from_str("b"), + &*NSString::from_str("c"), + ], + &[NSObject::new(), NSObject::new()], + ); + assert_eq!(dict.len(), 2); +} diff --git a/crates/icrate/tests/mutable_dictionary.rs b/crates/icrate/tests/mutable_dictionary.rs index 83ed4ba67..61db87512 100644 --- a/crates/icrate/tests/mutable_dictionary.rs +++ b/crates/icrate/tests/mutable_dictionary.rs @@ -6,19 +6,19 @@ use objc2::rc::{Id, __RcTestObject, __ThreadTestData}; use icrate::Foundation::{NSMutableDictionary, NSNumber, NSObject}; fn sample_dict() -> Id> { - NSMutableDictionary::from_keys_and_objects( + NSMutableDictionary::from_id_slice( &[ &*NSNumber::new_i32(1), &*NSNumber::new_i32(2), &*NSNumber::new_i32(3), ], - vec![NSObject::new(), NSObject::new(), NSObject::new()], + &[NSObject::new(), NSObject::new(), NSObject::new()], ) } #[cfg(feature = "Foundation_NSMutableString")] fn sample_dict_mut() -> Id> { - NSMutableDictionary::from_keys_and_objects( + NSMutableDictionary::from_vec( &[ &*NSNumber::new_i32(1), &*NSNumber::new_i32(2), @@ -32,6 +32,18 @@ fn sample_dict_mut() -> Id> = + NSMutableDictionary::from_id_slice( + &[&*icrate::Foundation::NSMutableString::from_str("a")], + &[Id::into_super( + icrate::Foundation::NSMutableString::from_str("b"), + )], + ); +} + #[test] fn test_new() { let dict = NSMutableDictionary::::new(); @@ -58,29 +70,33 @@ fn test_values_mut() { #[test] fn test_insert() { - let mut dict = NSMutableDictionary::new(); - assert!(dict.insert(NSNumber::new_i32(1), NSObject::new()).is_none()); - assert!(dict.insert(NSNumber::new_i32(2), NSObject::new()).is_none()); - assert!(dict.insert(NSNumber::new_i32(3), NSObject::new()).is_none()); - assert!(dict.insert(NSNumber::new_i32(1), NSObject::new()).is_some()); + let mut dict = >::new(); + assert!(dict + .insert_id(&NSNumber::new_i32(1), NSObject::new()) + .is_none()); + assert!(dict + .insert_id(&NSNumber::new_i32(2), NSObject::new()) + .is_none()); + assert!(dict + .insert_id(&NSNumber::new_i32(3), NSObject::new()) + .is_none()); + assert!(dict + .insert_id(&NSNumber::new_i32(1), NSObject::new()) + .is_some()); assert_eq!(dict.len(), 3); } #[test] fn test_insert_key_copies() { - let mut dict = NSMutableDictionary::new(); + let mut dict = >::new(); let key1 = __RcTestObject::new(); let mut expected = __ThreadTestData::current(); - let _ = dict.insert(key1, NSNumber::new_i32(1)); + let _ = dict.insert_id(&key1, NSNumber::new_i32(1)); // Create copy expected.copy += 1; expected.alloc += 1; expected.init += 1; - - // Release passed-in key - expected.release += 1; - expected.dealloc += 1; expected.assert_current(); dict.removeAllObjects(); @@ -90,14 +106,26 @@ fn test_insert_key_copies() { expected.assert_current(); } +#[test] +fn test_get_key_copies() { + let mut dict = >::new(); + let key1 = __RcTestObject::new(); + let _ = dict.insert_id(&key1, NSNumber::new_i32(1)); + let expected = __ThreadTestData::current(); + + let _ = dict.get(&key1); + // No change, getting doesn't do anything to the key! + expected.assert_current(); +} + #[test] fn test_insert_value_retain_release() { - let mut dict = NSMutableDictionary::new(); - dict.insert(NSNumber::new_i32(1), __RcTestObject::new()); + let mut dict = >::new(); + dict.insert_id(&NSNumber::new_i32(1), __RcTestObject::new()); let to_insert = __RcTestObject::new(); let mut expected = __ThreadTestData::current(); - let old = dict.insert(NSNumber::new_i32(1), to_insert); + let old = dict.insert(&NSNumber::new_i32(1), &to_insert); // Grab old value expected.retain += 1; @@ -105,9 +133,6 @@ fn test_insert_value_retain_release() { expected.retain += 1; expected.release += 1; - // Release passed-in `Id` - expected.release += 1; - expected.assert_current(); drop(old); @@ -138,9 +163,9 @@ fn test_clear() { #[test] fn test_remove_clear_release_dealloc() { - let mut dict = NSMutableDictionary::new(); + let mut dict = >::new(); for i in 0..4 { - dict.insert(NSNumber::new_i32(i), __RcTestObject::new()); + dict.insert_id(&NSNumber::new_i32(i), __RcTestObject::new()); } let mut expected = __ThreadTestData::current(); @@ -165,9 +190,9 @@ fn test_remove_clear_release_dealloc() { #[test] #[cfg(feature = "Foundation_NSArray")] -fn test_into_values_array() { +fn test_to_array() { let dict = sample_dict(); - let array = NSMutableDictionary::into_values_array(dict); + let array = dict.to_array(); assert_eq!(array.len(), 3); } diff --git a/crates/icrate/tests/mutable_set.rs b/crates/icrate/tests/mutable_set.rs index ba47c3310..da18d0266 100644 --- a/crates/icrate/tests/mutable_set.rs +++ b/crates/icrate/tests/mutable_set.rs @@ -10,9 +10,9 @@ fn test_insert() { let mut set = NSMutableSet::new(); assert!(set.is_empty()); - assert!(set.insert(NSString::from_str("one"))); - assert!(!set.insert(NSString::from_str("one"))); - assert!(set.insert(NSString::from_str("two"))); + assert!(set.insert_id(NSString::from_str("one"))); + assert!(!set.insert_id(NSString::from_str("one"))); + assert!(set.insert_id(NSString::from_str("two"))); } #[test] @@ -69,7 +69,7 @@ fn test_mutable_copy() { let set1 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str)); let mut set2 = set1.mutableCopy(); - set2.insert(NSString::from_str("four")); + set2.insert_id(NSString::from_str("four")); assert!(set1.is_subset(&set2)); assert_ne!(set1.mutableCopy(), set2); @@ -77,34 +77,28 @@ fn test_mutable_copy() { #[test] fn test_insert_retain_release() { - let mut set = NSMutableSet::new(); + let mut set = >::new(); let obj1 = __RcTestObject::new(); let obj2 = __RcTestObject::new(); let obj2_copy = obj2.retain(); let mut expected = __ThreadTestData::current(); - set.insert(obj1); + set.insert(&obj1); // Retain to store in set expected.retain += 1; - // Release passed in object - expected.release += 1; expected.assert_current(); assert_eq!(set.len(), 1); assert_eq!(set.get_any(), set.get_any()); - set.insert(obj2); + set.insert(&obj2); // Retain to store in set expected.retain += 1; - // Release passed in object - expected.release += 1; expected.assert_current(); assert_eq!(set.len(), 2); - set.insert(obj2_copy); + set.insert(&obj2_copy); // No retain, since the object is already in the set expected.retain += 0; - // Release passed in object - expected.release += 1; expected.assert_current(); assert_eq!(set.len(), 2); } @@ -113,7 +107,7 @@ fn test_insert_retain_release() { fn test_clear_release_dealloc() { let mut set = NSMutableSet::new(); for _ in 0..4 { - set.insert(__RcTestObject::new()); + set.insert_id(__RcTestObject::new()); } let mut expected = __ThreadTestData::current(); diff --git a/crates/icrate/tests/set.rs b/crates/icrate/tests/set.rs index 5979bf77e..5f3d5ca6a 100644 --- a/crates/icrate/tests/set.rs +++ b/crates/icrate/tests/set.rs @@ -4,7 +4,7 @@ use objc2::rc::{__RcTestObject, __ThreadTestData}; -use icrate::Foundation::{self, ns_string, NSNumber, NSObject, NSSet, NSString}; +use icrate::Foundation::{self, ns_string, NSNumber, NSSet, NSString}; #[test] fn test_new() { @@ -48,7 +48,11 @@ fn test_len() { let set = NSSet::from_id_slice(&["one", "two", "two"].map(NSString::from_str)); assert_eq!(set.len(), 2); - let set = NSSet::from_vec(vec![NSObject::new(), NSObject::new(), NSObject::new()]); + let set = NSSet::from_vec(vec![ + NSNumber::new_i32(1), + NSNumber::new_i32(2), + NSNumber::new_i32(3), + ]); assert_eq!(set.len(), 3); } diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index b2c01daca..c458ad14e 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -7,8 +7,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD ### Added -* Added `mutability::IsMainThreadOnly`. -* Added `mutability::CounterpartOrSelf`. +* Added the following traits to the `mutability` module (see the documentation + for motivation and usage info): + - `HasStableHash`. + - `IsMainThreadOnly`. + - `CounterpartOrSelf`. * Added new `encode` traits `EncodeReturn`, `EncodeArgument` and `EncodeArguments`. diff --git a/crates/objc2/src/mutability.rs b/crates/objc2/src/mutability.rs index 0138a1e21..799189084 100644 --- a/crates/objc2/src/mutability.rs +++ b/crates/objc2/src/mutability.rs @@ -136,10 +136,17 @@ pub struct ImmutableWithMutableSubclass { /// Marker type for mutable classes that have a immutable counterpart. /// -/// This is effectively the same as [`Mutable`], except that the immutable +/// This is effectively the same as [`Mutable`], except for the immutable /// counterpart being be specified as the type parameter `IS` to allow /// `NSCopying` and `NSMutableCopying` to return the correct type. /// +/// Functionality that is provided with this: +/// - [`IsAllocableAnyThread`] -> [`ClassType::alloc`]. +/// - [`IsMutable`] -> [`impl DerefMut for Id`][crate::rc::Id#impl-DerefMut-for-Id]. +/// - You are allowed to hand out pointers / references to an instance's +/// internal data, since you know such data will never be mutated without +/// the borrowchecker catching it. +/// /// /// # Example /// @@ -201,6 +208,11 @@ pub struct InteriorMutable { /// /// It is unsound to implement [`Send`] or [`Sync`] on a type with this /// mutability. +/// +/// Functionality that is provided with this: +/// - [`IsRetainable`] -> [`ClassType::retain`]. +/// - [`IsIdCloneable`] -> [`Id::clone`][crate::rc::Id#impl-Clone-for-Id]. +/// - [`IsMainThreadOnly`] -> `MainThreadMarker::from`. // // While Xcode's Main Thread Checker doesn't report `alloc` and `dealloc` as // unsafe from other threads, things like `NSView` and `NSWindow` still do a @@ -253,6 +265,12 @@ mod private { pub trait MutabilityIsMainThreadOnly: Mutability {} impl MutabilityIsMainThreadOnly for MainThreadOnly {} + pub trait MutabilityHashIsStable: Mutability {} + impl MutabilityHashIsStable for Immutable {} + impl MutabilityHashIsStable for Mutable {} + impl MutabilityHashIsStable for ImmutableWithMutableSubclass {} + impl MutabilityHashIsStable for MutableWithImmutableSuperclass {} + pub trait MutabilityCounterpartOrSelf: Mutability { type Immutable: ?Sized + ClassType; type Mutable: ?Sized + ClassType; @@ -295,9 +313,6 @@ mod private { type Immutable = T; type Mutable = T; } - - // TODO: Trait for objects whose `hash` is guaranteed to never change, - // which allows it to be used as a key in `NSDictionary`. } /// Marker trait for the different types of mutability a class can have. @@ -392,6 +407,26 @@ impl IsMainThreadOnly for T where { } +/// Marker trait for classes whose `hash` and `isEqual:` methods are stable. +/// +/// This is useful for hashing collection types like `NSDictionary` and +/// `NSSet` which require that their keys never change. +/// +/// This is implemented for classes whose [`ClassType::Mutability`] is one of: +/// - [`Immutable`]. +/// - [`Mutable`]. +/// - [`ImmutableWithMutableSubclass`]. +/// - [`MutableWithImmutableSuperclass`]. +/// +/// Since all of these do not use interior mutability, and since the `hash` +/// and `isEqual:` methods are required to not use external sources like +/// thread locals or randomness to determine their result, we can guarantee +/// that the hash is stable for these types. +// +// TODO: Exclude generic types like `NSArray` from this! +pub trait HasStableHash: ClassType {} +impl HasStableHash for T where T::Mutability: private::MutabilityHashIsStable {} + /// Retrieve the immutable/mutable counterpart class, and fall back to `Self` /// if not applicable. /// diff --git a/crates/objc2/src/rc/test_object.rs b/crates/objc2/src/rc/test_object.rs index 339a8b0ae..2faed6de2 100644 --- a/crates/objc2/src/rc/test_object.rs +++ b/crates/objc2/src/rc/test_object.rs @@ -60,7 +60,7 @@ std::thread_local! { declare_class!( /// A helper object that counts how many times various reference-counting /// primitives are called. - #[derive(Debug, PartialEq, Eq)] + #[derive(Debug, PartialEq, Eq, Hash)] #[doc(hidden)] pub struct __RcTestObject; diff --git a/crates/objc2/src/runtime/nsobject.rs b/crates/objc2/src/runtime/nsobject.rs index 549c330b0..4b2570818 100644 --- a/crates/objc2/src/runtime/nsobject.rs +++ b/crates/objc2/src/runtime/nsobject.rs @@ -102,6 +102,8 @@ unsafe impl ClassType for NSObject { /// that implements the `NSObject` protocol. #[allow(non_snake_case)] pub unsafe trait NSObjectProtocol { + // Note: This method must remain `unsafe` to override, since hashing + // collections like `NSDictionary` and `NSSet` rely on it being stable. #[doc(hidden)] fn __isEqual(&self, other: &Self) -> bool where @@ -110,6 +112,8 @@ pub unsafe trait NSObjectProtocol { unsafe { msg_send![self, isEqual: other] } } + // Note: This method must remain `unsafe` to override, since hashing + // collections like `NSDictionary` and `NSSet` rely on it being stable. #[doc(hidden)] fn __hash(&self) -> usize where diff --git a/crates/test-ui/Cargo.toml b/crates/test-ui/Cargo.toml index 244b0b684..a61d07a3e 100644 --- a/crates/test-ui/Cargo.toml +++ b/crates/test-ui/Cargo.toml @@ -21,6 +21,7 @@ default = [ "icrate/Foundation_NSArray", "icrate/Foundation_NSMutableArray", "icrate/Foundation_NSValue", + "icrate/Foundation_NSSet", "objc2/unstable-msg-send-always-comma", ] std = ["block2/std", "objc2/std", "icrate/std"] diff --git a/crates/test-ui/ui/msg_send_invalid_error.stderr b/crates/test-ui/ui/msg_send_invalid_error.stderr index be3b2a06e..00ee6e294 100644 --- a/crates/test-ui/ui/msg_send_invalid_error.stderr +++ b/crates/test-ui/ui/msg_send_invalid_error.stderr @@ -42,7 +42,7 @@ error[E0277]: the trait bound `i32: Message` is not satisfied NSMutableArray NSDictionary NSMutableDictionary - NSEnumerator + NSSet and $N others note: required by a bound in `__send_message_error` --> $WORKSPACE/crates/objc2/src/message/mod.rs diff --git a/crates/test-ui/ui/nsset_from_nsobject.rs b/crates/test-ui/ui/nsset_from_nsobject.rs new file mode 100644 index 000000000..955e2a08a --- /dev/null +++ b/crates/test-ui/ui/nsset_from_nsobject.rs @@ -0,0 +1,6 @@ +//! Test that `NSSet` can't be created from types with an unknown stable hash. +use icrate::Foundation::{NSObject, NSSet}; + +fn main() { + let _ = NSSet::from_vec(vec![NSObject::new()]); +} diff --git a/crates/test-ui/ui/nsset_from_nsobject.stderr b/crates/test-ui/ui/nsset_from_nsobject.stderr new file mode 100644 index 000000000..3fb60164a --- /dev/null +++ b/crates/test-ui/ui/nsset_from_nsobject.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `Root: mutability::private::MutabilityHashIsStable` is not satisfied + --> ui/nsset_from_nsobject.rs + | + | let _ = NSSet::from_vec(vec![NSObject::new()]); + | --------------- ^^^^^^^^^^^^^^^^^^^^^ the trait `mutability::private::MutabilityHashIsStable` is not implemented for `Root` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `mutability::private::MutabilityHashIsStable`: + Immutable + Mutable + ImmutableWithMutableSubclass + MutableWithImmutableSuperclass + = note: required for `NSObject` to implement `HasStableHash` +note: required by a bound in `set::>::from_vec` + --> $WORKSPACE/crates/icrate/src/generated/Foundation/../../additions/Foundation/set.rs + | + | pub fn from_vec(mut vec: Vec>) -> Id + | -------- required by a bound in this associated function + | where + | T: HasStableHash, + | ^^^^^^^^^^^^^ required by this bound in `set::>::from_vec` From b5b79aabaacf60e4d68103f34a24fcc0775a1940 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 7 Sep 2023 10:52:02 +0200 Subject: [PATCH 4/5] Make a few more types immutable, and hence allowed in NSSet/NSDictionary --- crates/header-translator/src/data/AppKit.rs | 4 ++++ crates/header-translator/src/data/CloudKit.rs | 2 ++ crates/header-translator/src/data/Foundation.rs | 2 ++ crates/icrate/src/generated | 2 +- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/header-translator/src/data/AppKit.rs b/crates/header-translator/src/data/AppKit.rs index fab0b72b4..57bc56ac4 100644 --- a/crates/header-translator/src/data/AppKit.rs +++ b/crates/header-translator/src/data/AppKit.rs @@ -250,4 +250,8 @@ data! { // `addChildWindow:ordered:` is not safe, as cycles must be prevented } + + class NSTouch: Immutable {} + + class NSUserInterfaceCompressionOptions: Immutable {} } diff --git a/crates/header-translator/src/data/CloudKit.rs b/crates/header-translator/src/data/CloudKit.rs index 89a871f29..4588cc7f1 100644 --- a/crates/header-translator/src/data/CloudKit.rs +++ b/crates/header-translator/src/data/CloudKit.rs @@ -1,2 +1,4 @@ data! { + class CKRecordID: Immutable {} + class CKRecordZoneID: Immutable {} } diff --git a/crates/header-translator/src/data/Foundation.rs b/crates/header-translator/src/data/Foundation.rs index 928fc67fb..40f0b20e3 100644 --- a/crates/header-translator/src/data/Foundation.rs +++ b/crates/header-translator/src/data/Foundation.rs @@ -241,4 +241,6 @@ data! { class NSURLRequest: ImmutableWithMutableSubclass {} class NSMutableURLRequest: MutableWithImmutableSuperclass {} + + class NSIndexPath: Immutable {} } diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated index 560cb6a40..316a9c6f9 160000 --- a/crates/icrate/src/generated +++ b/crates/icrate/src/generated @@ -1 +1 @@ -Subproject commit 560cb6a40f078eed5a6e1af49fdd5299b1ad0ba1 +Subproject commit 316a9c6f9640f34f859c0d9bf0da8973f03a3a00 From 7c9ff462806f4a2bbb744a281af1141f174add6a Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 10 Sep 2023 16:51:09 +0200 Subject: [PATCH 5/5] Add test for broken behaviour --- crates/icrate/tests/set.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/icrate/tests/set.rs b/crates/icrate/tests/set.rs index 5f3d5ca6a..fe4a5fbd4 100644 --- a/crates/icrate/tests/set.rs +++ b/crates/icrate/tests/set.rs @@ -294,3 +294,16 @@ fn test_iter_minimal_retains() { expected.dealloc += 1; expected.assert_current(); } + +/// This currently works, but we should figure out a way to disallow it! +#[test] +#[cfg(all(feature = "Foundation_NSArray", feature = "Foundation_NSConnection"))] +#[allow(deprecated)] +fn invalid_generic() { + let something_interior_mutable = unsafe { Foundation::NSConnection::defaultConnection() }; + let set = NSSet::from_id_slice(&[Foundation::NSArray::from_id_slice(&[ + something_interior_mutable, + ])]); + let _ = set.get_any().unwrap().get(0).unwrap(); + // something_interior_mutable.setAbc(...) +}