Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ClassType::alloc #272

Merged
merged 4 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added
* Allow directly specifying class name in `extern_class!` macro.
* Added `ClassType::alloc`.

This means you can now simplify your code as follows:
```rust
// Before
let obj: Id<NSObject, Shared> = unsafe {
msg_send_id![msg_send_id![NSObject::class(), alloc], init]
};

// After
let obj: Id<NSObject, Shared> = unsafe {
msg_send_id![NSObject::alloc(), init]
};
```

### Changed
* Allow other types than `&Class` as the receiver in `msg_send_id!` methods
of the `new` family.
* **BREAKING**: Changed the `Allocated` struct to be used as `Allocated<T>`
instead of `Id<Allocated<T>, O>`.


## 0.3.0-beta.3 - 2022-09-01
Expand Down
5 changes: 1 addition & 4 deletions objc2/examples/class_with_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ impl<'a> MyObject<'a> {
pub fn new(number: &'a mut u8) -> Id<Self, Owned> {
// SAFETY: The lifetime of the reference is properly bound to the
// returned type
unsafe {
let obj = msg_send_id![Self::class(), alloc];
msg_send_id![obj, initWithPtr: number]
}
unsafe { msg_send_id![Self::alloc(), initWithPtr: number] }
}

pub fn get(&self) -> &u8 {
Expand Down
9 changes: 1 addition & 8 deletions objc2/examples/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,7 @@ declare_class!(
#[cfg(all(feature = "apple", target_os = "macos"))]
impl CustomAppDelegate {
pub fn new(ivar: u8, another_ivar: bool) -> Id<Self, Shared> {
let cls = Self::class();
unsafe {
msg_send_id![
msg_send_id![cls, alloc],
initWith: ivar,
another: another_ivar,
]
}
unsafe { msg_send_id![Self::alloc(), initWith: ivar, another: another_ivar] }
}
}

Expand Down
2 changes: 1 addition & 1 deletion objc2/examples/speech_synthethis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ mod avfaudio {

impl Utterance {
pub fn new(string: &NSString) -> Id<Self, Owned> {
unsafe { msg_send_id![msg_send_id![Self::class(), alloc], initWithString: string] }
unsafe { msg_send_id![Self::alloc(), initWithString: string] }
}

pub fn set_rate(&mut self, rate: f32) {
Expand Down
102 changes: 61 additions & 41 deletions objc2/src/__macro_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,18 @@ type Init = RetainSemantics<false, false, true, false>;
type CopyOrMutCopy = RetainSemantics<false, false, false, true>;
type Other = RetainSemantics<false, false, false, false>;

pub trait MsgSendId<T, U: ?Sized, O: Ownership> {
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
pub trait MsgSendId<T, U> {
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = U>>(
obj: T,
sel: Sel,
args: A,
) -> R;
}

impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, U, O> for New {
impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, Id<U, O>> for New {
#[inline]
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Id<U, O>>>(
obj: T,
sel: Sel,
args: A,
Expand All @@ -107,48 +107,50 @@ impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, U, O> f
}
}

impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Allocated<T>, O> for Alloc {
impl<T: ?Sized + Message> MsgSendId<&'_ Class, Allocated<T>> for Alloc {
#[inline]
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Allocated<T>, O>>(
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Allocated<T>>>(
cls: &Class,
sel: Sel,
args: A,
) -> R {
// SAFETY: Checked by caller
let obj = unsafe { MessageReceiver::send_message(cls, sel, args) };
// SAFETY: The selector is `alloc`, so this has +1 retain count
let obj = unsafe { Id::new_allocated(obj) };
let obj = unsafe { Allocated::new(obj) };
R::maybe_unwrap::<Self>(obj, (cls, sel))
}
}

impl<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Id<Allocated<T>, O>>, T, O> for Init {
impl<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Allocated<T>>, Id<T, O>> for Init {
#[inline]
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<T, O>>(
obj: Option<Id<Allocated<T>, O>>,
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Id<T, O>>>(
obj: Option<Allocated<T>>,
sel: Sel,
args: A,
) -> R {
let ptr = Id::option_into_ptr(obj.map(|obj| unsafe { Id::assume_init(obj) }));
let ptr = Allocated::option_into_ptr(obj);
// SAFETY: `ptr` may be null here, but that's fine since the return
// is `*mut T`, which is one of the few types where messages to nil is
// allowed.
//
// We do this for efficiency, to avoid having a branch after every
// `alloc`, that the user did not intend.
// We do this for efficiency, to avoid having a branch that the user
// did not intend after every `alloc`.
let obj = unsafe { MessageReceiver::send_message(ptr, sel, args) };
// SAFETY: The selector is `init`, so this has +1 retain count
let obj = unsafe { Id::new(obj) };
R::maybe_unwrap::<Self>(obj, (ptr.cast(), sel))
}
}

impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, U, O> for CopyOrMutCopy {
impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, Id<U, O>>
for CopyOrMutCopy
{
#[inline]
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Id<U, O>>>(
obj: T,
sel: Sel,
args: A,
Expand All @@ -162,10 +164,10 @@ impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, U, O> f
}
}

impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, U, O> for Other {
impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, Id<U, O>> for Other {
#[inline]
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = Id<U, O>>>(
obj: T,
sel: Sel,
args: A,
Expand All @@ -186,34 +188,53 @@ impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, U, O> for Other
}
}

pub trait MaybeUnwrap<T: ?Sized, O: Ownership> {
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
obj: Option<Id<T, O>>,
args: Failed::Args,
) -> Self;
pub trait MaybeUnwrap {
type Input;
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Self::Input>, args: F::Args) -> Self;
}

impl<T: ?Sized, O: Ownership> MaybeUnwrap<T, O> for Option<Id<T, O>> {
impl<T: ?Sized, O: Ownership> MaybeUnwrap for Option<Id<T, O>> {
type Input = Id<T, O>;

#[inline]
#[track_caller]
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Id<T, O>>, _args: F::Args) -> Self {
obj
}
}

impl<T: ?Sized, O: Ownership> MaybeUnwrap for Id<T, O> {
type Input = Id<T, O>;

#[inline]
#[track_caller]
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Id<T, O>>, args: F::Args) -> Self {
match obj {
Some(obj) => obj,
None => F::failed(args),
}
}
}

impl<T: ?Sized> MaybeUnwrap for Option<Allocated<T>> {
type Input = Allocated<T>;

#[inline]
#[track_caller]
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
obj: Option<Id<T, O>>,
_args: Failed::Args,
) -> Self {
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Allocated<T>>, _args: F::Args) -> Self {
obj
}
}

impl<T: ?Sized, O: Ownership> MaybeUnwrap<T, O> for Id<T, O> {
impl<T: ?Sized> MaybeUnwrap for Allocated<T> {
type Input = Allocated<T>;

#[inline]
#[track_caller]
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
obj: Option<Id<T, O>>,
args: Failed::Args,
) -> Self {
fn maybe_unwrap<'a, F: MsgSendIdFailed<'a>>(obj: Option<Allocated<T>>, args: F::Args) -> Self {
match obj {
Some(obj) => obj,
None => Failed::failed(args),
None => F::failed(args),
}
}
}
Expand Down Expand Up @@ -447,7 +468,7 @@ mod tests {
let mut expected = ThreadTestData::current();
let cls = RcTestObject::class();

let obj: Id<Allocated<RcTestObject>, Shared> = unsafe { msg_send_id![cls, alloc] };
let obj: Allocated<RcTestObject> = unsafe { msg_send_id![cls, alloc] };
expected.alloc += 1;
expected.assert_current();

Expand All @@ -463,8 +484,7 @@ mod tests {
let cls = RcTestObject::class();

let zone: *const NSZone = ptr::null();
let _obj: Id<Allocated<RcTestObject>, Owned> =
unsafe { msg_send_id![cls, allocWithZone: zone] };
let _obj: Allocated<RcTestObject> = unsafe { msg_send_id![cls, allocWithZone: zone] };
expected.alloc += 1;
expected.assert_current();
}
Expand All @@ -474,14 +494,14 @@ mod tests {
let mut expected = ThreadTestData::current();
let cls = RcTestObject::class();

let obj: Option<Id<Allocated<RcTestObject>, Shared>> = unsafe { msg_send_id![cls, alloc] };
let obj: Option<Allocated<RcTestObject>> = unsafe { msg_send_id![cls, alloc] };
expected.alloc += 1;
// Don't check allocation error
let _obj: Id<RcTestObject, Shared> = unsafe { msg_send_id![obj, init] };
expected.init += 1;
expected.assert_current();

let obj: Option<Id<Allocated<RcTestObject>, Shared>> = unsafe { msg_send_id![cls, alloc] };
let obj: Option<Allocated<RcTestObject>> = unsafe { msg_send_id![cls, alloc] };
expected.alloc += 1;
// Check allocation error before init
let obj = obj.unwrap();
Expand Down Expand Up @@ -567,14 +587,14 @@ mod tests {
#[test]
#[should_panic = "failed allocating with +[RcTestObject allocReturningNull]"]
fn test_alloc_with_null() {
let _obj: Id<Allocated<RcTestObject>, Owned> =
let _obj: Allocated<RcTestObject> =
unsafe { msg_send_id![RcTestObject::class(), allocReturningNull] };
}

#[test]
#[should_panic = "failed initializing object with -initReturningNull"]
fn test_init_with_null() {
let obj: Option<Id<Allocated<RcTestObject>, Owned>> =
let obj: Option<Allocated<RcTestObject>> =
unsafe { msg_send_id![RcTestObject::class(), alloc] };
let _obj: Id<RcTestObject, Owned> = unsafe { msg_send_id![obj, initReturningNull] };
}
Expand All @@ -583,7 +603,7 @@ mod tests {
#[should_panic = "failed allocating object"]
#[cfg(not(feature = "verify_message"))] // Does NULL receiver checks
fn test_init_with_null_receiver() {
let obj: Option<Id<Allocated<RcTestObject>, Owned>> = None;
let obj: Option<Allocated<RcTestObject>> = None;
let _obj: Id<RcTestObject, Owned> = unsafe { msg_send_id![obj, init] };
}

Expand Down
17 changes: 17 additions & 0 deletions objc2/src/class_type.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::msg_send_id;
use crate::rc::Allocated;
use crate::runtime::Class;
use crate::Message;

Expand Down Expand Up @@ -91,4 +93,19 @@ pub unsafe trait ClassType: Message {

/// Get a mutable reference to the superclass.
fn as_super_mut(&mut self) -> &mut Self::Super;

/// Allocate a new instance of the class.
///
/// The return value can be used directly inside [`msg_send_id!`] to
/// initialize the object.
///
/// [`msg_send_id!`]: crate::msg_send_id
#[inline]
fn alloc() -> Option<Allocated<Self>> {
// SAFETY:
// - It is always safe to (attempt to) allocate an object.
// - The object is of the correct type, since we've used the class
// from `Self::class`.
unsafe { msg_send_id![Self::class(), alloc] }
}
}
2 changes: 1 addition & 1 deletion objc2/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
//! use objc2::foundation::NSObject;
//! use objc2::rc::{Id, Owned};
//! use objc2::runtime::{Class, Object, Sel};
//! use objc2::{class, sel, msg_send, msg_send_id, ClassType};
//! use objc2::{sel, msg_send, msg_send_id, ClassType};
//! # #[cfg(feature = "gnustep-1-7")]
//! # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
//!
Expand Down
8 changes: 3 additions & 5 deletions objc2/src/declare/ivar_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,7 @@ mod tests {
fn test_alloc_dealloc() {
let expected = ThreadTestData::current();

let obj: Id<Allocated<IvarTester>, Owned> =
unsafe { msg_send_id![IvarTester::class(), alloc] };
let obj: Allocated<IvarTester> = unsafe { msg_send_id![IvarTester::class(), alloc] };
expected.assert_current();

drop(obj);
Expand Down Expand Up @@ -315,8 +314,7 @@ mod tests {
#[cfg_attr(not(debug_assertions), ignore = "only panics in debug mode")]
#[should_panic = "an Id in instance variables must always be initialized before use"]
fn test_init_invalid_ref() {
let obj: Id<IvarTester, Owned> =
unsafe { msg_send_id![msg_send_id![IvarTester::class(), alloc], initInvalid] };
let obj: Id<IvarTester, Owned> = unsafe { msg_send_id![IvarTester::alloc(), initInvalid] };

std::println!("{:?}", obj.ivar1);
}
Expand All @@ -326,7 +324,7 @@ mod tests {
#[should_panic = "an Id in instance variables must always be initialized before use"]
fn test_init_invalid_mut() {
let mut obj: Id<IvarTester, Owned> =
unsafe { msg_send_id![msg_send_id![IvarTester::class(), alloc], initInvalid] };
unsafe { msg_send_id![IvarTester::alloc(), initInvalid] };

*obj.ivar1 = RcTestObject::new().into();
}
Expand Down
Loading