Skip to content

Commit

Permalink
Add id_msg_send to help with following memory management rules
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 12, 2022
1 parent 419086c commit 7273966
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 27 deletions.
18 changes: 8 additions & 10 deletions objc2-foundation/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<Self, Self::Ownership> = id_msg_send![cls, alloc];
id_msg_send![
obj,
initWithBytes: bytes_ptr,
length: bytes.len(),
];
Id::new(NonNull::new_unchecked(obj))
]
}
}

Expand Down Expand Up @@ -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<Self, Self::Ownership> = 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))
]
}
}
}
Expand Down
10 changes: 3 additions & 7 deletions objc2-foundation/tests/objc_id_retain_autoreleased.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::ffi::c_void;
use std::ptr::NonNull;

use objc2::msg_send;
use objc2::rc::{autoreleasepool, Id, Shared};
use objc2::{id_msg_send, msg_send};
use objc2_foundation::{INSObject, NSData};

fn retain_count(obj: &NSData) -> usize {
Expand All @@ -12,14 +11,11 @@ fn retain_count(obj: &NSData) -> usize {
fn create_data(bytes: &[u8]) -> Id<NSData, Shared> {
let bytes_ptr = bytes.as_ptr() as *const c_void;
unsafe {
// All code between the `msg_send!` and the `retain_autoreleased` must
// be able to be optimized away for this to work.
let obj: *mut NSData = msg_send![
id_msg_send![
NSData::class(),
dataWithBytes: bytes_ptr,
length: bytes.len(),
];
Id::retain_autoreleased(NonNull::new_unchecked(obj))
]
}
}

Expand Down
9 changes: 3 additions & 6 deletions objc2/examples/introspection.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -22,9 +20,8 @@ fn main() {

// Allocate an instance
let obj: Id<Object, Owned> = unsafe {
let obj: *mut Object = msg_send![cls, alloc];
let obj: NonNull<Object> = msg_send![obj, init];
Id::new(obj)
let obj: Id<Object, Owned> = id_msg_send![cls, alloc];
id_msg_send![obj, init]
};
println!("NSObject address: {:p}", obj);

Expand Down
8 changes: 4 additions & 4 deletions objc2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Object, Owned> = unsafe { Id::new(obj) };
//! let obj: Id<Object, Owned> = unsafe { id_msg_send![cls, new] }; // .expect("Failed allocating object")
//!
//! // Usage
//! let hash: NSUInteger = unsafe { msg_send![obj, hash] };
Expand Down Expand Up @@ -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;

Expand Down
72 changes: 72 additions & 0 deletions objc2/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,75 @@ 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")
};

::std::println!("IS_INIT: {}", IS_INIT);
::std::println!("IS_RETAINED: {}", IS_RETAINED);

let result = if IS_INIT {
// TODO: Ensure `obj` is Id here
let obj = ::core::mem::ManuallyDrop::new($obj);
$crate::msg_send![obj, $($sel)+]
} else {
$crate::msg_send![$obj, $($sel)+]
};
if IS_RETAINED {
$crate::rc::Id::new(result)
} else {
// All code between the `msg_send!` and the `retain_autoreleased` must
// be able to be optimized away for this to work.
$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"));
}
}

0 comments on commit 7273966

Please sign in to comment.