From 2cbf5b4a4cc9cca601e4f1f1b33e076bdaa888a9 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 13:17:41 +0100 Subject: [PATCH 01/12] Rename Object::get_[mut]_ivar methods Follow guideline: Getter names follow Rust convention (C-GETTER) --- .../examples/class_with_lifetime.rs | 4 ++-- objc2-foundation/examples/custom_class.rs | 4 ++-- objc2/CHANGELOG.md | 5 +++++ objc2/README.md | 2 +- objc2/examples/introspection.rs | 2 +- objc2/src/declare.rs | 2 +- objc2/src/runtime.rs | 22 ++++++++++++------- objc2/src/test_utils.rs | 2 +- 8 files changed, 27 insertions(+), 16 deletions(-) diff --git a/objc2-foundation/examples/class_with_lifetime.rs b/objc2-foundation/examples/class_with_lifetime.rs index 962f90461..4c78d038c 100644 --- a/objc2-foundation/examples/class_with_lifetime.rs +++ b/objc2-foundation/examples/class_with_lifetime.rs @@ -35,14 +35,14 @@ impl<'a> MyObject<'a> { fn get(&self) -> Option<&'a u8> { unsafe { let obj = &*(self as *const _ as *const Object); - *obj.get_ivar("_number_ptr") + *obj.ivar("_number_ptr") } } fn write(&mut self, number: u8) { let ptr: &mut Option<&'a mut u8> = unsafe { let obj = &mut *(self as *mut _ as *mut Object); - obj.get_mut_ivar("_number_ptr") + obj.ivar_mut("_number_ptr") }; if let Some(ptr) = ptr { **ptr = number; diff --git a/objc2-foundation/examples/custom_class.rs b/objc2-foundation/examples/custom_class.rs index 2c9d1f28a..5acd1b616 100644 --- a/objc2-foundation/examples/custom_class.rs +++ b/objc2-foundation/examples/custom_class.rs @@ -31,7 +31,7 @@ impl MYObject { fn number(&self) -> u32 { unsafe { let obj = &*(self as *const _ as *const Object); - *obj.get_ivar("_number") + *obj.ivar("_number") } } @@ -62,7 +62,7 @@ unsafe impl INSObject for MYObject { } extern "C" fn my_object_get_number(this: &Object, _cmd: Sel) -> u32 { - unsafe { *this.get_ivar("_number") } + unsafe { *this.ivar("_number") } } unsafe { diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 8bcc68dcb..1ae92ff70 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Changed +* **BREAKING**: Renamed: + - `Object::get_ivar` -> `Object::ivar` + - `Object::get_mut_ivar` -> `Object::ivar_mut` + ## 0.3.0-alpha.5 - 2021-12-22 diff --git a/objc2/README.md b/objc2/README.md index cd2bda13e..6ab78e00a 100644 --- a/objc2/README.md +++ b/objc2/README.md @@ -80,7 +80,7 @@ decl.add_ivar::("_number"); // Add an ObjC method for getting the number extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 { - unsafe { *this.get_ivar("_number") } + unsafe { *this.ivar("_number") } } unsafe { decl.add_method(sel!(number), diff --git a/objc2/examples/introspection.rs b/objc2/examples/introspection.rs index 900b0e51e..904f77f40 100644 --- a/objc2/examples/introspection.rs +++ b/objc2/examples/introspection.rs @@ -29,7 +29,7 @@ fn main() { println!("NSObject address: {:p}", obj); // Access an ivar of the object - let isa: *const Class = unsafe { *obj.get_ivar("isa") }; + let isa: *const Class = unsafe { *obj.ivar("isa") }; println!("NSObject isa: {:?}", isa); #[cfg(feature = "malloc")] diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index 9bdb02f3a..d55578a1c 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -21,7 +21,7 @@ decl.add_ivar::("_number"); // Add an ObjC method for getting the number extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 { - unsafe { *this.get_ivar("_number") } + unsafe { *this.ivar("_number") } } unsafe { decl.add_method(sel!(number), diff --git a/objc2/src/runtime.rs b/objc2/src/runtime.rs index 3a3e388d9..bad885609 100644 --- a/objc2/src/runtime.rs +++ b/objc2/src/runtime.rs @@ -502,7 +502,7 @@ impl UnwindSafe for Protocol {} impl RefUnwindSafe for Protocol {} // Note that Unpin is not applicable. -fn get_ivar_offset(cls: &Class, name: &str) -> isize { +fn ivar_offset(cls: &Class, name: &str) -> isize { match cls.instance_variable(name) { Some(ivar) => { assert!(T::ENCODING.equivalent_to_str(ivar.type_encoding())); @@ -517,7 +517,7 @@ impl Object { self as *const Self as *const _ } - /// Returns the class of this object. + /// Dynamically find the class of this object. pub fn class(&self) -> &Class { unsafe { &*(ffi::object_getClass(self.as_ptr()) as *const Class) } } @@ -532,8 +532,10 @@ impl Object { /// # Safety /// /// The caller must ensure that the ivar is actually of type `T`. - pub unsafe fn get_ivar(&self, name: &str) -> &T { - let offset = get_ivar_offset::(self.class(), name); + /// + /// Library implementors should expose a safe interface to the ivar. + pub unsafe fn ivar(&self, name: &str) -> &T { + let offset = ivar_offset::(self.class(), name); // `offset` is given in bytes, so we convert to `u8` let ptr = self as *const Self as *const u8; let ptr = unsafe { ptr.offset(offset) } as *const T; @@ -550,8 +552,10 @@ impl Object { /// # Safety /// /// The caller must ensure that the ivar is actually of type `T`. - pub unsafe fn get_mut_ivar(&mut self, name: &str) -> &mut T { - let offset = get_ivar_offset::(self.class(), name); + /// + /// Library implementors should expose a safe interface to the ivar. + pub unsafe fn ivar_mut(&mut self, name: &str) -> &mut T { + let offset = ivar_offset::(self.class(), name); // `offset` is given in bytes, so we convert to `u8` let ptr = self as *mut Self as *mut u8; let ptr = unsafe { ptr.offset(offset) } as *mut T; @@ -568,9 +572,11 @@ impl Object { /// # Safety /// /// The caller must ensure that the ivar is actually of type `T`. + /// + /// Library implementors should expose a safe interface to the ivar. pub unsafe fn set_ivar(&mut self, name: &str, value: T) { // SAFETY: Invariants upheld by caller - unsafe { *self.get_mut_ivar::(name) = value }; + unsafe { *self.ivar_mut::(name) = value }; } // objc_setAssociatedObject @@ -713,7 +719,7 @@ mod tests { assert_eq!(obj.class(), test_utils::custom_class()); let result: u32 = unsafe { obj.set_ivar("_foo", 4u32); - *obj.get_ivar("_foo") + *obj.ivar("_foo") }; assert_eq!(result, 4); } diff --git a/objc2/src/test_utils.rs b/objc2/src/test_utils.rs index b494371bf..12f44d82a 100644 --- a/objc2/src/test_utils.rs +++ b/objc2/src/test_utils.rs @@ -88,7 +88,7 @@ pub(crate) fn custom_class() -> &'static Class { } extern "C" fn custom_obj_get_foo(this: &Object, _cmd: Sel) -> u32 { - unsafe { *this.get_ivar::("_foo") } + unsafe { *this.ivar::("_foo") } } extern "C" fn custom_obj_get_struct(_this: &Object, _cmd: Sel) -> CustomStruct { From 04d13eeebaf47e8ca27c24b5742059fcb8f17e75 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 13:26:41 +0100 Subject: [PATCH 02/12] Rename NSFastEnumerator::enumerator Follow guideline: Methods on collections that produce iterators follow iter, iter_mut, into_iter (C-ITER) --- objc2-foundation/CHANGELOG.md | 4 ++++ objc2-foundation/src/enumerator.rs | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md index ceae11fe2..bbeab27c9 100644 --- a/objc2-foundation/CHANGELOG.md +++ b/objc2-foundation/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Changed +* **BREAKING**: Renamed `INSFastEnumeration::enumerator` to + `INSFastEnumeration::iter_fast`. + ## 0.2.0-alpha.3 - 2021-12-22 diff --git a/objc2-foundation/src/enumerator.rs b/objc2-foundation/src/enumerator.rs index 32bcba0f7..f54b3fb9d 100644 --- a/objc2-foundation/src/enumerator.rs +++ b/objc2-foundation/src/enumerator.rs @@ -44,7 +44,7 @@ impl<'a, T: INSObject> Iterator for NSEnumerator<'a, T> { pub unsafe trait INSFastEnumeration: INSObject { type Item: INSObject; - fn enumerator(&self) -> NSFastEnumerator<'_, Self> { + fn iter_fast(&self) -> NSFastEnumerator<'_, Self> { NSFastEnumerator::new(self) } } @@ -212,10 +212,10 @@ mod tests { let vec = (0u32..4).map(NSValue::new).collect(); let array = NSArray::from_vec(vec); - let enumerator = array.enumerator(); + let enumerator = array.iter_fast(); assert_eq!(enumerator.count(), 4); - let enumerator = array.enumerator(); + let enumerator = array.iter_fast(); assert!(enumerator.enumerate().all(|(i, obj)| obj.get() == i as u32)); } } From 166da58e60d5b0736dfc3f6815b9860b5777abf0 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 13:39:29 +0100 Subject: [PATCH 03/12] Implement common traits for types Follow: Types eagerly implement common traits (C-COMMON-TRAITS) --- objc2-encode/CHANGELOG.md | 3 ++ objc2-encode/src/encoding.rs | 2 +- objc2-foundation/CHANGELOG.md | 5 ++ objc2-foundation/src/array.rs | 20 +++++++- objc2-foundation/src/comparison_result.rs | 2 +- objc2-foundation/src/data.rs | 20 +++++++- objc2-foundation/src/dictionary.rs | 11 ++++- objc2-foundation/src/object.rs | 11 ++++- objc2-foundation/src/string.rs | 10 ++++ objc2-foundation/src/value.rs | 32 +++++++++++- objc2/CHANGELOG.md | 6 +++ objc2/src/declare.rs | 2 + objc2/src/message/mod.rs | 3 +- objc2/src/rc/autorelease.rs | 8 +++ objc2/src/runtime.rs | 59 ++++++++++++++--------- 15 files changed, 163 insertions(+), 31 deletions(-) diff --git a/objc2-encode/CHANGELOG.md b/objc2-encode/CHANGELOG.md index 3588b2036..fc8cfb72d 100644 --- a/objc2-encode/CHANGELOG.md +++ b/objc2-encode/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Added +* Implement `Hash` for `Encoding`. + ## 2.0.0-beta.1 - 2021-12-22 diff --git a/objc2-encode/src/encoding.rs b/objc2-encode/src/encoding.rs index f52791018..e8edd4e2b 100644 --- a/objc2-encode/src/encoding.rs +++ b/objc2-encode/src/encoding.rs @@ -15,7 +15,7 @@ use crate::parse; /// /// [ocrtTypeEncodings]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html /// [clang-src]: https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850 -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] // Maybe we're missing some encodings? pub enum Encoding<'a> { /// A C `char`. Corresponds to the `c` code. diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md index bbeab27c9..d124d6f0f 100644 --- a/objc2-foundation/CHANGELOG.md +++ b/objc2-foundation/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Added +* Implement `PartialOrd` and `Ord` for `NSComparisonResult` and `NSValue`. +* Implement `fmt::Display` for `NSValue`. +* Implement `DefaultId` for relevant objects. + ### Changed * **BREAKING**: Renamed `INSFastEnumeration::enumerator` to `INSFastEnumeration::iter_fast`. diff --git a/objc2-foundation/src/array.rs b/objc2-foundation/src/array.rs index 097fe8c6d..bdce02e81 100644 --- a/objc2-foundation/src/array.rs +++ b/objc2-foundation/src/array.rs @@ -6,7 +6,7 @@ use core::ops::{Index, Range}; use core::ptr::NonNull; use objc2::msg_send; -use objc2::rc::{Id, Owned, Ownership, Shared, SliceId}; +use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; use objc2::runtime::Object; use super::{ @@ -229,6 +229,15 @@ impl Index for NSArray { } } +impl DefaultId for NSArray { + type Ownership = O; + + #[inline] + fn default_id() -> Id { + Self::new() + } +} + pub unsafe trait INSMutableArray: INSArray { #[doc(alias = "addObject:")] fn push(&mut self, obj: Id) { @@ -378,6 +387,15 @@ impl Index for NSMutableArray { } } +impl DefaultId for NSMutableArray { + type Ownership = Owned; + + #[inline] + fn default_id() -> Id { + Self::new() + } +} + #[cfg(test)] mod tests { use alloc::format; diff --git a/objc2-foundation/src/comparison_result.rs b/objc2-foundation/src/comparison_result.rs index 1c6f1ec94..52342c5cf 100644 --- a/objc2-foundation/src/comparison_result.rs +++ b/objc2-foundation/src/comparison_result.rs @@ -3,7 +3,7 @@ use core::cmp::Ordering; use objc2::{Encode, Encoding, RefEncode}; #[repr(isize)] // NSInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum NSComparisonResult { Ascending = -1, Same = 0, diff --git a/objc2-foundation/src/data.rs b/objc2-foundation/src/data.rs index aed26318b..704daaf19 100644 --- a/objc2-foundation/src/data.rs +++ b/objc2-foundation/src/data.rs @@ -6,7 +6,7 @@ use core::{ffi::c_void, ptr::NonNull}; use super::{INSCopying, INSMutableCopying, INSObject, NSRange}; use objc2::msg_send; -use objc2::rc::{Id, Owned, Ownership, Shared}; +use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared}; pub unsafe trait INSData: INSObject { type Ownership: Ownership; @@ -111,6 +111,15 @@ unsafe impl INSMutableCopying for NSData { type Output = NSMutableData; } +impl DefaultId for NSData { + type Ownership = Shared; + + #[inline] + fn default_id() -> Id { + Self::new() + } +} + impl Deref for NSData { type Target = [u8]; @@ -180,6 +189,15 @@ unsafe impl INSMutableCopying for NSMutableData { type Output = NSMutableData; } +impl DefaultId for NSMutableData { + type Ownership = Owned; + + #[inline] + fn default_id() -> Id { + Self::new() + } +} + impl Deref for NSMutableData { type Target = [u8]; diff --git a/objc2-foundation/src/dictionary.rs b/objc2-foundation/src/dictionary.rs index ab60e6da6..e693ca596 100644 --- a/objc2-foundation/src/dictionary.rs +++ b/objc2-foundation/src/dictionary.rs @@ -5,7 +5,7 @@ use core::ops::Index; use core::ptr::{self, NonNull}; use objc2::msg_send; -use objc2::rc::{Id, Owned, Ownership, Shared, SliceId}; +use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; use super::{INSCopying, INSFastEnumeration, INSObject, NSArray, NSEnumerator}; @@ -153,6 +153,15 @@ impl NSDictionary { unsafe_def_fn!(pub fn new -> Shared); } +impl DefaultId for NSDictionary { + type Ownership = Shared; + + #[inline] + fn default_id() -> Id { + Self::new() + } +} + unsafe impl INSDictionary for NSDictionary { type Key = K; type Value = V; diff --git a/objc2-foundation/src/object.rs b/objc2-foundation/src/object.rs index 049fb5c8c..290c4d761 100644 --- a/objc2-foundation/src/object.rs +++ b/objc2-foundation/src/object.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; use core::ptr::NonNull; use objc2::msg_send; -use objc2::rc::{Id, Owned, Shared}; +use objc2::rc::{DefaultId, Id, Owned, Shared}; use objc2::runtime::{Bool, Class, Object}; use objc2::Message; @@ -55,6 +55,15 @@ impl NSObject { unsafe_def_fn!(pub fn new -> Owned); } +impl DefaultId for NSObject { + type Ownership = Owned; + + #[inline] + fn default_id() -> Id { + Self::new() + } +} + #[cfg(test)] mod tests { use super::{INSObject, NSObject}; diff --git a/objc2-foundation/src/string.rs b/objc2-foundation/src/string.rs index c3570d07e..64635b77c 100644 --- a/objc2-foundation/src/string.rs +++ b/objc2-foundation/src/string.rs @@ -8,6 +8,7 @@ use std::os::raw::c_char; use alloc::borrow::ToOwned; use objc2::ffi; use objc2::msg_send; +use objc2::rc::DefaultId; use objc2::rc::{autoreleasepool, AutoreleasePool}; use objc2::rc::{Id, Shared}; @@ -111,6 +112,15 @@ impl NSString { unsafe_def_fn!(pub fn new -> Shared); } +impl DefaultId for NSString { + type Ownership = Shared; + + #[inline] + fn default_id() -> Id { + Self::new() + } +} + unsafe impl INSString for NSString {} unsafe impl INSCopying for NSString { diff --git a/objc2-foundation/src/value.rs b/objc2-foundation/src/value.rs index e886a1d5e..a537fa5c3 100644 --- a/objc2-foundation/src/value.rs +++ b/objc2-foundation/src/value.rs @@ -1,14 +1,15 @@ use alloc::string::ToString; +use core::cmp::Ordering; use core::ffi::c_void; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::ptr::NonNull; -use core::str; +use core::{fmt, str}; use std::ffi::{CStr, CString}; use std::os::raw::c_char; use objc2::msg_send; -use objc2::rc::{Id, Shared}; +use objc2::rc::{DefaultId, Id, Shared}; use objc2::Encode; use super::{INSCopying, INSObject}; @@ -91,6 +92,33 @@ unsafe impl INSCopying for NSValue { type Output = NSValue; } +impl Ord for NSValue { + fn cmp(&self, other: &Self) -> Ordering { + self.get().cmp(&other.get()) + } +} + +impl PartialOrd for NSValue { + fn partial_cmp(&self, other: &Self) -> Option { + self.get().partial_cmp(&other.get()) + } +} + +impl DefaultId for NSValue { + type Ownership = Shared; + + #[inline] + fn default_id() -> Id { + Self::new(Default::default()) + } +} + +impl fmt::Display for NSValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + /// ```compile_fail /// use objc2_foundation::NSValue; /// fn needs_eq() {} diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 1ae92ff70..16b75c2f6 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Added +* Implement `Hash` for `Sel`, `Ivar`, `Class`, `Method` and `MessageError`. +* Implement `PartialEq` and `Eq` for `Ivar`, `Method` and `MessageError`. +* Implement `fmt::Pointer` for `Sel` and `rc::AutoreleasePool`. +* Implement `fmt::Debug` for `ClassDecl`, `ProtocolDecl` and `rc::AutoreleasePool`. + ### Changed * **BREAKING**: Renamed: - `Object::get_ivar` -> `Object::ivar` diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index d55578a1c..284e374d9 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -116,6 +116,7 @@ fn log2_align_of() -> u8 { /// A type for declaring a new class and adding new methods and ivars to it /// before registering it. +#[derive(Debug)] pub struct ClassDecl { cls: *mut Class, } @@ -306,6 +307,7 @@ impl Drop for ClassDecl { /// A type for declaring a new protocol and adding new methods to it /// before registering it. +#[derive(Debug)] pub struct ProtocolDecl { proto: *mut Protocol, } diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index 8c994875f..59b8f0dd7 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -345,7 +345,8 @@ message_args_impl!( /// enabled /// - the encodings of the arguments do not match the encoding of the method /// and the `verify_message` feature is enabled -#[derive(Debug)] +// Currently not Clone for future compat +#[derive(Debug, PartialEq, Eq, Hash)] pub struct MessageError(String); impl fmt::Display for MessageError { diff --git a/objc2/src/rc/autorelease.rs b/objc2/src/rc/autorelease.rs index f22e3bc5e..bf59cb607 100644 --- a/objc2/src/rc/autorelease.rs +++ b/objc2/src/rc/autorelease.rs @@ -1,5 +1,6 @@ use core::cell::UnsafeCell; use core::ffi::c_void; +use core::fmt; use core::marker::PhantomData; #[cfg(all(debug_assertions, not(feature = "unstable_autoreleasesafe")))] use std::{cell::RefCell, thread_local, vec::Vec}; @@ -15,6 +16,7 @@ use crate::ffi; /// /// And this is not [`Sync`], since you can only autorelease a reference to a /// pool on the current thread. +#[derive(Debug)] pub struct AutoreleasePool { context: *mut c_void, // May pointer to data that is mutated (even though we hold shared access) @@ -160,6 +162,12 @@ impl Drop for AutoreleasePool { } } +impl fmt::Pointer for AutoreleasePool { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Pointer::fmt(&self.context, f) + } +} + /// We use a macro here so that the documentation is included whether the /// feature is enabled or not. #[cfg(not(feature = "unstable_autoreleasesafe"))] diff --git a/objc2/src/runtime.rs b/objc2/src/runtime.rs index bad885609..3ce337187 100644 --- a/objc2/src/runtime.rs +++ b/objc2/src/runtime.rs @@ -5,6 +5,7 @@ use core::ffi::c_void; use core::fmt; +use core::hash; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr; use core::str; @@ -32,6 +33,8 @@ pub const NO: ffi::BOOL = ffi::NO; /// A type that represents a method selector. #[repr(transparent)] +// ffi::sel_isEqual is just pointer comparison, so just generate PartialEq +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct Sel { ptr: *const ffi::objc_selector, } @@ -52,6 +55,30 @@ pub struct Class(ffi::objc_class); #[repr(C)] pub struct Protocol(ffi::Protocol); +macro_rules! standard_pointer_impls { + ($($name:ident),*) => { + $( + impl PartialEq for $name { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ptr() == other.as_ptr() + } + } + impl Eq for $name {} + impl hash::Hash for $name { + #[inline] + fn hash(&self, state: &mut H) { + self.as_ptr().hash(state) + } + } + )* + } +} + +// Implement PartialEq, Eq and Hash using pointer semantics; there's not +// really a better way to do it. +standard_pointer_impls!(Ivar, Method, Class); + /// A type that represents an instance of a class. /// /// Note: This is intentionally neither [`Sync`], [`Send`], [`UnwindSafe`], @@ -107,13 +134,9 @@ unsafe impl Encode for Sel { const ENCODING: Encoding<'static> = Encoding::Sel; } -impl PartialEq for Sel { - fn eq(&self, other: &Sel) -> bool { - self.ptr == other.ptr - } -} - -impl Eq for Sel {} +// RefEncode is not implemented for Sel, because there is literally no API +// that takes &Sel, but the user could easily get confused and accidentally +// attempt that. // SAFETY: Sel is immutable (and can be retrieved from any thread using the // `sel!` macro). @@ -122,17 +145,15 @@ unsafe impl Send for Sel {} impl UnwindSafe for Sel {} impl RefUnwindSafe for Sel {} -impl Copy for Sel {} - -impl Clone for Sel { - fn clone(&self) -> Self { - Self { ptr: self.ptr } +impl fmt::Debug for Sel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name()) } } -impl fmt::Debug for Sel { +impl fmt::Pointer for Sel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name()) + fmt::Pointer::fmt(&self.ptr, f) } } @@ -405,14 +426,6 @@ unsafe impl RefEncode for Class { const ENCODING_REF: Encoding<'static> = Encoding::Class; } -impl PartialEq for Class { - fn eq(&self, other: &Class) -> bool { - self.as_ptr() == other.as_ptr() - } -} - -impl Eq for Class {} - impl fmt::Debug for Class { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name()) @@ -477,6 +490,8 @@ impl Protocol { } impl PartialEq for Protocol { + /// Check whether the protocols are equal, or conform to each other. + #[inline] fn eq(&self, other: &Protocol) -> bool { unsafe { Bool::from_raw(ffi::protocol_isEqual(self.as_ptr(), other.as_ptr())).is_true() } } From 0eba2af9e366d6b8bfd8f52725ea639e3b1e158b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 2 Jan 2022 22:20:28 +0100 Subject: [PATCH 04/12] Use AsRef, AsMut and Index --- objc2-foundation/CHANGELOG.md | 2 ++ objc2-foundation/src/array.rs | 8 ++++- objc2-foundation/src/data.rs | 47 ++++++++++++++++++++++++++++-- objc2-foundation/src/dictionary.rs | 2 +- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md index d124d6f0f..9d549bf00 100644 --- a/objc2-foundation/CHANGELOG.md +++ b/objc2-foundation/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Implement `PartialOrd` and `Ord` for `NSComparisonResult` and `NSValue`. * Implement `fmt::Display` for `NSValue`. * Implement `DefaultId` for relevant objects. +* Implement `AsRef` and `Index` for `NSData` and `NSMutableData`. +* Implement `AsMut` and `IndexMut` for `NSMutableData`. ### Changed * **BREAKING**: Renamed `INSFastEnumeration::enumerator` to diff --git a/objc2-foundation/src/array.rs b/objc2-foundation/src/array.rs index bdce02e81..5ed39b4fb 100644 --- a/objc2-foundation/src/array.rs +++ b/objc2-foundation/src/array.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use core::cmp::Ordering; use core::ffi::c_void; use core::marker::PhantomData; -use core::ops::{Index, Range}; +use core::ops::{Index, IndexMut, Range}; use core::ptr::NonNull; use objc2::msg_send; @@ -387,6 +387,12 @@ impl Index for NSMutableArray { } } +impl IndexMut for NSMutableArray { + fn index_mut(&mut self, index: usize) -> &mut T { + self.get_mut(index).unwrap() + } +} + impl DefaultId for NSMutableArray { type Ownership = Owned; diff --git a/objc2-foundation/src/data.rs b/objc2-foundation/src/data.rs index 704daaf19..5a99c3d05 100644 --- a/objc2-foundation/src/data.rs +++ b/objc2-foundation/src/data.rs @@ -1,7 +1,7 @@ #[cfg(feature = "block")] use alloc::vec::Vec; -use core::ops::{Deref, DerefMut, Range}; -use core::slice; +use core::ops::{Deref, DerefMut, Index, IndexMut, Range}; +use core::slice::{self, SliceIndex}; use core::{ffi::c_void, ptr::NonNull}; use super::{INSCopying, INSMutableCopying, INSObject, NSRange}; @@ -111,6 +111,21 @@ unsafe impl INSMutableCopying for NSData { type Output = NSMutableData; } +impl AsRef<[u8]> for NSData { + fn as_ref(&self) -> &[u8] { + self.bytes() + } +} + +impl> Index for NSData { + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + Index::index(self.bytes(), index) + } +} + impl DefaultId for NSData { type Ownership = Shared; @@ -189,6 +204,34 @@ unsafe impl INSMutableCopying for NSMutableData { type Output = NSMutableData; } +impl AsRef<[u8]> for NSMutableData { + fn as_ref(&self) -> &[u8] { + self.bytes() + } +} + +impl AsMut<[u8]> for NSMutableData { + fn as_mut(&mut self) -> &mut [u8] { + self.bytes_mut() + } +} + +impl> Index for NSMutableData { + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + Index::index(self.bytes(), index) + } +} + +impl> IndexMut for NSMutableData { + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + IndexMut::index_mut(self.bytes_mut(), index) + } +} + impl DefaultId for NSMutableData { type Ownership = Owned; diff --git a/objc2-foundation/src/dictionary.rs b/objc2-foundation/src/dictionary.rs index e693ca596..62414191d 100644 --- a/objc2-foundation/src/dictionary.rs +++ b/objc2-foundation/src/dictionary.rs @@ -175,7 +175,7 @@ unsafe impl INSFastEnumeration for NSDictionary Index<&'a K> for NSDictionary { type Output = V; - fn index(&self, index: &K) -> &V { + fn index<'s>(&'s self, index: &'a K) -> &'s V { self.get(index).unwrap() } } From d1ca27d6481c7a05a24fff7569189c16ab4f1e33 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 18:52:31 +0100 Subject: [PATCH 05/12] Changed `global_block!` macro to take an optional semicolon at the end. Follow: Input syntax is evocative of the output (C-EVOCATIVE) --- block2/CHANGELOG.md | 3 +++ block2/src/global.rs | 39 ++++++++++++++++++++------------------- tests/src/lib.rs | 4 ++-- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/block2/CHANGELOG.md b/block2/CHANGELOG.md index 60b100c35..67aee288d 100644 --- a/block2/CHANGELOG.md +++ b/block2/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Changed +* Changed `global_block!` macro to take an optional semicolon at the end. + ## 0.2.0-alpha.2 - 2021-12-22 diff --git a/block2/src/global.rs b/block2/src/global.rs index b653d28ec..d2da2b280 100644 --- a/block2/src/global.rs +++ b/block2/src/global.rs @@ -99,8 +99,8 @@ where /// global_block! { /// static MY_BLOCK = || -> i32 { /// 42 -/// } -/// }; +/// }; +/// } /// assert_eq!(unsafe { MY_BLOCK.call(()) }, 42); /// ``` /// @@ -109,8 +109,8 @@ where /// global_block! { /// static ADDER_BLOCK = |x: i32, y: i32| -> i32 { /// x + y -/// } -/// }; +/// }; +/// } /// assert_eq!(unsafe { ADDER_BLOCK.call((5, 7)) }, 12); /// ``` /// @@ -119,8 +119,8 @@ where /// global_block! { /// pub static MUTATING_BLOCK = |x: &mut i32| { /// *x = *x + 42; -/// } -/// }; +/// }; +/// } /// let mut x = 5; /// unsafe { MUTATING_BLOCK.call((&mut x,)) }; /// assert_eq!(x, 47); @@ -131,8 +131,8 @@ where /// ```compile_fail /// use block2::global_block; /// global_block! { -/// pub static INVALID_BLOCK = |b: Box| {} -/// }; +/// pub static INVALID_BLOCK = |b: Box| {}; +/// } /// ``` /// /// [`Box`]: std::boxed::Box @@ -141,20 +141,23 @@ macro_rules! global_block { // `||` is parsed as one token ( $(#[$m:meta])* - $vis:vis static $name:ident = || $(-> $r:ty)? $body:block + $vis:vis static $name:ident = || $(-> $r:ty)? $body:block $(;)? ) => { - $crate::global_block!($(#[$m])* $vis static $name = |,| $(-> $r)? $body); + $crate::global_block!( + $(#[$m])* + $vis static $name = |,| $(-> $r)? $body + ); }; ( $(#[$m:meta])* - $vis:vis static $name:ident = |$($a:ident: $t:ty),* $(,)?| $(-> $r:ty)? $body:block + $vis:vis static $name:ident = |$($a:ident: $t:ty),* $(,)?| $(-> $r:ty)? $body:block $(;)? ) => { $(#[$m])* #[allow(unused_unsafe)] $vis static $name: $crate::GlobalBlock<($($t,)*) $(, $r)?> = unsafe { let mut layout = $crate::GlobalBlock::<($($t,)*) $(, $r)?>::__DEFAULT_LAYOUT; layout.isa = &$crate::ffi::_NSConcreteGlobalBlock as *const _ as *mut _; - layout.invoke = Some({ + layout.invoke = ::core::option::Option::Some({ unsafe extern "C" fn inner(_: *mut $crate::ffi::Block_layout, $($a: $t),*) $(-> $r)? { $body } @@ -172,7 +175,7 @@ macro_rules! global_block { mod tests { global_block! { /// Test comments and visibility - pub(super) static NOOP_BLOCK = || {} + pub(super) static NOOP_BLOCK = || {}; } global_block! { @@ -180,7 +183,7 @@ mod tests { #[allow(unused)] static BLOCK = |x: i32, y: i32, z: i32, w: i32,| -> i32 { x + y + z + w - } + }; } #[test] @@ -190,11 +193,9 @@ mod tests { #[test] fn test_defined_in_function() { - global_block!( - static MY_BLOCK = || -> i32 { - 42 - } - ); + global_block!(static MY_BLOCK = || -> i32 { + 42 + }); assert_eq!(unsafe { MY_BLOCK.call(()) }, 42); } } diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 9c16ca284..4c78fc72e 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -52,7 +52,7 @@ mod tests { /// Test `global_block` in an external crate static MY_BLOCK = || -> i32 { 42 - } + }; } #[test] @@ -120,7 +120,7 @@ mod tests { let mut data = data; data.mutate(); data - } + }; } let data = LargeStruct::get(); From fb897fc20dd8efaa446ec5343f39f9d10798a1cd Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 18:57:50 +0100 Subject: [PATCH 06/12] Document that global_block! cannot be a generic function Follow: Type fragments are flexible (C-MACRO-TY) --- block2/src/global.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/block2/src/global.rs b/block2/src/global.rs index d2da2b280..de81064a2 100644 --- a/block2/src/global.rs +++ b/block2/src/global.rs @@ -131,7 +131,18 @@ where /// ```compile_fail /// use block2::global_block; /// global_block! { -/// pub static INVALID_BLOCK = |b: Box| {}; +/// pub static BLOCK = |b: Box| {}; +/// } +/// ``` +/// +/// There is also no way to get a block function that's generic over it's +/// arguments. One could imagine the following syntax would work, but it can't +/// due to implementation limitations: +/// +/// ```compile_fail +/// use block2::global_block; +/// global_block! { +/// pub static BLOCK = |b: T| {}; /// } /// ``` /// From a8556a0477bfb7989f97b433c42dd4cc7ba9ad67 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 19:36:23 +0100 Subject: [PATCH 07/12] Use //! instead of /*! for module documentation Part of following: Crate level docs are thorough and include examples (C-CRATE-DOC) --- block2/src/lib.rs | 117 +++++++++++++++++++++--------------------- objc2/src/declare.rs | 65 ++++++++++++------------ objc2/src/lib.rs | 118 +++++++++++++++++++++---------------------- 3 files changed, 149 insertions(+), 151 deletions(-) diff --git a/block2/src/lib.rs b/block2/src/lib.rs index 3d253fb1d..354a8870f 100644 --- a/block2/src/lib.rs +++ b/block2/src/lib.rs @@ -1,62 +1,61 @@ -/*! -A Rust interface for Objective-C blocks. - -For more information on the specifics of the block implementation, see -Clang's documentation: - -# Invoking blocks - -The `Block` struct is used for invoking blocks from Objective-C. For example, -consider this Objective-C function: - -``` objc -int32_t sum(int32_t (^block)(int32_t, int32_t)) { - return block(5, 8); -} -``` - -We could write it in Rust as the following: - -``` -# use block2::Block; -unsafe fn sum(block: &Block<(i32, i32), i32>) -> i32 { - block.call((5, 8)) -} -``` - -Note the extra parentheses in the `call` method, since the arguments must be -passed as a tuple. - -# Creating blocks - -Creating a block to pass to Objective-C can be done with the `ConcreteBlock` -struct. For example, to create a block that adds two `i32`s, we could write: - -``` -# use block2::ConcreteBlock; -let block = ConcreteBlock::new(|a: i32, b: i32| a + b); -let block = block.copy(); -assert_eq!(unsafe { block.call((5, 8)) }, 13); -``` - -It is important to copy your block to the heap (with the `copy` method) before -passing it to Objective-C; this is because our `ConcreteBlock` is only meant -to be copied once, and we can enforce this in Rust, but if Objective-C code -were to copy it twice we could have a double free. - -As an optimization if your block doesn't capture any variables, you can use -the [`global_block!`] macro to create a static block: - -``` -use block2::global_block; -global_block! { - static MY_BLOCK = || -> f32 { - 10.0 - } -}; -assert_eq!(unsafe { MY_BLOCK.call(()) }, 10.0); -``` -*/ +//! A Rust interface for Objective-C blocks. +//! +//! For more information on the specifics of the block implementation, see +//! Clang's documentation: +//! +//! # Invoking blocks +//! +//! The `Block` struct is used for invoking blocks from Objective-C. For +//! example, consider this Objective-C function: +//! +//! ``` objc +//! int32_t sum(int32_t (^block)(int32_t, int32_t)) { +//! return block(5, 8); +//! } +//! ``` +//! +//! We could write it in Rust as the following: +//! +//! ``` +//! # use block2::Block; +//! unsafe fn sum(block: &Block<(i32, i32), i32>) -> i32 { +//! block.call((5, 8)) +//! } +//! ``` +//! +//! Note the extra parentheses in the `call` method, since the arguments must +//! be passed as a tuple. +//! +//! # Creating blocks +//! +//! Creating a block to pass to Objective-C can be done with the +//! `ConcreteBlock` struct. For example, to create a block that adds two +//! `i32`s, we could write: +//! +//! ``` +//! # use block2::ConcreteBlock; +//! let block = ConcreteBlock::new(|a: i32, b: i32| a + b); +//! let block = block.copy(); +//! assert_eq!(unsafe { block.call((5, 8)) }, 13); +//! ``` +//! +//! It is important to copy your block to the heap (with the `copy` method) +//! before passing it to Objective-C; this is because our `ConcreteBlock` is +//! only meant to be copied once, and we can enforce this in Rust, but if +//! Objective-C code were to copy it twice we could have a double free. +//! +//! As an optimization if your block doesn't capture any variables, you can +//! use the [`global_block!`] macro to create a static block: +//! +//! ``` +//! use block2::global_block; +//! global_block! { +//! static MY_BLOCK = || -> f32 { +//! 10.0 +//! } +//! }; +//! assert_eq!(unsafe { MY_BLOCK.call(()) }, 10.0); +//! ``` #![no_std] #![warn(elided_lifetimes_in_paths)] diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index 284e374d9..b3976465f 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -1,36 +1,35 @@ -/*! -Functionality for declaring Objective-C classes. - -Classes can be declared using the [`ClassDecl`] struct. Instance variables and -methods can then be added before the class is ultimately registered. - -# Example - -The following example demonstrates declaring a class named `MyNumber` that has -one ivar, a `u32` named `_number` and a `number` method that returns it: - -``` no_run -# use objc2::{class, sel}; -# use objc2::declare::ClassDecl; -# use objc2::runtime::{Class, Object, Sel}; -let superclass = class!(NSObject); -let mut decl = ClassDecl::new("MyNumber", superclass).unwrap(); - -// Add an instance variable -decl.add_ivar::("_number"); - -// Add an ObjC method for getting the number -extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 { - unsafe { *this.ivar("_number") } -} -unsafe { - decl.add_method(sel!(number), - my_number_get as extern fn(&Object, Sel) -> u32); -} - -decl.register(); -``` -*/ +//! Functionality for declaring Objective-C classes. +//! +//! Classes can be declared using the [`ClassDecl`] struct. Instance variables +//! and methods can then be added before the class is ultimately registered. +//! +//! # Example +//! +//! The following example demonstrates declaring a class named `MyNumber` that +//! has one ivar, a `u32` named `_number` and a `number` method that returns +//! it: +//! +//! ``` no_run +//! # use objc2::{class, sel}; +//! # use objc2::declare::ClassDecl; +//! # use objc2::runtime::{Class, Object, Sel}; +//! let superclass = class!(NSObject); +//! let mut decl = ClassDecl::new("MyNumber", superclass).unwrap(); +//! +//! // Add an instance variable +//! decl.add_ivar::("_number"); +//! +//! // Add an ObjC method for getting the number +//! extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 { +//! unsafe { *this.ivar("_number") } +//! } +//! unsafe { +//! decl.add_method(sel!(number), +//! my_number_get as extern fn(&Object, Sel) -> u32); +//! } +//! +//! decl.register(); +//! ``` use alloc::format; use alloc::string::ToString; diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index 2c54de5a7..257f76659 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -1,62 +1,62 @@ -/*! -Objective-C Runtime bindings and wrapper for Rust. - -# Messaging objects - -Objective-C objects can be messaged using the [`msg_send!`](macro.msg_send!.html) macro: - -``` no_run -# use objc2::{class, msg_send}; -# use objc2::runtime::{Bool, Class, Object}; -# unsafe { -let cls = class!(NSObject); -let obj: *mut Object = msg_send![cls, new]; -let hash: usize = msg_send![obj, hash]; -let is_kind: Bool = msg_send![obj, isKindOfClass: cls]; -// Even void methods must have their return type annotated -let _: () = msg_send![obj, release]; -# } -``` - -# Reference counting - -Utilities for reference counting Objective-C objects are provided in the -[`rc`](rc/index.html) module. - -# Declaring classes - -Objective-C classes can even be declared from Rust using the functionality of -the [`declare`](declare/index.html) module. - -# Exceptions - -By default, if the `msg_send!` macro causes an exception to be thrown, this -will unwind into Rust resulting in unsafe, undefined behavior. -However, this crate has an `"catch_all"` feature which, when enabled, wraps -each `msg_send!` in a `@try`/`@catch` and panics if an exception is caught, -preventing Objective-C from unwinding into Rust. - -# Message type verification - -The Objective-C runtime includes encodings for each method that describe the -argument and return types. This crate can take advantage of these encodings to -verify that the types used in Rust match the types encoded for the method. - -To use this functionality, enable the `"verify_message"` feature. -With this feature enabled, type checking is performed for every message send, -which also requires that all arguments and return values for all messages -implement `Encode`. - -If this requirement is burdensome or you'd rather -just verify specific messages, you can call the -[`Message::verify_message`](trait.Message.html#method.verify_message) method -for specific selectors. - -# Support for other Operating Systems - -The bindings can be used on Linux or *BSD utilizing the -[GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2). -*/ +//! Objective-C Runtime bindings and wrapper for Rust. +//! +//! # Messaging objects +//! +//! Objective-C objects can be messaged using the +//! [`msg_send!`](macro.msg_send!.html) macro: +//! +//! ``` no_run +//! # use objc2::{class, msg_send}; +//! # use objc2::runtime::{Bool, Class, Object}; +//! # unsafe { +//! let cls = class!(NSObject); +//! let obj: *mut Object = msg_send![cls, new]; +//! let hash: usize = msg_send![obj, hash]; +//! let is_kind: Bool = msg_send![obj, isKindOfClass: cls]; +//! // Even void methods must have their return type annotated +//! let _: () = msg_send![obj, release]; +//! # } +//! ``` +//! +//! # Reference counting +//! +//! Utilities for reference counting Objective-C objects are provided in the +//! [`rc`](rc/index.html) module. +//! +//! # Declaring classes +//! +//! Objective-C classes can even be declared from Rust using the functionality +//! of the [`declare`](declare/index.html) module. +//! +//! # Exceptions +//! +//! By default, if the `msg_send!` macro causes an exception to be thrown, +//! this will unwind into Rust resulting in unsafe, undefined behavior. +//! However, this crate has an `"catch_all"` feature which, when enabled, +//! wraps each `msg_send!` in a `@try`/`@catch` and panics if an exception is +//! caught, preventing Objective-C from unwinding into Rust. +//! +//! # Message type verification +//! +//! The Objective-C runtime includes encodings for each method that describe +//! the argument and return types. This crate can take advantage of these +//! encodings to verify that the types used in Rust match the types encoded +//! for the method. +//! +//! To use this functionality, enable the `"verify_message"` feature. +//! With this feature enabled, type checking is performed for every message +//! send, which also requires that all arguments and return values for all +//! messages implement `Encode`. +//! +//! If this requirement is burdensome or you'd rather +//! just verify specific messages, you can call the +//! [`Message::verify_message`](trait.Message.html#method.verify_message) +//! method for specific selectors. +//! +//! # Support for other Operating Systems +//! +//! The bindings can be used on Linux or *BSD utilizing the +//! [GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2). #![no_std] #![cfg_attr( From 2ced7db3be0fbc7c2ef03ba28ac656a41e98b963 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 20:07:28 +0100 Subject: [PATCH 08/12] Fix broken CI badges Oversight from #99 --- README.md | 3 +-- block-sys/README.md | 3 +-- block2/README.md | 3 +-- objc-sys/README.md | 3 +-- objc2-encode/README.md | 3 +-- objc2-foundation/README.md | 3 +-- objc2/README.md | 3 +-- 7 files changed, 7 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 65d02081d..5fe07cc41 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # [![Rust + \[Obj-C\]](assets/logo-small.png)](https://github.com/madsmtm/objc2)
Objective-C in Rust [![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt) -[![Apple CI](https://github.com/madsmtm/objc2/actions/workflows/apple.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/apple.yml) -[![GNUStep CI](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml) +[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) # DISCLAIMER! These crates are work in progress, and should not be used in production environments. Use the battle-tested `objc` family instead! diff --git a/block-sys/README.md b/block-sys/README.md index dea21e567..f06851f5f 100644 --- a/block-sys/README.md +++ b/block-sys/README.md @@ -3,8 +3,7 @@ [![Latest version](https://badgen.net/crates/v/block-sys)](https://crates.io/crates/block-sys) [![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt) [![Documentation](https://docs.rs/block-sys/badge.svg)](https://docs.rs/block-sys/) -[![Apple CI](https://github.com/madsmtm/objc2/actions/workflows/apple.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/apple.yml) -[![GNUStep CI](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml) +[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) Raw Rust bindings to Apple's C language extension of blocks. diff --git a/block2/README.md b/block2/README.md index 042fda6a5..18603223c 100644 --- a/block2/README.md +++ b/block2/README.md @@ -3,8 +3,7 @@ [![Latest version](https://badgen.net/crates/v/block2)](https://crates.io/crates/block2) [![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt) [![Documentation](https://docs.rs/block2/badge.svg)](https://docs.rs/block2/) -[![Apple CI](https://github.com/madsmtm/objc2/actions/workflows/apple.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/apple.yml) -[![GNUStep CI](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml) +[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) Apple's C language extension of blocks in Rust. diff --git a/objc-sys/README.md b/objc-sys/README.md index cbb73dbd1..ae4d940d0 100644 --- a/objc-sys/README.md +++ b/objc-sys/README.md @@ -3,8 +3,7 @@ [![Latest version](https://badgen.net/crates/v/objc-sys)](https://crates.io/crates/objc-sys) [![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt) [![Documentation](https://docs.rs/objc-sys/badge.svg)](https://docs.rs/objc-sys/) -[![Apple CI](https://github.com/madsmtm/objc2/actions/workflows/apple.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/apple.yml) -[![GNUStep CI](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml) +[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) Raw Rust bindings to the Objective-C runtime and ABI. diff --git a/objc2-encode/README.md b/objc2-encode/README.md index e0fd44aae..5df91e552 100644 --- a/objc2-encode/README.md +++ b/objc2-encode/README.md @@ -3,8 +3,7 @@ [![Latest version](https://badgen.net/crates/v/objc2-encode)](https://crates.io/crates/objc2-encode) [![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt) [![Documentation](https://docs.rs/objc2-encode/badge.svg)](https://docs.rs/objc2-encode/) -[![Apple CI](https://github.com/madsmtm/objc2/actions/workflows/apple.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/apple.yml) -[![GNUStep CI](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml) +[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) Objective-C type-encoding in Rust. diff --git a/objc2-foundation/README.md b/objc2-foundation/README.md index 3aa65c619..97649fef2 100644 --- a/objc2-foundation/README.md +++ b/objc2-foundation/README.md @@ -3,7 +3,6 @@ [![Latest version](https://badgen.net/crates/v/objc2-foundation)](https://crates.io/crates/objc2-foundation) [![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt) [![Documentation](https://docs.rs/objc2-foundation/badge.svg)](https://docs.rs/objc2-foundation/) -[![Apple CI](https://github.com/madsmtm/objc2/actions/workflows/apple.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/apple.yml) -[![GNUStep CI](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml) +[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) Bindings to the Objective-C `Foundation` framework in Rust. diff --git a/objc2/README.md b/objc2/README.md index 6ab78e00a..246e82a89 100644 --- a/objc2/README.md +++ b/objc2/README.md @@ -3,8 +3,7 @@ [![Latest version](https://badgen.net/crates/v/objc2)](https://crates.io/crates/objc2) [![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt) [![Documentation](https://docs.rs/objc2/badge.svg)](https://docs.rs/objc2/) -[![Apple CI](https://github.com/madsmtm/objc2/actions/workflows/apple.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/apple.yml) -[![GNUStep CI](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/gnustep.yml) +[![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) Objective-C runtime bindings and interface for Rust. From e3298a6400f4aa53dae0edc8cd2fa10538847496 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 23:47:05 +0100 Subject: [PATCH 09/12] Vastly improve crate-level documentation in all crates Follow: Crate level docs are thorough and include examples (C-CRATE-DOC) --- README.md | 3 + block-sys/README.md | 10 ++- block-sys/src/lib.rs | 5 +- block2/CHANGELOG.md | 1 + block2/README.md | 45 ++-------- block2/src/lib.rs | 59 ++++++++----- objc-sys/README.md | 9 +- objc-sys/src/lib.rs | 7 ++ objc2-encode/CHANGELOG.md | 3 + objc2-encode/README.md | 66 ++------------ objc2-encode/src/encoding.rs | 25 +++++- objc2-encode/src/lib.rs | 64 +++++++++++++- objc2-foundation/Cargo.toml | 2 +- objc2-foundation/README.md | 10 ++- objc2-foundation/src/lib.rs | 7 ++ objc2/CHANGELOG.md | 1 + objc2/Cargo.toml | 4 +- objc2/README.md | 117 ++++--------------------- objc2/examples/introspection.rs | 1 + objc2/src/declare.rs | 17 ++-- objc2/src/exception.rs | 8 +- objc2/src/lib.rs | 147 +++++++++++++++++++++++--------- objc2/src/macros.rs | 4 +- objc2/src/message/mod.rs | 3 +- objc2/src/rc/autorelease.rs | 11 +-- objc2/src/rc/mod.rs | 43 ++++++++-- 26 files changed, 374 insertions(+), 298 deletions(-) diff --git a/README.md b/README.md index 5fe07cc41..f0d23f3a9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ # DISCLAIMER! These crates are work in progress, and should not be used in production environments. Use the battle-tested `objc` family instead! +Anyway, thanks for being here, any help testing things out is highly +appreciated! + ## Migrating from original crates diff --git a/block-sys/README.md b/block-sys/README.md index f06851f5f..3d0d1a5ce 100644 --- a/block-sys/README.md +++ b/block-sys/README.md @@ -7,6 +7,10 @@ Raw Rust bindings to Apple's C language extension of blocks. +This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2), +see that for related crates. + + ## Runtime Support This library is basically just a raw interface to the aptly specified [Blocks @@ -16,7 +20,9 @@ several different helper functions), the most important aspect being that the libraries are named differently, so the linking must take that into account. The user can choose the desired runtime by using the relevant cargo feature -flags, see the following sections: +flags, see the following sections. Note that if the `objc-sys` crate is +present in the module tree, this should have the same feature flag enabled as +that. ### Apple's [`libclosure`](https://opensource.apple.com/source/libclosure/) @@ -51,6 +57,8 @@ and is now used in [Swift's `libdispatch`] and [Swift's Foundation] as well. This can be easily used on many Linux systems with the `libblocksruntime-dev` package. +Using this runtime probably won't work together with `objc-sys` crate. + [Swift's `libdispatch`]: https://github.com/apple/swift-corelibs-libdispatch/tree/swift-5.5.1-RELEASE/src/BlocksRuntime [Swift's Foundation]: https://github.com/apple/swift-corelibs-foundation/tree/swift-5.5.1-RELEASE/Sources/BlocksRuntime diff --git a/block-sys/src/lib.rs b/block-sys/src/lib.rs index b7b51df01..ac2ed7d3c 100644 --- a/block-sys/src/lib.rs +++ b/block-sys/src/lib.rs @@ -4,7 +4,8 @@ //! sources, but the [ABI specification][ABI] is really the place you should //! be looking! //! -//! See also the `README.md` for more info. +//! See also the [`README.md`](https://crates.io/crates/block-sys) for more +//! background information, and for how to configure the desired runtime. //! //! [ABI]: https://clang.llvm.org/docs/Block-ABI-Apple.html @@ -257,7 +258,7 @@ pub struct Block_layout { /// space on the stack allocated to hold the return value. pub invoke: Option, /// The block's descriptor. The actual type of this is: - /// ```ignore + /// ```pseudo-code /// match (BLOCK_HAS_COPY_DISPOSE, BLOCK_HAS_SIGNATURE) { /// (false, false) => Block_descriptor_header, /// (true, false) => Block_descriptor, diff --git a/block2/CHANGELOG.md b/block2/CHANGELOG.md index 67aee288d..55c7ce66d 100644 --- a/block2/CHANGELOG.md +++ b/block2/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed * Changed `global_block!` macro to take an optional semicolon at the end. +* Improved documentation. ## 0.2.0-alpha.2 - 2021-12-22 diff --git a/block2/README.md b/block2/README.md index 18603223c..3d9039979 100644 --- a/block2/README.md +++ b/block2/README.md @@ -7,45 +7,10 @@ Apple's C language extension of blocks in Rust. -For more information on the specifics of the block implementation, see -Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html +This crate provides functionality for interracting with C blocks, effectively +the C-equivalent of Rust's closures. -## Invoking blocks +See [the docs](https://docs.rs/block2/) for a more thorough overview. -The `Block` struct is used for invoking blocks from Objective-C. For example, -consider this Objective-C function: - -```objc -int32_t sum(int32_t (^block)(int32_t, int32_t)) { - return block(5, 8); -} -``` - -We could write it in Rust as the following: - -```rust -use block2::Block; -unsafe fn sum(block: &Block<(i32, i32), i32>) -> i32 { - block.call((5, 8)) -} -``` - -Note the extra parentheses in the `call` method, since the arguments must be -passed as a tuple. - -## Creating blocks - -Creating a block to pass to Objective-C can be done with the `ConcreteBlock` -struct. For example, to create a block that adds two `i32`s, we could write: - -```rust -use block2::ConcreteBlock; -let block = ConcreteBlock::new(|a: i32, b: i32| a + b); -let block = block.copy(); -assert_eq!(unsafe { block.call((5, 8)) }, 13); -``` - -It is important to copy your block to the heap (with the `copy` method) before -passing it to Objective-C; this is because our `ConcreteBlock` is only meant -to be copied once, and we can enforce this in Rust, but if Objective-C code -were to copy it twice we could have a double free. +This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2), +see that for related crates. diff --git a/block2/src/lib.rs b/block2/src/lib.rs index 354a8870f..4b083ebf4 100644 --- a/block2/src/lib.rs +++ b/block2/src/lib.rs @@ -1,24 +1,36 @@ -//! A Rust interface for Objective-C blocks. +//! # Apple's C language extension of blocks //! -//! For more information on the specifics of the block implementation, see -//! Clang's documentation: +//! C Blocks are effectively the C-equivalent of Rust's closures, in that they +//! have the ability to capture their environments. //! -//! # Invoking blocks +//! This crate provides capabilities to create and invoke these blocks, in an +//! ergonomic "Rust-centric" fashion. //! -//! The `Block` struct is used for invoking blocks from Objective-C. For -//! example, consider this Objective-C function: +//! For more information on the specifics of the block implementation, see the +//! [C language specification][lang] and the [ABI specification][ABI]. //! -//! ``` objc -//! int32_t sum(int32_t (^block)(int32_t, int32_t)) { +//! (Note that while this library can be used separately from Objective-C, +//! they're most commonly used together). +//! +//! ## Invoking blocks +//! +//! The [`Block`] struct is used for invoking blocks from Objective-C. For +//! example, consider this Objective-C function that takes a block as a +//! parameter, executes the block with some arguments, and returns the result: +//! +//! ```objc +//! #include +//! #include +//! int32_t run_block(int32_t (^block)(int32_t, int32_t)) { //! return block(5, 8); //! } //! ``` //! -//! We could write it in Rust as the following: +//! We could write the equivalent function in Rust like this: //! //! ``` -//! # use block2::Block; -//! unsafe fn sum(block: &Block<(i32, i32), i32>) -> i32 { +//! use block2::Block; +//! unsafe fn run_block(block: &Block<(i32, i32), i32>) -> i32 { //! block.call((5, 8)) //! } //! ``` @@ -26,24 +38,26 @@ //! Note the extra parentheses in the `call` method, since the arguments must //! be passed as a tuple. //! -//! # Creating blocks +//! ## Creating blocks //! //! Creating a block to pass to Objective-C can be done with the -//! `ConcreteBlock` struct. For example, to create a block that adds two -//! `i32`s, we could write: +//! [`ConcreteBlock`] struct. For example, to create a block that adds two +//! integers, we could write: //! //! ``` -//! # use block2::ConcreteBlock; +//! use block2::ConcreteBlock; //! let block = ConcreteBlock::new(|a: i32, b: i32| a + b); //! let block = block.copy(); //! assert_eq!(unsafe { block.call((5, 8)) }, 13); //! ``` //! -//! It is important to copy your block to the heap (with the `copy` method) -//! before passing it to Objective-C; this is because our `ConcreteBlock` is +//! It is important to copy your block to the heap (with the [`copy`] method) +//! before passing it to Objective-C; this is because our [`ConcreteBlock`] is //! only meant to be copied once, and we can enforce this in Rust, but if //! Objective-C code were to copy it twice we could have a double free. //! +//! [`copy`]: ConcreteBlock::copy +//! //! As an optimization if your block doesn't capture any variables, you can //! use the [`global_block!`] macro to create a static block: //! @@ -52,10 +66,13 @@ //! global_block! { //! static MY_BLOCK = || -> f32 { //! 10.0 -//! } -//! }; +//! }; +//! } //! assert_eq!(unsafe { MY_BLOCK.call(()) }, 10.0); //! ``` +//! +//! [lang]: https://clang.llvm.org/docs/BlockLanguageSpec.html +//! [ABI]: http://clang.llvm.org/docs/Block-ABI-Apple.html #![no_std] #![warn(elided_lifetimes_in_paths)] @@ -68,6 +85,10 @@ extern crate std; +#[cfg(doctest)] +#[doc = include_str!("../README.md")] +extern "C" {} + use core::ffi::c_void; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop}; diff --git a/objc-sys/README.md b/objc-sys/README.md index ae4d940d0..dbe756948 100644 --- a/objc-sys/README.md +++ b/objc-sys/README.md @@ -7,6 +7,10 @@ Raw Rust bindings to the Objective-C runtime and ABI. +This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2), +see that for related crates. + + ## Runtime Support Objective-C has a runtime, different implementations of said runtime exist, @@ -14,6 +18,7 @@ and they act in slightly different ways. By default, Apple platforms link to Apple's runtime, but if you're using another runtime you must tell it to this library using feature flags. + ### Apple's [`objc4`](https://opensource.apple.com/source/objc4/) - Feature flag: `apple`. @@ -144,5 +149,5 @@ Some items (in particular the `objc_msgSend_X` family) have `cfg`s that prevent their usage on different platforms; these are **semver-stable** in the sense that they will only get less restrictive, never more. -1 That said, most of this is created with the help of `bindgen`'s -commandline interface, so huge thanks to them! +1: That said, most of this is created with the help of `bindgen`'s +commandline interface, so huge thanks to them! diff --git a/objc-sys/src/lib.rs b/objc-sys/src/lib.rs index 5e929f97c..82617ddc8 100644 --- a/objc-sys/src/lib.rs +++ b/objc-sys/src/lib.rs @@ -7,6 +7,9 @@ //! particular `runtime.h`. //! - GNUStep's `libobjc2` [source code][libobjc2], in particular `runtime.h`. //! +//! See also the [`README.md`](https://crates.io/crates/objc-sys) for more +//! background information, and for how to configure the desired runtime. +//! //! [apple]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc //! [libobjc2]: https://github.com/gnustep/libobjc2/tree/v2.1/objc //! [objc4]: https://opensource.apple.com/source/objc4/objc4-818.2/runtime/ @@ -26,6 +29,10 @@ // See https://github.com/japaric/cty/issues/14. extern crate std; +#[cfg(doctest)] +#[doc = include_str!("../README.md")] +extern "C" {} + use core::cell::UnsafeCell; use core::marker::{PhantomData, PhantomPinned}; diff --git a/objc2-encode/CHANGELOG.md b/objc2-encode/CHANGELOG.md index fc8cfb72d..6f1183425 100644 --- a/objc2-encode/CHANGELOG.md +++ b/objc2-encode/CHANGELOG.md @@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added * Implement `Hash` for `Encoding`. +### Changed +* Improved documentation. + ## 2.0.0-beta.1 - 2021-12-22 diff --git a/objc2-encode/README.md b/objc2-encode/README.md index 5df91e552..33e4ba611 100644 --- a/objc2-encode/README.md +++ b/objc2-encode/README.md @@ -7,65 +7,13 @@ Objective-C type-encoding in Rust. -The Objective-C directive `@encode` encodes types as strings for usage in -various places in the runtime. +This crates provides the equivalent of the Objective-C `@encode` directive, +and functions for comparing these encodings. -This crate provides the `Encoding` type to describe and compare these -type-encodings without memory allocation. +Additionally, it provides traits for annotating types that has an Objective-C +encoding. -Additionally it provides traits for annotating types that has a corresponding -Objective-C encoding, respectively `Encode` for structs and `RefEncode` for -references (and `EncodeArguments` for function arguments). +See [the docs](https://docs.rs/objc2-encode/) for a more thorough overview. -These types are exported under the `objc2` crate as well, so usually you would -just use that. - - -## Examples - -Implementing `Encode` and `RefEncode`: - -```rust -use objc2_encode::{Encode, Encoding, RefEncode}; - -#[repr(C)] -struct MyObject { - a: f32, - b: i16, -} - -unsafe impl Encode for MyObject { - const ENCODING: Encoding<'static> = Encoding::Struct( - "MyObject", - &[f32::ENCODING, i16::ENCODING], - ); -} - -assert!(MyObject::ENCODING.equivalent_to_str("{MyObject=fs}")); - -unsafe impl RefEncode for MyObject { - const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING); -} - -assert!(MyObject::ENCODING_REF.equivalent_to_str("^{MyObject=fs}")); -``` - -An `Encoding` can be compared with an encoding string from the Objective-C -runtime: - -```rust -use objc2_encode::Encode; -assert!(i32::ENCODING.equivalent_to_str("i")); -``` - -`Encoding` implements `Display` as its string representation. This can be -generated conveniently through the `to_string` method: - -```rust -use objc2_encode::Encode; -assert_eq!(i32::ENCODING.to_string(), "i"); -``` - -See the [`examples`] folder for more complex usage. - -[`examples`]: https://github.com/madsmtm/objc2/tree/master/objc2-encode/examples +This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2), +see that for related crates. diff --git a/objc2-encode/src/encoding.rs b/objc2-encode/src/encoding.rs index e8edd4e2b..85491f899 100644 --- a/objc2-encode/src/encoding.rs +++ b/objc2-encode/src/encoding.rs @@ -6,15 +6,32 @@ use crate::parse; /// /// Can be retrieved in Objective-C for a type `T` using the `@encode(T)` /// directive. -/// ```objective-c , ignore +/// ```objc /// NSLog(@"Encoding of NSException: %s", @encode(NSException)); /// ``` /// -/// For more information, see [Apple's documentation][ocrtTypeEncodings] and -/// [`clang`'s source code for generating `@encode`][clang-src]. +/// The [`Display`][`fmt::Display`] implementation converts the [`Encoding`] +/// into its string representation, that the the `@encode` directive would +/// return. This can be used conveniently through the `to_string` method: +/// +/// ``` +/// use objc2_encode::Encoding; +/// assert_eq!(Encoding::Int.to_string(), "i"); +/// ``` +/// +/// For more information on the string value of an encoding, see [Apple's +/// documentation][ocrtTypeEncodings]. /// /// [ocrtTypeEncodings]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html -/// [clang-src]: https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850 +/// +/// # Examples +/// +/// Comparing an encoding to a string from the Objective-C runtime: +/// +/// ``` +/// use objc2_encode::Encoding; +/// assert!(Encoding::Array(10, &Encoding::FloatComplex).equivalent_to_str("[10jf]")); +/// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] // Maybe we're missing some encodings? pub enum Encoding<'a> { diff --git a/objc2-encode/src/lib.rs b/objc2-encode/src/lib.rs index 80e284cdc..6b256d162 100644 --- a/objc2-encode/src/lib.rs +++ b/objc2-encode/src/lib.rs @@ -2,10 +2,68 @@ //! //! This is re-exported into the top level of `objc2`. //! +//! The Objective-C directive `@encode` encodes types as strings, and this is +//! used in various places in the runtime. +//! +//! This crate provides the [`Encoding`] type to efficiently describe and +//! compare these type-encodings. +//! +//! Additionally it provides traits for annotating types that has an +//! Objective-C encoding: Specifically [`Encode`] for structs, [`RefEncode`] +//! for references and [`EncodeArguments`] for function arguments. +//! +//! These types are exported under the [`objc2`] crate as well, so usually you +//! would just use them from there. +//! +//! ## Example +//! +//! Implementing [`Encode`] and [`RefEncode`] for a custom type: +//! +//! ``` +//! use objc2_encode::{Encode, Encoding, RefEncode}; +//! // or from objc2: +//! // use objc2::{Encode, Encoding, RefEncode}; +//! +//! #[repr(C)] +//! struct MyStruct { +//! a: f32, // float +//! b: i16, // int16_t +//! } +//! +//! unsafe impl Encode for MyStruct { +//! const ENCODING: Encoding<'static> = Encoding::Struct( +//! "MyStruct", // Must use the same name as defined in C header files +//! &[ +//! f32::ENCODING, // Same as Encoding::Float +//! i16::ENCODING, // Same as Encoding::Short +//! ], +//! ); +//! } +//! +//! // @encode(MyStruct) -> "{MyStruct=fs}" +//! assert!(MyStruct::ENCODING.equivalent_to_str("{MyStruct=fs}")); +//! +//! unsafe impl RefEncode for MyStruct { +//! // Note that if `MyStruct` is an Objective-C instance, this should +//! // be `Encoding::Object`. +//! const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING); +//! } +//! +//! // @encode(MyStruct*) -> "^{MyStruct=fs}" +//! assert!(MyStruct::ENCODING_REF.equivalent_to_str("^{MyStruct=fs}")); +//! ``` +//! +//! See the [`examples`] folder for more complex usage. +//! +//! [`examples`]: https://github.com/madsmtm/objc2/tree/master/objc2-encode/examples +//! //! Further resources: -//! - -//! - -//! - +//! - [Objective-C, Encoding and You](https://dmaclach.medium.com/objective-c-encoding-and-you-866624cc02de). +//! - [Apple's documentation on Type Encodings](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html). +//! - [How are the digits in ObjC method type encoding calculated?](https://stackoverflow.com/a/11527925) +//! - [`clang`'s source code for generating `@encode`](https://github.com/llvm/llvm-project/blob/fae0dfa6421ea6c02f86ba7292fa782e1e2b69d1/clang/lib/AST/ASTContext.cpp#L7500-L7850). +//! +//! [`objc2`]: https://crates.io/crates/objc2 #![no_std] #![warn(elided_lifetimes_in_paths)] diff --git a/objc2-foundation/Cargo.toml b/objc2-foundation/Cargo.toml index 47d8a7e18..fa53fe4e8 100644 --- a/objc2-foundation/Cargo.toml +++ b/objc2-foundation/Cargo.toml @@ -4,7 +4,7 @@ version = "0.2.0-alpha.3" # Remember to update html_root_url in lib.rs authors = ["Steven Sheldon", "Mads Marquart "] edition = "2018" -description = "Bindings to the Objective-C Foundation framework" +description = "Bindings to the Objective-C Cocoa Foundation framework" keywords = ["objective-c", "macos", "ios", "cocoa", "uikit"] categories = [ "api-bindings", diff --git a/objc2-foundation/README.md b/objc2-foundation/README.md index 97649fef2..a7c1896a6 100644 --- a/objc2-foundation/README.md +++ b/objc2-foundation/README.md @@ -5,4 +5,12 @@ [![Documentation](https://docs.rs/objc2-foundation/badge.svg)](https://docs.rs/objc2-foundation/) [![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) -Bindings to the Objective-C `Foundation` framework in Rust. +Bindings to the Objective-C Cocoa `Foundation` framework in Rust. + +This library is very much in progress, consider using the more battle-tested +[`cocoa-foundation`] crate in the meantime. + +This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2), +see that for related crates. + +[`cocoa-foundation`]: https://crates.io/crates/cocoa-foundation diff --git a/objc2-foundation/src/lib.rs b/objc2-foundation/src/lib.rs index c18f20c6e..56933c887 100644 --- a/objc2-foundation/src/lib.rs +++ b/objc2-foundation/src/lib.rs @@ -1,3 +1,10 @@ +//! # Bindings to the Objective-C Cocoa `Foundation` framework +//! +//! This library is very much in progress, consider using the more +//! battle-tested [`cocoa-foundation`] crate in the meantime. +//! +//! [`cocoa-foundation`]: https://crates.io/crates/cocoa-foundation + #![no_std] #![warn(elided_lifetimes_in_paths)] #![deny(non_ascii_idents)] diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 16b75c2f6..3348f6cd8 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * **BREAKING**: Renamed: - `Object::get_ivar` -> `Object::ivar` - `Object::get_mut_ivar` -> `Object::ivar_mut` +* Vastly improved documentation. ## 0.3.0-alpha.5 - 2021-12-22 diff --git a/objc2/Cargo.toml b/objc2/Cargo.toml index e551af72d..e2079e6c3 100644 --- a/objc2/Cargo.toml +++ b/objc2/Cargo.toml @@ -4,7 +4,7 @@ version = "0.3.0-alpha.5" # Remember to update html_root_url in lib.rs authors = ["Steven Sheldon", "Mads Marquart "] edition = "2018" -description = "Objective-C runtime bindings and interface." +description = "Objective-C interface and runtime bindings" keywords = ["objective-c", "macos", "ios", "objc_msgSend"] categories = [ "api-bindings", @@ -18,6 +18,8 @@ license = "MIT" build = "build.rs" +# NOTE: 'unstable' features are _not_ considered part of the SemVer contract, +# and may be removed in a minor release. [features] # Enables `objc2::exception::throw` and `objc2::exception::catch` exception = ["cc"] diff --git a/objc2/README.md b/objc2/README.md index 246e82a89..cf818d105 100644 --- a/objc2/README.md +++ b/objc2/README.md @@ -5,113 +5,32 @@ [![Documentation](https://docs.rs/objc2/badge.svg)](https://docs.rs/objc2/) [![CI](https://github.com/madsmtm/objc2/actions/workflows/ci.yml/badge.svg)](https://github.com/madsmtm/objc2/actions/workflows/ci.yml) -Objective-C runtime bindings and interface for Rust. +Objective-C interface and runtime bindings for Rust. -## Messaging objects +Most of the core libraries and frameworks that are in use on Apple systems are +written in Objective-C; this crate enables you to interract with those. -Objective-C objects can be messaged using the `msg_send!` macro: +## Example -```rust , no_run +```rust +use std::ptr::NonNull; use objc2::{class, msg_send}; -use objc2::runtime::{Bool, Object}; +use objc2::rc::{Id, Owned}; +use objc2::runtime::{Class, Object}; let cls = class!(NSObject); -unsafe { - let obj: *mut Object = msg_send![cls, new]; - let hash: usize = msg_send![obj, hash]; - let is_kind: Bool = msg_send![obj, isKindOfClass: cls]; - // Even void methods must have their return type annotated - let _: () = msg_send![obj, release]; -} -``` - -## Reference counting - -The utilities of the `rc` module provide ARC-like semantics for working with -Objective-C's reference counted objects in Rust. - -An `Id` retains an object and releases the object when dropped. -A `WeakId` will not retain the object, but can be upgraded to an `Id` and -safely fails if the object has been deallocated. - -```rust , no_run -use objc2::{class, msg_send}; -use objc2::rc::{autoreleasepool, Id, Shared, WeakId}; -use objc2::runtime::Object; - -// Id will release the object when dropped -let obj: Id = unsafe { - Id::new(msg_send![class!(NSObject), new]) -}; - -// Cloning retains the object an additional time -let cloned = obj.clone(); -autoreleasepool(|pool| { - // Autorelease consumes the Id, but won't - // actually release until the end of an autoreleasepool - let obj_ref: &Object = cloned.autorelease(pool); -}); - -// Weak references won't retain the object -let weak = WeakId::new(&obj); -drop(obj); -assert!(weak.load().is_none()); -``` - -## Declaring classes +let obj: *mut Object = unsafe { msg_send![cls, new] }; +let obj: Id = unsafe { Id::new(NonNull::new(obj).unwrap()) }; -Classes can be declared using the `ClassDecl` struct. Instance variables and -methods can then be added before the class is ultimately registered. - -The following example demonstrates declaring a class named `MyNumber` that has -one ivar, a `u32` named `_number` and a `number` method that returns it: - -```rust , no_run -use objc2::{class, sel}; -use objc2::declare::ClassDecl; -use objc2::runtime::{Object, Sel}; - -let superclass = class!(NSObject); -let mut decl = ClassDecl::new("MyNumber", superclass).unwrap(); - -// Add an instance variable -decl.add_ivar::("_number"); - -// Add an ObjC method for getting the number -extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 { - unsafe { *this.ivar("_number") } -} -unsafe { - decl.add_method(sel!(number), - my_number_get as extern fn(&Object, Sel) -> u32); -} - -decl.register(); +// TODO +// let isa = unsafe { obj.ivar::("isa") }; +// assert_eq!(cls, isa); ``` -## Exceptions - -By default, if the `msg_send!` macro causes an exception to be thrown, this -will unwind into Rust resulting in unsafe, undefined behavior. -However, this crate has an `"catch_all"` feature which, when enabled, wraps -each `msg_send!` in a `@try`/`@catch` and panics if an exception is caught, -preventing Objective-C from unwinding into Rust. - -## Message type verification - -The Objective-C runtime includes encodings for each method that describe the -argument and return types. This crate can take advantage of these encodings to -verify that the types used in Rust match the types encoded for the method. - -To use this functionality, enable the `"verify_message"` feature. -With this feature enabled, type checking is performed for every message send, -which also requires that all arguments and return values for all messages -implement `Encode`. - -If this requirement is burdensome or you'd rather just verify specific messages, -you can call the `Message::verify_message` method for specific selectors. +See [the docs](https://docs.rs/objc2/) for a more thorough overview, or jump +right into the [examples]. -## Support for other Operating Systems +This crate is part of the [`objc2` project](https://github.com/madsmtm/objc2), +see that for related crates. -The bindings can be used on Linux or *BSD utilizing the -[GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2). +[examples]: https://github.com/madsmtm/objc2/tree/master/objc2/examples diff --git a/objc2/examples/introspection.rs b/objc2/examples/introspection.rs index 904f77f40..5a10b2736 100644 --- a/objc2/examples/introspection.rs +++ b/objc2/examples/introspection.rs @@ -29,6 +29,7 @@ fn main() { println!("NSObject address: {:p}", obj); // Access an ivar of the object + // TODO: Fix this! let isa: *const Class = unsafe { *obj.ivar("isa") }; println!("NSObject isa: {:?}", isa); diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index b3976465f..c5128d7e5 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -9,10 +9,11 @@ //! has one ivar, a `u32` named `_number` and a `number` method that returns //! it: //! -//! ``` no_run -//! # use objc2::{class, sel}; -//! # use objc2::declare::ClassDecl; -//! # use objc2::runtime::{Class, Object, Sel}; +//! ```no_run +//! use objc2::{class, sel}; +//! use objc2::declare::ClassDecl; +//! use objc2::runtime::{Class, Object, Sel}; +//! //! let superclass = class!(NSObject); //! let mut decl = ClassDecl::new("MyNumber", superclass).unwrap(); //! @@ -20,12 +21,14 @@ //! decl.add_ivar::("_number"); //! //! // Add an ObjC method for getting the number -//! extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 { +//! extern "C" fn my_number_get(this: &Object, _cmd: Sel) -> u32 { //! unsafe { *this.ivar("_number") } //! } //! unsafe { -//! decl.add_method(sel!(number), -//! my_number_get as extern fn(&Object, Sel) -> u32); +//! decl.add_method( +//! sel!(number), +//! my_number_get as extern "C" fn(&Object, Sel) -> u32, +//! ); //! } //! //! decl.register(); diff --git a/objc2/src/exception.rs b/objc2/src/exception.rs index cc72649e1..d2c4d9aa3 100644 --- a/objc2/src/exception.rs +++ b/objc2/src/exception.rs @@ -1,6 +1,12 @@ //! Objective-C's @throw and @try/@catch. //! -//! This is only available when the `exception` feature is enabled. +//! By default, if the [`msg_send!`] macro causes an exception to be thrown, +//! this will unwind into Rust, resulting in undefined behavior. However, this +//! crate has an `"catch_all"` feature which, when enabled, wraps each +//! [`msg_send!`] in a [`catch`] and panics if an exception is caught, +//! preventing Objective-C from unwinding into Rust. +//! +//! This module is only available when the `"exception"` feature is enabled. //! //! See the following links for more information: //! - diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index 257f76659..524376765 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -1,62 +1,124 @@ -//! Objective-C Runtime bindings and wrapper for Rust. +//! # Objective-C interface and runtime bindings //! -//! # Messaging objects +//! Objective-C is1 the standard programming language on Apple +//! platforms like macOS, iOS, tvOS and watchOS. It is an object-oriented +//! language centered around sending messages to it's instances, which is for +//! the most part equivalent to a function call. //! -//! Objective-C objects can be messaged using the -//! [`msg_send!`](macro.msg_send!.html) macro: +//! Most of the core libraries and frameworks that are in use on Apple systems +//! are written in Objective-C, and hence we would like the ability to +//! interract with these using Rust; this crate enables you to do that, in +//! as safe a manner as possible. //! -//! ``` no_run -//! # use objc2::{class, msg_send}; -//! # use objc2::runtime::{Bool, Class, Object}; -//! # unsafe { +//! 1: Yes, I know, "was", Swift now exists. All the existing frameworks +//! are written in Objective-C though, so the point still holds. +//! +//! +//! ## Basic usage +//! +//! This example illustrates major parts of the functionality in this crate: +//! +//! First, we get a reference to the `NSObject`'s [`runtime::Class`] using the +//! [`class!`] macro. +//! Next, we creates a new [`runtime::Object`] pointer, and ensures it is +//! deallocated after we've used it by putting it into an [`rc::Owned`] +//! [`rc::Id`]. +//! Now we send messages to the object to our hearts desire using +//! the [`msg_send!`] macro, and lastly, the `Id` goes out of +//! scope, and the object is deallocated. +//! +#![cfg_attr(apple, doc = "```")] +#![cfg_attr(not(apple), doc = "```no_run")] +//! use core::ptr::NonNull; +//! use objc2::{class, msg_send}; +//! use objc2::ffi::NSUInteger; +//! use objc2::rc::{Id, Owned}; +//! use objc2::runtime::{Bool, Object}; +//! +//! // Creation //! let cls = class!(NSObject); -//! let obj: *mut Object = msg_send![cls, new]; -//! let hash: usize = msg_send![obj, hash]; -//! let is_kind: Bool = msg_send![obj, isKindOfClass: cls]; -//! // Even void methods must have their return type annotated -//! let _: () = msg_send![obj, release]; -//! # } -//! ``` +//! let obj: *mut Object = unsafe { msg_send![cls, new] }; +//! let obj = NonNull::new(obj).expect("Failed allocating object"); +//! let obj: Id = unsafe { Id::new(obj) }; //! -//! # Reference counting +//! // Usage +//! let hash: NSUInteger = unsafe { msg_send![obj, hash] }; +//! let is_kind: Bool = unsafe { msg_send![obj, isKindOfClass: cls] }; +//! assert!(is_kind.is_true()); +//! ``` //! -//! Utilities for reference counting Objective-C objects are provided in the -//! [`rc`](rc/index.html) module. +//! Note that this very simple example contains **a lot** of `unsafe` (which +//! should all ideally be justified with a `// SAFETY` comment). This is +//! required because our compiler can verify very little about the Objective-C +//! invocation, including all argument and return types used in [`msg_send!`]; +//! we could have just as easily accidentally made `hash` an `f32`, or any +//! other type, and this would trigger undefined behaviour! //! -//! # Declaring classes +//! Making the ergonomics better is something that is currently being worked +//! on, see e.g. the [`objc2-foundation`] crate for more ergonomic usage of at +//! least the `Foundation` framework. //! -//! Objective-C classes can even be declared from Rust using the functionality -//! of the [`declare`](declare/index.html) module. +//! Anyhow, this nicely leads us to another feature that this crate has: //! -//! # Exceptions +//! [`runtime::Class`]: crate::runtime::Class +//! [`runtime::Object`]: crate::runtime::Object +//! [`rc::Owned`]: crate::rc::Owned +//! [`rc::Id`]: crate::rc::Id +//! [`objc2-foundation`]: https://crates.io/crates/objc2-foundation //! -//! By default, if the `msg_send!` macro causes an exception to be thrown, -//! this will unwind into Rust resulting in unsafe, undefined behavior. -//! However, this crate has an `"catch_all"` feature which, when enabled, -//! wraps each `msg_send!` in a `@try`/`@catch` and panics if an exception is -//! caught, preventing Objective-C from unwinding into Rust. //! -//! # Message type verification +//! ## Encodings and message type verification //! //! The Objective-C runtime includes encodings for each method that describe -//! the argument and return types. This crate can take advantage of these -//! encodings to verify that the types used in Rust match the types encoded -//! for the method. +//! the argument and return types. See the [`objc2-encode`] crate for the +//! full overview of what this is. +//! +//! The important part is, to make message sending _safer_ (not fully safe), +//! all arguments and return values for messages must implement [`Encode`]. +//! This allows the Rust compiler to prevent you from passing e.g. a [`Box`] +//! into Objective-C, which would both be UB and leak the box. +//! +//! Furthermore, this crate can take advantage of the encodings provided by +//! the runtime to verify that the types used in Rust match the types encoded +//! for the method. This is not a perfect solution for ensuring safety of +//! message sends (some Rust types have the same encoding, but are not +//! equivalent), but it gets us much closer to it! //! -//! To use this functionality, enable the `"verify_message"` feature. -//! With this feature enabled, type checking is performed for every message -//! send, which also requires that all arguments and return values for all -//! messages implement `Encode`. +//! To use this functionality, enable the `"verify_message"` cargo feature +//! while debugging. With this feature enabled, encoding types are checked +//! every time your send a message, and the message send will panic if they +//! are not equivalent. //! -//! If this requirement is burdensome or you'd rather -//! just verify specific messages, you can call the -//! [`Message::verify_message`](trait.Message.html#method.verify_message) -//! method for specific selectors. +//! [`objc2-encode`]: https://crates.io/crates/objc2-encode +//! [`Box`]: std::boxed::Box //! -//! # Support for other Operating Systems +//! +//! ## Crate features +//! +//! This crate exports several optional cargo features, see [`Cargo.toml`] for +//! an overview and description of these. +//! +//! [`Cargo.toml`]: https://github.com/madsmtm/objc2/blob/master/objc2/Cargo.toml +//! +//! +//! ## Support for other Operating Systems //! //! The bindings can be used on Linux or *BSD utilizing the -//! [GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2). +//! [GNUstep Objective-C runtime](https://www.github.com/gnustep/libobjc2), +//! see the [`objc-sys`][`objc_sys`] crate for how to configure this. +//! +//! +//! ## Other features +//! +//! Anyhow, that was a quick introduction, this library also has [support for +//! handling exceptions][exc], [the ability to dynamically declare Objective-C +//! classes][declare], [more advanced reference-counting utilities][rc] and +//! more, peruse the documentation at will! +//! +#![cfg_attr(feature = "exception", doc = "[exc]: crate::exception")] +#![cfg_attr(not(feature = "exception"), doc = "[exc]: #exception-feature-disabled")] +//! [declare]: crate::declare +//! [rc]: crate::rc #![no_std] #![cfg_attr( @@ -74,7 +136,8 @@ extern crate alloc; extern crate std; -#[cfg(doctest)] +// The example uses NSObject without doing the __gnustep_hack +#[cfg(all(apple, doctest))] #[doc = include_str!("../README.md")] extern "C" {} diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index 08b82eed3..f29b9af4a 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -61,7 +61,8 @@ macro_rules! sel { /// Sends a message to an object or class. /// /// The first argument can be any type that implements [`MessageReceiver`], -/// like a reference, a pointer, or an [`rc::Id`] to an object. +/// like a reference, a pointer, or an [`rc::Id`] to an object (where the +/// object implements [`Message`]). /// /// In general this is wildly `unsafe`, even more so than sending messages in /// Objective-C, because this macro doesn't know the expected types and @@ -73,6 +74,7 @@ macro_rules! sel { /// Variadic arguments are not currently supported. /// /// [`MessageReceiver`]: crate::MessageReceiver +/// [`Message`]: crate::Message /// [`rc::Id`]: crate::rc::Id /// /// # Panics diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index 59b8f0dd7..3b2e530b0 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -188,7 +188,8 @@ pub unsafe trait MessageReceiver: private::Sealed { /// arguments `A` and return type `R`. /// /// # Example - /// ``` no_run + /// + /// ```no_run /// # use objc2::{class, msg_send, sel}; /// # use objc2::runtime::{Bool, Class, Object}; /// # use objc2::MessageReceiver; diff --git a/objc2/src/rc/autorelease.rs b/objc2/src/rc/autorelease.rs index bf59cb607..f247ebeaa 100644 --- a/objc2/src/rc/autorelease.rs +++ b/objc2/src/rc/autorelease.rs @@ -233,7 +233,7 @@ impl !AutoreleaseSafe for AutoreleasePool {} /// /// Basic usage: /// -/// ```rust,no_run +/// ```no_run /// use objc2::{class, msg_send}; /// use objc2::rc::{autoreleasepool, AutoreleasePool}; /// use objc2::runtime::Object; @@ -256,7 +256,7 @@ impl !AutoreleaseSafe for AutoreleasePool {} /// Fails to compile because `obj` does not live long enough for us to /// safely take it out of the pool: /// -/// ```rust,compile_fail +/// ```compile_fail /// # use objc2::{class, msg_send}; /// # use objc2::rc::{autoreleasepool, AutoreleasePool}; /// # use objc2::runtime::Object; @@ -277,11 +277,8 @@ impl !AutoreleaseSafe for AutoreleasePool {} /// Incorrect usage which panics because we tried to pass an outer pool to an /// inner pool: /// -#[cfg_attr(feature = "unstable_autoreleasesafe", doc = "```rust,compile_fail")] -#[cfg_attr( - not(feature = "unstable_autoreleasesafe"), - doc = "```rust,should_panic" -)] +#[cfg_attr(feature = "unstable_autoreleasesafe", doc = "```compile_fail")] +#[cfg_attr(not(feature = "unstable_autoreleasesafe"), doc = "```should_panic")] /// # use objc2::{class, msg_send}; /// # use objc2::rc::{autoreleasepool, AutoreleasePool}; /// # use objc2::runtime::Object; diff --git a/objc2/src/rc/mod.rs b/objc2/src/rc/mod.rs index 1d8f75fde..bab1c1fcc 100644 --- a/objc2/src/rc/mod.rs +++ b/objc2/src/rc/mod.rs @@ -1,30 +1,59 @@ //! Utilities for reference counting Objective-C objects. //! -//! The utilities of the `rc` module provide ARC-like semantics for working -//! with Objective-C's reference counted objects in Rust. +//! These utilities in this module provide ARC-like semantics for working with +//! Objective-C's reference counted objects. //! //! A smart pointer [`Id`] is provided to ensure that Objective-C objects are -//! retained and released at the proper times. +//! retained and released when created and dropped, respectively. //! //! To enforce aliasing rules, an `Id` can be either owned or shared; if it is //! owned, meaning the `Id` is the only reference to the object, it can be //! mutably dereferenced. An owned `Id` can be downgraded to a shared `Id` //! which can be cloned to allow multiple references. //! -//! Weak references may be created using the [`WeakId`] struct. +//! Weak references may be created using the [`WeakId`] struct; these will not +//! retain the object, but they can upgraded to an `Id` in a manner that +//! safely fails if the object has been deallocated. //! //! See [the clang documentation][clang-arc] and [the Apple article on memory -//! management][mem-mgmt] (similar document exists [for Core Foundation][mem-cf]) +//! management][mem-mgmt] (similar document exists [for Core Foundation][cf]) //! for more information on automatic and manual reference counting. //! //! It can also be useful to [enable Malloc Debugging][mem-debug] if you're trying //! to figure out if/where your application has memory errors and leaks. //! -//! //! [clang-arc]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html //! [mem-mgmt]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html -//! [mem-cf]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html +//! [cf]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html //! [mem-debug]: https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html +//! +//! +//! ## Example +//! +#![cfg_attr(apple, doc = "```")] +#![cfg_attr(not(apple), doc = "```no_run")] +//! use objc2::{class, msg_send}; +//! use objc2::rc::{autoreleasepool, Id, Shared, WeakId}; +//! use objc2::runtime::Object; +//! +//! // Id will release the object when dropped +//! let obj: Id = unsafe { +//! Id::new(msg_send![class!(NSObject), new]) +//! }; +//! +//! // Cloning retains the object an additional time +//! let cloned = obj.clone(); +//! autoreleasepool(|pool| { +//! // Autorelease consumes the Id, but won't +//! // actually release until the end of an autoreleasepool +//! let obj_ref: &Object = cloned.autorelease(pool); +//! }); +//! +//! // Weak references won't retain the object +//! let weak = WeakId::new(&obj); +//! drop(obj); +//! assert!(weak.load().is_none()); +//! ``` mod autorelease; mod id; From ad585191d61473e0fc52f9ca311f729699c957ca Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 Dec 2021 23:42:52 +0100 Subject: [PATCH 10/12] Add fun text-to-speech example Here's to hoping it actually works, I don't have a new enough system to actually test it --- objc2/examples/talk_to_me.rs | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 objc2/examples/talk_to_me.rs diff --git a/objc2/examples/talk_to_me.rs b/objc2/examples/talk_to_me.rs new file mode 100644 index 000000000..3109db9f0 --- /dev/null +++ b/objc2/examples/talk_to_me.rs @@ -0,0 +1,39 @@ +use objc2::ffi::NSUInteger; +use objc2::rc::{Id, Owned, Shared}; +use objc2::runtime::Object; +use objc2::{class, msg_send}; +use std::ffi::c_void; +use std::ptr::NonNull; + +#[cfg(apple)] // Does not work on GNUStep +#[link(name = "AVFoundation", kind = "framework")] +extern "C" {} + +// Only works on macOS >= 10.15 or iOS > 7.0 +fn main() { + let text = "Hello from Rust!"; + + let string: *const Object = unsafe { msg_send![class!(NSString), alloc] }; + let string = unsafe { + msg_send![ + string, + initWithBytes: text.as_ptr() as *const c_void, + length: text.len(), + encoding: 4 as NSUInteger, // UTF8_ENCODING on macOS / iOS + ] + }; + let string: Id = unsafe { Id::new(NonNull::new(string).unwrap()) }; + + let synthesizer: *mut Object = unsafe { msg_send![class!(AVSpeechSynthesizer), new] }; + let synthesizer: Id = unsafe { Id::new(NonNull::new(synthesizer).unwrap()) }; + + let utterance: *mut Object = unsafe { msg_send![class!(AVSpeechUtterance), alloc] }; + let utterance: *mut Object = unsafe { msg_send![utterance, initWithString: &*string] }; + let utterance: Id = unsafe { Id::new(NonNull::new(utterance).unwrap()) }; + + // let _: () = unsafe { msg_send![utterance, setVolume: 90.0f32 }; + // let _: () = unsafe { msg_send![utterance, setRate: 0.50f32 }; + // let _: () = unsafe { msg_send![utterance, setPitchMultiplier: 0.80f32 }; + + let _: () = unsafe { msg_send![synthesizer, speakUtterance: &*utterance] }; +} From 2bb91802c91c01c20b4df33715436b6381fbb57d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 23 Dec 2021 00:00:10 +0100 Subject: [PATCH 11/12] Add smoke test to objc-sys --- objc-sys/src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/objc-sys/src/lib.rs b/objc-sys/src/lib.rs index 82617ddc8..6e3363235 100644 --- a/objc-sys/src/lib.rs +++ b/objc-sys/src/lib.rs @@ -71,3 +71,18 @@ pub use various::*; /// /// TODO: Replace this with `extern type` to also mark it as `!Sized`. type OpaqueData = PhantomData<(UnsafeCell<()>, *const UnsafeCell<()>, PhantomPinned)>; + +#[cfg(test)] +mod tests { + use super::*; + use std::ffi::CStr; + + #[test] + fn smoke() { + // Verify that this library links and works fine by itself + let name = CStr::from_bytes_with_nul(b"abc:def:\0").unwrap(); + let sel = unsafe { sel_registerName(name.as_ptr()) }; + let rtn = unsafe { CStr::from_ptr(sel_getName(sel)) }; + assert_eq!(name, rtn); + } +} From 2dd13511b408692ee400d9182b06acbf7a3ab812 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 2 Jan 2022 22:27:25 +0100 Subject: [PATCH 12/12] Remove Deref[Mut] impls from NS[Mutable]Data --- objc2-foundation/CHANGELOG.md | 5 +++++ objc2-foundation/src/data.rs | 24 +----------------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md index 9d549bf00..3763a7e62 100644 --- a/objc2-foundation/CHANGELOG.md +++ b/objc2-foundation/CHANGELOG.md @@ -17,6 +17,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * **BREAKING**: Renamed `INSFastEnumeration::enumerator` to `INSFastEnumeration::iter_fast`. +### Removed +* **BREAKING**: Removed `Deref` and `DerefMut` from `NSData` and + `NSMutableData`, since these invoke a non-trivial amount of code, and could + easily lead to hard-to-diagnose performance issues. + ## 0.2.0-alpha.3 - 2021-12-22 diff --git a/objc2-foundation/src/data.rs b/objc2-foundation/src/data.rs index 5a99c3d05..1eb774087 100644 --- a/objc2-foundation/src/data.rs +++ b/objc2-foundation/src/data.rs @@ -1,6 +1,6 @@ #[cfg(feature = "block")] use alloc::vec::Vec; -use core::ops::{Deref, DerefMut, Index, IndexMut, Range}; +use core::ops::{Index, IndexMut, Range}; use core::slice::{self, SliceIndex}; use core::{ffi::c_void, ptr::NonNull}; @@ -135,14 +135,6 @@ impl DefaultId for NSData { } } -impl Deref for NSData { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - self.bytes() - } -} - pub unsafe trait INSMutableData: INSData { fn bytes_mut(&mut self) -> &mut [u8] { let ptr: *mut c_void = unsafe { msg_send![self, mutableBytes] }; @@ -241,20 +233,6 @@ impl DefaultId for NSMutableData { } } -impl Deref for NSMutableData { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - self.bytes() - } -} - -impl DerefMut for NSMutableData { - fn deref_mut(&mut self) -> &mut [u8] { - self.bytes_mut() - } -} - #[cfg(test)] mod tests { use super::{INSData, INSMutableData, NSData, NSMutableData};