diff options
Diffstat (limited to 'third_party/rust/objc/src/message')
-rw-r--r-- | third_party/rust/objc/src/message/apple/arm.rs | 40 | ||||
-rw-r--r-- | third_party/rust/objc/src/message/apple/arm64.rs | 18 | ||||
-rw-r--r-- | third_party/rust/objc/src/message/apple/mod.rs | 40 | ||||
-rw-r--r-- | third_party/rust/objc/src/message/apple/x86.rs | 40 | ||||
-rw-r--r-- | third_party/rust/objc/src/message/apple/x86_64.rs | 32 | ||||
-rw-r--r-- | third_party/rust/objc/src/message/gnustep.rs | 35 | ||||
-rw-r--r-- | third_party/rust/objc/src/message/mod.rs | 296 | ||||
-rw-r--r-- | third_party/rust/objc/src/message/verify.rs | 49 |
8 files changed, 550 insertions, 0 deletions
diff --git a/third_party/rust/objc/src/message/apple/arm.rs b/third_party/rust/objc/src/message/apple/arm.rs new file mode 100644 index 0000000000..d26defc0de --- /dev/null +++ b/third_party/rust/objc/src/message/apple/arm.rs @@ -0,0 +1,40 @@ +use std::any::{Any, TypeId}; +use std::mem; + +use runtime::Imp; + +extern { + fn objc_msgSend(); + fn objc_msgSend_stret(); + + fn objc_msgSendSuper(); + fn objc_msgSendSuper_stret(); +} + +pub fn msg_send_fn<R: Any>() -> Imp { + // Double-word sized fundamental data types don't use stret, + // but any composite type larger than 4 bytes does. + // <http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf> + + let type_id = TypeId::of::<R>(); + if mem::size_of::<R>() <= 4 || + type_id == TypeId::of::<i64>() || + type_id == TypeId::of::<u64>() || + type_id == TypeId::of::<f64>() { + objc_msgSend + } else { + objc_msgSend_stret + } +} + +pub fn msg_send_super_fn<R: Any>() -> Imp { + let type_id = TypeId::of::<R>(); + if mem::size_of::<R>() <= 4 || + type_id == TypeId::of::<i64>() || + type_id == TypeId::of::<u64>() || + type_id == TypeId::of::<f64>() { + objc_msgSendSuper + } else { + objc_msgSendSuper_stret + } +} diff --git a/third_party/rust/objc/src/message/apple/arm64.rs b/third_party/rust/objc/src/message/apple/arm64.rs new file mode 100644 index 0000000000..54cfc897c5 --- /dev/null +++ b/third_party/rust/objc/src/message/apple/arm64.rs @@ -0,0 +1,18 @@ +use runtime::Imp; + +extern { + fn objc_msgSend(); + + fn objc_msgSendSuper(); +} + +pub fn msg_send_fn<R>() -> Imp { + // stret is not even available in arm64. + // <https://twitter.com/gparker/status/378079715824660480> + + objc_msgSend +} + +pub fn msg_send_super_fn<R>() -> Imp { + objc_msgSendSuper +} diff --git a/third_party/rust/objc/src/message/apple/mod.rs b/third_party/rust/objc/src/message/apple/mod.rs new file mode 100644 index 0000000000..30f59ca870 --- /dev/null +++ b/third_party/rust/objc/src/message/apple/mod.rs @@ -0,0 +1,40 @@ +use std::any::Any; + +use runtime::{Class, Object, Sel}; +use super::{Message, MessageArguments, MessageError, Super}; + +#[cfg(target_arch = "x86")] +#[path = "x86.rs"] +mod arch; +#[cfg(target_arch = "x86_64")] +#[path = "x86_64.rs"] +mod arch; +#[cfg(target_arch = "arm")] +#[path = "arm.rs"] +mod arch; +#[cfg(target_arch = "aarch64")] +#[path = "arm64.rs"] +mod arch; + +use self::arch::{msg_send_fn, msg_send_super_fn}; + +pub unsafe fn send_unverified<T, A, R>(obj: *const T, sel: Sel, args: A) + -> Result<R, MessageError> + where T: Message, A: MessageArguments, R: Any { + let receiver = obj as *mut T as *mut Object; + let msg_send_fn = msg_send_fn::<R>(); + objc_try!({ + A::invoke(msg_send_fn, receiver, sel, args) + }) +} + +pub unsafe fn send_super_unverified<T, A, R>(obj: *const T, superclass: &Class, + sel: Sel, args: A) -> Result<R, MessageError> + where T: Message, A: MessageArguments, R: Any { + let sup = Super { receiver: obj as *mut T as *mut Object, superclass: superclass }; + let receiver = &sup as *const Super as *mut Object; + let msg_send_fn = msg_send_super_fn::<R>(); + objc_try!({ + A::invoke(msg_send_fn, receiver, sel, args) + }) +} diff --git a/third_party/rust/objc/src/message/apple/x86.rs b/third_party/rust/objc/src/message/apple/x86.rs new file mode 100644 index 0000000000..93422de24b --- /dev/null +++ b/third_party/rust/objc/src/message/apple/x86.rs @@ -0,0 +1,40 @@ +use std::any::{Any, TypeId}; +use std::mem; + +use runtime::Imp; + +extern { + fn objc_msgSend(); + fn objc_msgSend_fpret(); + fn objc_msgSend_stret(); + + fn objc_msgSendSuper(); + fn objc_msgSendSuper_stret(); +} + +pub fn msg_send_fn<R: Any>() -> Imp { + // Structures 1 or 2 bytes in size are placed in EAX. + // Structures 4 or 8 bytes in size are placed in: EAX and EDX. + // Structures of other sizes are placed at the address supplied by the caller. + // <https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html> + + let type_id = TypeId::of::<R>(); + let size = mem::size_of::<R>(); + if type_id == TypeId::of::<f32>() || + type_id == TypeId::of::<f64>() { + objc_msgSend_fpret + } else if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 { + objc_msgSend + } else { + objc_msgSend_stret + } +} + +pub fn msg_send_super_fn<R: Any>() -> Imp { + let size = mem::size_of::<R>(); + if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 { + objc_msgSendSuper + } else { + objc_msgSendSuper_stret + } +} diff --git a/third_party/rust/objc/src/message/apple/x86_64.rs b/third_party/rust/objc/src/message/apple/x86_64.rs new file mode 100644 index 0000000000..49c90ce53e --- /dev/null +++ b/third_party/rust/objc/src/message/apple/x86_64.rs @@ -0,0 +1,32 @@ +use std::mem; + +use runtime::Imp; + +extern { + fn objc_msgSend(); + fn objc_msgSend_stret(); + + fn objc_msgSendSuper(); + fn objc_msgSendSuper_stret(); +} + +pub fn msg_send_fn<R>() -> Imp { + // If the size of an object is larger than two eightbytes, it has class MEMORY. + // If the type has class MEMORY, then the caller provides space for the return + // value and passes the address of this storage. + // <http://people.freebsd.org/~obrien/amd64-elf-abi.pdf> + + if mem::size_of::<R>() <= 16 { + objc_msgSend + } else { + objc_msgSend_stret + } +} + +pub fn msg_send_super_fn<R>() -> Imp { + if mem::size_of::<R>() <= 16 { + objc_msgSendSuper + } else { + objc_msgSendSuper_stret + } +} diff --git a/third_party/rust/objc/src/message/gnustep.rs b/third_party/rust/objc/src/message/gnustep.rs new file mode 100644 index 0000000000..2e28689cef --- /dev/null +++ b/third_party/rust/objc/src/message/gnustep.rs @@ -0,0 +1,35 @@ +use std::any::Any; +use std::mem; + +use runtime::{Class, Object, Imp, Sel}; +use super::{Message, MessageArguments, MessageError, Super}; + +extern { + fn objc_msg_lookup(receiver: *mut Object, op: Sel) -> Imp; + fn objc_msg_lookup_super(sup: *const Super, sel: Sel) -> Imp; +} + +pub unsafe fn send_unverified<T, A, R>(obj: *const T, sel: Sel, args: A) + -> Result<R, MessageError> + where T: Message, A: MessageArguments, R: Any { + if obj.is_null() { + return mem::zeroed(); + } + + let receiver = obj as *mut T as *mut Object; + let msg_send_fn = objc_msg_lookup(receiver, sel); + objc_try!({ + A::invoke(msg_send_fn, receiver, sel, args) + }) +} + +pub unsafe fn send_super_unverified<T, A, R>(obj: *const T, superclass: &Class, + sel: Sel, args: A) -> Result<R, MessageError> + where T: Message, A: MessageArguments, R: Any { + let receiver = obj as *mut T as *mut Object; + let sup = Super { receiver: receiver, superclass: superclass }; + let msg_send_fn = objc_msg_lookup_super(&sup, sel); + objc_try!({ + A::invoke(msg_send_fn, receiver, sel, args) + }) +} diff --git a/third_party/rust/objc/src/message/mod.rs b/third_party/rust/objc/src/message/mod.rs new file mode 100644 index 0000000000..cb11e993a8 --- /dev/null +++ b/third_party/rust/objc/src/message/mod.rs @@ -0,0 +1,296 @@ +use std::any::Any; +use std::error::Error; +use std::fmt; +use std::mem; + +use runtime::{Class, Imp, Object, Sel}; +use {Encode, EncodeArguments}; + +#[cfg(feature = "exception")] +macro_rules! objc_try { + ($b:block) => ( + $crate::exception::try(|| $b).map_err(|exception| + if exception.is_null() { + MessageError("Uncaught exception nil".to_owned()) + } else { + MessageError(format!("Uncaught exception {:?}", &**exception)) + } + ) + ) +} + +#[cfg(not(feature = "exception"))] +macro_rules! objc_try { + ($b:block) => (Ok($b)) +} + +mod verify; + +#[cfg(any(target_os = "macos", target_os = "ios"))] +#[path = "apple/mod.rs"] +mod platform; +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[path = "gnustep.rs"] +mod platform; + +use self::platform::{send_unverified, send_super_unverified}; +use self::verify::verify_message_signature; + +/// Specifies the superclass of an instance. +#[repr(C)] +pub struct Super { + /// Specifies an instance of a class. + pub receiver: *mut Object, + /// Specifies the particular superclass of the instance to message. + pub superclass: *const Class, +} + +/// Types that may be sent Objective-C messages. +/// For example: objects, classes, and blocks. +pub unsafe trait Message { + /** + Sends a message to self with the given selector and arguments. + + The correct version of `objc_msgSend` will be chosen based on the + return type. For more information, see Apple's documentation: + <https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html#//apple_ref/doc/uid/TP40001418-CH1g-88778> + + If the selector is known at compile-time, it is recommended to use the + `msg_send!` macro rather than this method. + */ + #[cfg(not(feature = "verify_message"))] + unsafe fn send_message<A, R>(&self, sel: Sel, args: A) + -> Result<R, MessageError> + where Self: Sized, A: MessageArguments, R: Any { + send_message(self, sel, args) + } + + #[cfg(feature = "verify_message")] + unsafe fn send_message<A, R>(&self, sel: Sel, args: A) + -> Result<R, MessageError> + where Self: Sized, A: MessageArguments + EncodeArguments, + R: Any + Encode { + send_message(self, sel, args) + } + + /** + Verifies that the argument and return types match the encoding of the + method for the given selector. + + This will look up the encoding of the method for the given selector, `sel`, + and return a `MessageError` if any encodings differ for the arguments `A` + and return type `R`. + + # Example + ``` no_run + # #[macro_use] extern crate objc; + # use objc::runtime::{BOOL, Class, Object}; + # use objc::Message; + # fn main() { + let obj: &Object; + # obj = unsafe { msg_send![class!(NSObject), new] }; + let sel = sel!(isKindOfClass:); + // Verify isKindOfClass: takes one Class and returns a BOOL + let result = obj.verify_message::<(&Class,), BOOL>(sel); + assert!(result.is_ok()); + # } + ``` + */ + fn verify_message<A, R>(&self, sel: Sel) -> Result<(), MessageError> + where Self: Sized, A: EncodeArguments, R: Encode { + let obj = unsafe { &*(self as *const _ as *const Object) }; + verify_message_signature::<A, R>(obj.class(), sel) + } +} + +unsafe impl Message for Object { } + +unsafe impl Message for Class { } + +/// Types that may be used as the arguments of an Objective-C message. +pub trait MessageArguments: Sized { + /// Invoke an `Imp` with the given object, selector, and arguments. + /// + /// This method is the primitive used when sending messages and should not + /// be called directly; instead, use the `msg_send!` macro or, in cases + /// with a dynamic selector, the `Message::send_message` method. + unsafe fn invoke<R>(imp: Imp, obj: *mut Object, sel: Sel, args: Self) -> R + where R: Any; +} + +macro_rules! message_args_impl { + ($($a:ident : $t:ident),*) => ( + impl<$($t),*> MessageArguments for ($($t,)*) { + unsafe fn invoke<R>(imp: Imp, obj: *mut Object, sel: Sel, ($($a,)*): Self) -> R + where R: Any { + let imp: unsafe extern fn(*mut Object, Sel $(, $t)*) -> R = + mem::transmute(imp); + imp(obj, sel $(, $a)*) + } + } + ); +} + +message_args_impl!(); +message_args_impl!(a: A); +message_args_impl!(a: A, b: B); +message_args_impl!(a: A, b: B, c: C); +message_args_impl!(a: A, b: B, c: C, d: D); +message_args_impl!(a: A, b: B, c: C, d: D, e: E); +message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F); +message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G); +message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H); +message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I); +message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J); +message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K); +message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L); + +/** +An error encountered while attempting to send a message. + +Currently, an error may be returned in two cases: + +* an Objective-C exception is thrown and the `exception` feature is enabled +* the encodings of the arguments do not match the encoding of the method + and the `verify_message` feature is enabled +*/ +#[derive(Debug)] +pub struct MessageError(String); + +impl fmt::Display for MessageError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl Error for MessageError { + fn description(&self) -> &str { + &self.0 + } +} + +#[doc(hidden)] +#[inline(always)] +#[cfg(not(feature = "verify_message"))] +pub unsafe fn send_message<T, A, R>(obj: *const T, sel: Sel, args: A) + -> Result<R, MessageError> + where T: Message, A: MessageArguments, R: Any { + send_unverified(obj, sel, args) +} + +#[doc(hidden)] +#[inline(always)] +#[cfg(feature = "verify_message")] +pub unsafe fn send_message<T, A, R>(obj: *const T, sel: Sel, args: A) + -> Result<R, MessageError> + where T: Message, A: MessageArguments + EncodeArguments, + R: Any + Encode { + let cls = if obj.is_null() { + return Err(MessageError(format!("Messaging {:?} to nil", sel))); + } else { + (*(obj as *const Object)).class() + }; + + verify_message_signature::<A, R>(cls, sel).and_then(|_| { + send_unverified(obj, sel, args) + }) +} + +#[doc(hidden)] +#[inline(always)] +#[cfg(not(feature = "verify_message"))] +pub unsafe fn send_super_message<T, A, R>(obj: *const T, superclass: &Class, + sel: Sel, args: A) -> Result<R, MessageError> + where T: Message, A: MessageArguments, R: Any { + send_super_unverified(obj, superclass, sel, args) +} + +#[doc(hidden)] +#[inline(always)] +#[cfg(feature = "verify_message")] +pub unsafe fn send_super_message<T, A, R>(obj: *const T, superclass: &Class, + sel: Sel, args: A) -> Result<R, MessageError> + where T: Message, A: MessageArguments + EncodeArguments, + R: Any + Encode { + if obj.is_null() { + return Err(MessageError(format!("Messaging {:?} to nil", sel))); + } + + verify_message_signature::<A, R>(superclass, sel).and_then(|_| { + send_super_unverified(obj, superclass, sel, args) + }) +} + +#[cfg(test)] +mod tests { + use test_utils; + use runtime::Object; + use super::Message; + + #[test] + fn test_send_message() { + let obj = test_utils::custom_object(); + let result: u32 = unsafe { + let _: () = msg_send![obj, setFoo:4u32]; + msg_send![obj, foo] + }; + assert!(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 expected = test_utils::CustomStruct { a: 1, b:2, c: 3, d: 4 }; + assert!(result == expected); + } + + #[cfg(not(feature = "verify_message"))] + #[test] + fn test_send_message_nil() { + let nil: *mut Object = ::std::ptr::null_mut(); + let result: usize = unsafe { + msg_send![nil, hash] + }; + assert!(result == 0); + + let result: *mut Object = unsafe { + msg_send![nil, description] + }; + assert!(result.is_null()); + + let result: f64 = unsafe { + msg_send![nil, doubleValue] + }; + assert!(result == 0.0); + } + + #[test] + fn test_send_message_super() { + let 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]; + assert!(foo == 4); + + // The subclass is overriden to return foo + 2 + let foo: u32 = msg_send![obj, foo]; + assert!(foo == 6); + } + } + + #[test] + fn test_verify_message() { + let obj = test_utils::custom_object(); + assert!(obj.verify_message::<(), u32>(sel!(foo)).is_ok()); + assert!(obj.verify_message::<(u32,), ()>(sel!(setFoo:)).is_ok()); + + // Incorrect types + assert!(obj.verify_message::<(), u64>(sel!(setFoo:)).is_err()); + // Unimplemented selector + assert!(obj.verify_message::<(u32,), ()>(sel!(setFoo)).is_err()); + } +} diff --git a/third_party/rust/objc/src/message/verify.rs b/third_party/rust/objc/src/message/verify.rs new file mode 100644 index 0000000000..61bd4ebb39 --- /dev/null +++ b/third_party/rust/objc/src/message/verify.rs @@ -0,0 +1,49 @@ +use runtime::{Class, Object, Sel}; +use {Encode, EncodeArguments}; +use super::MessageError; + +pub fn verify_message_signature<A, R>(cls: &Class, sel: Sel) + -> Result<(), MessageError> + where A: EncodeArguments, R: Encode { + let method = match cls.instance_method(sel) { + Some(method) => method, + None => return Err(MessageError( + format!("Method {:?} not found on class {:?}", + sel, cls) + )), + }; + + let ret = R::encode(); + let expected_ret = method.return_type(); + if ret != expected_ret { + return Err(MessageError( + format!("Return type code {:?} does not match expected {:?} for method {:?}", + ret, expected_ret, method.name()) + )); + } + + let self_and_cmd = [<*mut Object>::encode(), Sel::encode()]; + let args = A::encodings(); + let args = args.as_ref(); + + let count = self_and_cmd.len() + args.len(); + let expected_count = method.arguments_count(); + if count != expected_count { + return Err(MessageError( + format!("Method {:?} accepts {} arguments, but {} were given", + method.name(), expected_count, count) + )); + } + + for (i, arg) in self_and_cmd.iter().chain(args).enumerate() { + let expected = method.argument_type(i).unwrap(); + if *arg != expected { + return Err(MessageError( + format!("Method {:?} expected argument at index {} with type code {:?} but was given {:?}", + method.name(), i, expected, arg) + )); + } + } + + Ok(()) +} |