From a09e9f3f0be9fd8103a8a71789b0b3cd0f119065 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 12 Jan 2022 01:01:51 +0100 Subject: [PATCH] Add id_msg_send to help with following memory management rules --- objc2-foundation/src/data.rs | 18 ++++----- objc2/examples/introspection.rs | 9 ++--- objc2/src/lib.rs | 8 ++-- objc2/src/macros.rs | 71 +++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/objc2-foundation/src/data.rs b/objc2-foundation/src/data.rs index 1eb774087..531eb33fe 100644 --- a/objc2-foundation/src/data.rs +++ b/objc2-foundation/src/data.rs @@ -5,10 +5,10 @@ use core::slice::{self, SliceIndex}; use core::{ffi::c_void, ptr::NonNull}; use super::{INSCopying, INSMutableCopying, INSObject, NSRange}; -use objc2::msg_send; use objc2::rc::{DefaultId, Id, Owned, Ownership, Shared}; +use objc2::{id_msg_send, msg_send}; -pub unsafe trait INSData: INSObject { +pub unsafe trait INSData: INSObject + Sized { type Ownership: Ownership; unsafe_def_fn!(fn new -> Self::Ownership); @@ -35,13 +35,12 @@ pub unsafe trait INSData: INSObject { let cls = Self::class(); let bytes_ptr = bytes.as_ptr() as *const c_void; unsafe { - let obj: *mut Self = msg_send![cls, alloc]; - let obj: *mut Self = msg_send![ + let obj: Id = id_msg_send![cls, alloc]; + id_msg_send![ obj, initWithBytes: bytes_ptr, length: bytes.len(), - ]; - Id::new(NonNull::new_unchecked(obj)) + ] } } @@ -80,14 +79,13 @@ pub unsafe trait INSData: INSObject { let mut bytes = ManuallyDrop::new(bytes); unsafe { - let obj: *mut Self = msg_send![cls, alloc]; - let obj: *mut Self = msg_send![ + let obj: Id = id_msg_send![cls, alloc]; + id_msg_send![ obj, initWithBytesNoCopy: bytes.as_mut_ptr() as *mut c_void, length: bytes.len(), deallocator: dealloc, - ]; - Id::new(NonNull::new_unchecked(obj)) + ] } } } diff --git a/objc2/examples/introspection.rs b/objc2/examples/introspection.rs index 5a10b2736..fd2d7633f 100644 --- a/objc2/examples/introspection.rs +++ b/objc2/examples/introspection.rs @@ -1,8 +1,6 @@ -use core::ptr::NonNull; - use objc2::rc::{Id, Owned}; use objc2::runtime::{Class, Object}; -use objc2::{class, msg_send}; +use objc2::{class, id_msg_send, msg_send}; #[cfg(feature = "malloc")] use objc2::{sel, Encode}; @@ -22,9 +20,8 @@ fn main() { // Allocate an instance let obj: Id = unsafe { - let obj: *mut Object = msg_send![cls, alloc]; - let obj: NonNull = msg_send![obj, init]; - Id::new(obj) + let obj: Id = id_msg_send![cls, alloc]; + id_msg_send![obj, init] }; println!("NSObject address: {:p}", obj); diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index eda98a0ba..ed153f965 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -30,16 +30,14 @@ #![cfg_attr(apple, doc = "```")] #![cfg_attr(not(apple), doc = "```no_run")] //! use core::ptr::NonNull; -//! use objc2::{class, msg_send}; +//! use objc2::{class, id_msg_send, 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 = unsafe { msg_send![cls, new] }; -//! let obj = NonNull::new(obj).expect("Failed allocating object"); -//! let obj: Id = unsafe { Id::new(obj) }; +//! let obj: Id = unsafe { id_msg_send![cls, new] }; // .expect("Failed allocating object") //! //! // Usage //! let hash: NSUInteger = unsafe { msg_send![obj, hash] }; @@ -150,6 +148,8 @@ pub use crate::message::{Message, MessageArguments, MessageError, MessageReceive pub use crate::cache::CachedClass as __CachedClass; pub use crate::cache::CachedSel as __CachedSel; +pub use crate::macros::__starts_with_str; + #[macro_use] mod macros; diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index f29b9af4a..a969fa457 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -172,3 +172,74 @@ macro_rules! msg_send { result }); } + +/// TODO +#[macro_export] +macro_rules! id_msg_send { + ($obj:expr, $name:ident) => ({ + const NAME: &[u8] = stringify!($name).as_bytes(); + $crate::id_msg_send!(@ $obj, NAME, $name) + }); + ($obj:expr, $($name:ident: $arg:expr),+ $(,)?) => ({ + const NAME: &[u8] = concat!($(stringify!($name), ':'),+).as_bytes(); + $crate::id_msg_send!(@ $obj, NAME, $($name: $arg),+) + }); + (@ $obj:expr, $name:ident, $($sel:tt)+) => {{ + const IS_INIT: bool = $crate::__starts_with_str($name, b"init"); + const IS_RETAINED: bool = { + $crate::__starts_with_str($name, b"alloc") + || $crate::__starts_with_str($name, b"new") + || $crate::__starts_with_str($name, b"copy") + || $crate::__starts_with_str($name, b"mutableCopy") + || $crate::__starts_with_str($name, b"init") + }; + + let result = if IS_INIT { + ::std::println!("IS_INIT"); + // TODO: Ensure `obj` is Id here + let obj = ::core::mem::ManuallyDrop::new($obj); + $crate::msg_send![obj, $($sel)+] + } else { + ::std::println!("!IS_INIT"); + $crate::msg_send![$obj, $($sel)+] + }; + if IS_RETAINED { + ::std::println!("IS_RETAINED"); + $crate::rc::Id::new(result) + } else { + ::std::println!("!IS_RETAINED"); + $crate::rc::Id::retain_autoreleased(result) + } + }}; +} + +#[doc(hidden)] +pub const fn __starts_with_str(haystack: &[u8], needle: &[u8]) -> bool { + if needle.len() > haystack.len() { + return false; + } + let mut i = 0; + while i < needle.len() { + if needle[i] != haystack[i] { + return false; + } + i += 1; + } + true +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_starts_with_str() { + assert!(__starts_with_str(b"abcdef", b"abc")); + assert!(__starts_with_str(b"a", b"")); + assert!(__starts_with_str(b"", b"")); + + assert!(!__starts_with_str(b"abcdef", b"def")); + assert!(!__starts_with_str(b"abcdef", b"abb")); + assert!(!__starts_with_str(b"", b"a")); + } +}