use std::marker::PhantomFn;
use std::mem;
use block::Block;
use runtime::{Class, Object, Sel, Super, self};
pub unsafe trait Message : PhantomFn<Self> { }
unsafe impl Message for Object { }
unsafe impl Message for Class { }
unsafe impl<A, R> Message for Block<A, R> { }
#[cfg(target_arch = "x86_64")]
fn msg_send_fn<R>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
if mem::size_of::<R>() <= 16 {
unsafe { mem::transmute(runtime::objc_msgSend) }
} else {
unsafe { mem::transmute(runtime::objc_msgSend_stret) }
}
}
#[cfg(target_arch = "x86_64")]
fn msg_send_super_fn<R>() -> unsafe extern fn(*const Super, Sel, ...) -> R {
if mem::size_of::<R>() <= 16 {
unsafe { mem::transmute(runtime::objc_msgSendSuper) }
} else {
unsafe { mem::transmute(runtime::objc_msgSendSuper_stret) }
}
}
pub trait MessageArguments {
unsafe fn send<T, R>(self, obj: *mut T, sel: Sel) -> R where T: Message;
unsafe fn send_super<T, R>(self, obj: *mut T, superclass: &Class, sel: Sel) -> R
where T: Message;
}
macro_rules! message_args_impl {
($($a:ident : $t:ident),*) => (
impl<$($t),*> MessageArguments for ($($t,)*) {
unsafe fn send<T, R>(self, obj: *mut T, sel: Sel) -> R where T: Message {
let msg_send_fn = msg_send_fn::<R>();
let msg_send_fn: unsafe extern fn(*mut Object, Sel $(, $t)*) -> R =
mem::transmute(msg_send_fn);
let ($($a,)*) = self;
msg_send_fn(obj as *mut Object, sel $(, $a)*)
}
unsafe fn send_super<T, R>(self, obj: *mut T, superclass: &Class, sel: Sel) -> R
where T: Message {
let msg_send_fn = msg_send_super_fn::<R>();
let msg_send_fn: unsafe extern fn(*const Super, Sel $(, $t)*) -> R =
mem::transmute(msg_send_fn);
let sup = Super { receiver: obj as *mut Object, superclass: superclass };
let ($($a,)*) = self;
msg_send_fn(&sup, 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);
#[cfg(test)]
mod tests {
use runtime::Object;
use test_utils;
#[test]
fn test_send_message() {
let obj = test_utils::sample_object();
let result: *const Object = unsafe {
msg_send![obj, self]
};
assert!(&*obj as *const Object == result);
}
#[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);
}
#[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];
assert!(msg_send![super(obj, superclass), foo] == 4u32);
assert!(msg_send![obj, foo] == 6u32);
}
}
}