Skip to content

Commit

Permalink
Change msg_send! such that callers can properly communicate mutability
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed May 24, 2022
1 parent b6b9f9c commit f1edce7
Show file tree
Hide file tree
Showing 15 changed files with 98 additions and 71 deletions.
4 changes: 2 additions & 2 deletions objc2-foundation/examples/custom_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@ fn main() {

obj.set_number(7);
println!("Number: {}", unsafe {
let number: u32 = msg_send![obj, number];
let number: u32 = msg_send![&obj, number];
number
});

unsafe {
let _: () = msg_send![obj, setNumber: 12u32];
let _: () = msg_send![&mut obj, setNumber: 12u32];
}
println!("Number: {}", obj.number());
}
15 changes: 8 additions & 7 deletions objc2-foundation/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,14 @@ impl<T: Message, O: Ownership> NSMutableArray<T, O> {

#[doc(alias = "removeLastObject")]
pub fn pop(&mut self) -> Option<Id<T, O>> {
self.last().map(|obj| {
let obj = unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() };
unsafe {
let _: () = msg_send![self, removeLastObject];
}
obj
})
self.last()
.map(|obj| unsafe { Id::retain(obj as *const T as *mut T).unwrap_unchecked() })
.map(|obj| {
unsafe {
let _: () = msg_send![self, removeLastObject];
}
obj
})
}

