summaryrefslogtreecommitdiffstats
path: root/third_party/rust/objc/src/message
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/objc/src/message')
-rw-r--r--third_party/rust/objc/src/message/apple/arm.rs40
-rw-r--r--third_party/rust/objc/src/message/apple/arm64.rs18
-rw-r--r--third_party/rust/objc/src/message/apple/mod.rs40
-rw-r--r--third_party/rust/objc/src/message/apple/x86.rs40
-rw-r--r--third_party/rust/objc/src/message/apple/x86_64.rs32
-rw-r--r--third_party/rust/objc/src/message/gnustep.rs35
-rw-r--r--third_party/rust/objc/src/message/mod.rs296
-rw-r--r--third_party/rust/objc/src/message/verify.rs49
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(())
+}