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;
#[repr(C)]
pub struct Super {
pub receiver: *mut Object,
pub superclass: *const Class,
}
pub unsafe trait Message {
#[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)
}
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 { }
pub trait MessageArguments: Sized {
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);
#[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);
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());
assert!(obj.verify_message::<(), u64>(sel!(setFoo:)).is_err());
assert!(obj.verify_message::<(u32,), ()>(sel!(setFoo)).is_err());
}
}