#[doc(alias = "removeAllObjects")]
Expand Down
3 changes: 2 additions & 1 deletion objc2-foundation/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ impl NSMutableData {
impl NSMutableData {
#[doc(alias = "mutableBytes")]
pub fn bytes_mut(&mut self) -> &mut [u8] {
let ptr: *mut c_void = unsafe { msg_send![self, mutableBytes] };
let this = &mut *self; // Reborrow
let ptr: *mut c_void = unsafe { msg_send![this, mutableBytes] };
// The bytes pointer may be null for length zero
if ptr.is_null() {
&mut []
Expand Down
2 changes: 1 addition & 1 deletion objc2-foundation/src/dictionary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl<K: Message, V: Message> NSDictionary<K, V> {

pub fn into_values_array(dict: Id<Self, Owned>) -> Id<NSArray<V, Owned>, Shared> {
unsafe {
let vals = msg_send![dict, allValues];
let vals = msg_send![&dict, allValues];
Id::retain_autoreleased(vals).unwrap()
}
}
Expand Down
2 changes: 1 addition & 1 deletion objc2-foundation/src/enumerator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl<'a, T: Message> Iterator for NSEnumerator<'a, T> {
type Item = &'a T;

fn next(&mut self) -> Option<&'a T> {
unsafe { msg_send![self.id, nextObject] }
unsafe { msg_send![&mut self.id, nextObject] }
}
}

Expand Down
2 changes: 1 addition & 1 deletion objc2-foundation/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ mod tests {
fn test_value_nsrange() {
let val = NSValue::new(NSRange::from(1..2));
assert!(NSRange::ENCODING.equivalent_to_str(val.encoding().unwrap()));
let range: NSRange = unsafe { objc2::msg_send![val, rangeValue] };
let range: NSRange = unsafe { objc2::msg_send![&val, rangeValue] };
assert_eq!(range, NSRange::from(1..2));
// NSValue -getValue is broken on GNUStep for some types
#[cfg(not(feature = "gnustep-1-7"))]
Expand Down
2 changes: 1 addition & 1 deletion objc2/examples/introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ fn main() {
}

// Invoke a method on the object
let hash: usize = unsafe { msg_send![obj, hash] };
let hash: usize = unsafe { msg_send![&obj, hash] };
println!("NSObject hash: {}", hash);
}
8 changes: 4 additions & 4 deletions objc2/examples/talk_to_me.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ fn main() {
let utterance: *mut Object = unsafe { msg_send![utterance, initWithString: &*string] };
let utterance: Id<Object, Owned> = unsafe { Id::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![&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] };
let _: () = unsafe { msg_send![&synthesizer, speakUtterance: &*utterance] };
}
6 changes: 3 additions & 3 deletions objc2/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,9 @@ mod tests {
#[test]
fn test_custom_class() {
// Registering the custom class is in test_utils
let obj = test_utils::custom_object();
let _: () = unsafe { msg_send![obj, setFoo: 13u32] };
let result: u32 = unsafe { msg_send![obj, foo] };
let mut obj = test_utils::custom_object();
let _: () = unsafe { msg_send![&mut obj, setFoo: 13u32] };
let result: u32 = unsafe { msg_send![&obj, foo] };
assert_eq!(result, 13);
}

Expand Down
4 changes: 2 additions & 2 deletions objc2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
//! };
//!
//! // Usage
//! let hash: NSUInteger = unsafe { msg_send![obj, hash] };
//! let is_kind = unsafe { msg_send_bool![obj, isKindOfClass: cls] };
//! let hash: NSUInteger = unsafe { msg_send![&obj, hash] };
//! let is_kind = unsafe { msg_send_bool![&obj, isKindOfClass: cls] };
//! assert!(is_kind);
//! ```
//!
Expand Down
8 changes: 4 additions & 4 deletions objc2/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ macro_rules! msg_send {
[super($obj:expr, $superclass:expr), $selector:ident $(,)?] => ({
let sel = $crate::sel!($selector);
let result;
match $crate::MessageReceiver::send_super_message(&$obj, $superclass, sel, ()) {
match $crate::MessageReceiver::send_super_message($obj, $superclass, sel, ()) {
Err(s) => panic!("{}", s),
Ok(r) => result = r,
}
Expand All @@ -141,7 +141,7 @@ macro_rules! msg_send {
[super($obj:expr, $superclass:expr), $($selector:ident : $argument:expr $(,)?)+] => ({
let sel = $crate::sel!($($selector :)+);
let result;
match $crate::MessageReceiver::send_super_message(&$obj, $superclass, sel, ($($argument,)+)) {
match $crate::MessageReceiver::send_super_message($obj, $superclass, sel, ($($argument,)+)) {
Err(s) => panic!("{}", s),
Ok(r) => result = r,
}
Expand All @@ -150,7 +150,7 @@ macro_rules! msg_send {
[$obj:expr, $selector:ident $(,)?] => ({
let sel = $crate::sel!($selector);
let result;
match $crate::MessageReceiver::send_message(&$obj, sel, ()) {
match $crate::MessageReceiver::send_message($obj, sel, ()) {
Err(s) => panic!("{}", s),
Ok(r) => result = r,
}
Expand All @@ -159,7 +159,7 @@ macro_rules! msg_send {
[$obj:expr, $($selector:ident : $argument:expr $(,)?)+] => ({
let sel = $crate::sel!($($selector :)+);
let result;
match $crate::MessageReceiver::send_message(&$obj, sel, ($($argument,)+)) {
match $crate::MessageReceiver::send_message($obj, sel, ($($argument,)+)) {
Err(s) => panic!("{}", s),
Ok(r) => result = r,
}
Expand Down
76 changes: 38 additions & 38 deletions objc2/src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,19 @@ unsafe impl Message for Class {}

// TODO: Make this fully private
pub(crate) mod private {
use super::{Id, ManuallyDrop, Message, MessageReceiver, NonNull, Ownership};
use super::*;

pub trait Sealed {}

impl<T: Message + ?Sized> Sealed for *const T {}
impl<T: Message + ?Sized> Sealed for *mut T {}
impl<T: Message + ?Sized> Sealed for NonNull<T> {}

impl<'a, T: Message + ?Sized> Sealed for &'a T {}
impl<'a, T: Message + ?Sized> Sealed for &'a mut T {}
impl<T: Message + ?Sized> Sealed for NonNull<T> {}
impl<T: Message + ?Sized, O: Ownership> Sealed for Id<T, O> {}

impl<T: MessageReceiver + ?Sized> Sealed for ManuallyDrop<T> {}
impl<'a, T: Message + ?Sized, O: Ownership> Sealed for &'a Id<T, O> {}
impl<'a, T: Message + ?Sized, O: Ownership> Sealed for &'a mut Id<T, O> {}
}

/// Types that can directly be used as the receiver of Objective-C messages.
Expand All @@ -96,9 +96,9 @@ pub(crate) mod private {
/// # Safety
///
/// [`Self::as_raw_receiver`] must be implemented correctly.
pub unsafe trait MessageReceiver: private::Sealed {
pub unsafe trait MessageReceiver: private::Sealed + Sized {
/// Get a raw pointer to the receiver of the message.
fn as_raw_receiver(&self) -> *mut Object;
fn as_raw_receiver(self) -> *mut Object;

/// Sends a message to self with the given selector and arguments.
///
Expand All @@ -118,7 +118,7 @@ pub unsafe trait MessageReceiver: private::Sealed {
/// The added invariant is that the selector must take the same number of
/// arguments as is given.
#[cfg_attr(not(feature = "verify_message"), inline(always))]
unsafe fn send_message<A, R>(&self, sel: Sel, args: A) -> Result<R, MessageError>
unsafe fn send_message<A, R>(self, sel: Sel, args: A) -> Result<R, MessageError>
where
A: MessageArguments,
R: Encode,
Expand Down Expand Up @@ -161,7 +161,7 @@ pub unsafe trait MessageReceiver: private::Sealed {
/// arguments as is given.
#[cfg_attr(not(feature = "verify_message"), inline(always))]
unsafe fn send_super_message<A, R>(
&self,
self,
superclass: &Class,
sel: Sel,
args: A,
Expand Down Expand Up @@ -202,7 +202,7 @@ pub unsafe trait MessageReceiver: private::Sealed {
/// assert!(result.is_ok());
/// ```
#[cfg(feature = "malloc")]
fn verify_message<A, R>(&self, sel: Sel) -> Result<(), MessageError>
fn verify_message<A, R>(self, sel: Sel) -> Result<(), MessageError>
where
A: EncodeArguments,
R: Encode,
Expand All @@ -217,50 +217,50 @@ pub unsafe trait MessageReceiver: private::Sealed {

unsafe impl<T: Message + ?Sized> MessageReceiver for *const T {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
*self as *mut T as *mut Object
fn as_raw_receiver(self) -> *mut Object {
self as *mut T as *mut Object
}
}

unsafe impl<T: Message + ?Sized> MessageReceiver for *mut T {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
*self as *mut Object
fn as_raw_receiver(self) -> *mut Object {
self as *mut Object
}
}

unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a T {
unsafe impl<T: Message + ?Sized> MessageReceiver for NonNull<T> {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
*self as *const T as *mut T as *mut Object
fn as_raw_receiver(self) -> *mut Object {
self.as_ptr() as *mut Object
}
}

unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a mut T {
unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a T {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
*self as *const T as *mut T as *mut Object
fn as_raw_receiver(self) -> *mut Object {
self as *const T as *mut T as *mut Object
}
}

unsafe impl<T: Message + ?Sized> MessageReceiver for NonNull<T> {
unsafe impl<'a, T: Message + ?Sized> MessageReceiver for &'a mut T {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
self.as_ptr() as *mut Object
fn as_raw_receiver(self) -> *mut Object {
self as *const T as *mut T as *mut Object
}
}

unsafe impl<T: Message + ?Sized, O: Ownership> MessageReceiver for Id<T, O> {
unsafe impl<'a, T: Message + ?Sized, O: Ownership> MessageReceiver for &'a Id<T, O> {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
fn as_raw_receiver(self) -> *mut Object {
self.as_ptr() as *mut Object
}
}

unsafe impl<T: MessageReceiver + ?Sized> MessageReceiver for ManuallyDrop<T> {
unsafe impl<'a, T: Message + ?Sized, O: Ownership> MessageReceiver for &'a mut Id<T, O> {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
(**self).as_raw_receiver()
fn as_raw_receiver(self) -> *mut Object {
self.as_ptr() as *mut Object
}
}

Expand Down Expand Up @@ -382,18 +382,18 @@ mod tests {

#[test]
fn test_send_message() {
let obj = test_utils::custom_object();
let mut obj = test_utils::custom_object();
let result: u32 = unsafe {
let _: () = msg_send![obj, setFoo: 4u32];
msg_send![obj, foo]
let _: () = msg_send![&mut obj, setFoo: 4u32];
msg_send![&obj, foo]
};
assert_eq!(result, 4);
}

#[test]
fn test_send_message_stret() {
let obj = test_utils::custom_object();
let result: test_utils::CustomStruct = unsafe { msg_send![obj, customStruct] };
let result: test_utils::CustomStruct = unsafe { msg_send![&obj, customStruct] };
let expected = test_utils::CustomStruct {
a: 1,
b: 2,
Expand Down Expand Up @@ -431,26 +431,26 @@ mod tests {

#[test]
fn test_send_message_super() {
let obj = test_utils::custom_subclass_object();
let mut obj = test_utils::custom_subclass_object();
let superclass = test_utils::custom_class();
unsafe {
let _: () = msg_send![obj, setFoo: 4u32];
let foo: u32 = msg_send![super(obj, superclass), foo];
let _: () = msg_send![&mut obj, setFoo: 4u32];
let foo: u32 = msg_send![super(&obj, superclass), foo];
assert_eq!(foo, 4);

// The subclass is overriden to return foo + 2
let foo: u32 = msg_send![obj, foo];
let foo: u32 = msg_send![&obj, foo];
assert_eq!(foo, 6);
}
}

#[test]
fn test_send_message_manuallydrop() {
let obj = test_utils::custom_object();
let obj = ManuallyDrop::new(obj);
let mut obj = ManuallyDrop::new(obj);
let result: u32 = unsafe {
let _: () = msg_send![obj, setFoo: 4u32];
msg_send![obj, foo]
let _: () = msg_send![&mut obj, setFoo: 4u32];
msg_send![&obj, foo]
};
assert_eq!(result, 4);

Expand Down
33 changes: 29 additions & 4 deletions objc2/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::mem::ManuallyDrop;
use core::ops::{Deref, DerefMut};
use std::os::raw::c_char;
use std::sync::Once;
Expand All @@ -20,12 +21,36 @@ impl CustomObject {
}

// TODO: Remove the need for this hack
impl crate::message::private::Sealed for CustomObject {}
impl crate::message::private::Sealed for &CustomObject {}
impl crate::message::private::Sealed for &mut CustomObject {}
impl crate::message::private::Sealed for &ManuallyDrop<CustomObject> {}
impl crate::message::private::Sealed for &mut ManuallyDrop<CustomObject> {}

unsafe impl MessageReceiver for CustomObject {
unsafe impl MessageReceiver for &CustomObject {
#[inline]
fn as_raw_receiver(&self) -> *mut Object {
self.obj
fn as_raw_receiver(self) -> *mut Object {
(&**self).as_raw_receiver()
}
}

unsafe impl MessageReceiver for &mut CustomObject {
#[inline]
fn as_raw_receiver(self) -> *mut Object {
(&**self).as_raw_receiver()
}
}

unsafe impl MessageReceiver for &ManuallyDrop<CustomObject> {
#[inline]
fn as_raw_receiver(self) -> *mut Object {
(&**self).as_raw_receiver()
}
}

unsafe impl MessageReceiver for &mut ManuallyDrop<CustomObject> {
#[inline]
fn as_raw_receiver(self) -> *mut Object {
(&**self).as_raw_receiver()
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/assembly/test_msg_send_zero_cost/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ use objc2::MessageReceiver;

#[no_mangle]
pub fn handle(obj: &Object, sel: Sel) -> *mut Object {
unsafe { MessageReceiver::send_message(&obj, sel, ()).unwrap() }
unsafe { MessageReceiver::send_message(obj, sel, ()).unwrap() }
}
Loading

0 comments on commit f1edce7

Please sign in to comment.