-
Notifications
You must be signed in to change notification settings - Fork 57
/
message.rs
234 lines (202 loc) · 8.25 KB
/
message.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
use std::any::Any;
use std::mem;
use runtime::{Class, Object, Sel, Super, self};
/// Types that may be sent Objective-C messages.
/// For example: objects, classes, and blocks.
pub unsafe trait Message { }
unsafe impl Message for Object { }
unsafe impl Message for Class { }
#[cfg(target_arch = "x86")]
fn msg_send_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
// 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
use std::any::TypeId;
let type_id = TypeId::of::<R>();
let size = mem::size_of::<R>();
if type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>() {
unsafe { mem::transmute(runtime::objc_msgSend_fpret) }
} else if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
unsafe { mem::transmute(runtime::objc_msgSend) }
} else {
unsafe { mem::transmute(runtime::objc_msgSend_stret) }
}
}
#[cfg(target_arch = "x86")]
fn msg_send_super_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
let size = mem::size_of::<R>();
if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
unsafe { mem::transmute(runtime::objc_msgSendSuper) }
} else {
unsafe { mem::transmute(runtime::objc_msgSendSuper_stret) }
}
}
#[cfg(target_arch = "x86_64")]
fn msg_send_fn<R>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
// 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 {
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) }
}
}
#[cfg(target_arch = "arm")]
fn msg_send_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
// 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
use std::any::TypeId;
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>() {
unsafe { mem::transmute(runtime::objc_msgSend) }
} else {
unsafe { mem::transmute(runtime::objc_msgSend_stret) }
}
}
#[cfg(target_arch = "arm")]
fn msg_send_super_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
use std::any::TypeId;
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>() {
unsafe { mem::transmute(runtime::objc_msgSendSuper) }
} else {
unsafe { mem::transmute(runtime::objc_msgSendSuper_stret) }
}
}
#[cfg(target_arch = "aarch64")]
fn msg_send_fn<R>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
// stret is not even available in arm64.
// https://twitter.com/gparker/status/378079715824660480
unsafe { mem::transmute(runtime::objc_msgSend) }
}
#[cfg(target_arch = "aarch64")]
fn msg_send_super_fn<R>() -> unsafe extern fn(*const Super, Sel, ...) -> R {
unsafe { mem::transmute(runtime::objc_msgSendSuper) }
}
/// Types that may be used as the arguments of an Objective-C message.
pub trait MessageArguments {
/// Sends a message to the given obj with the given selector and self as
/// the arguments.
///
/// The correct version of `objc_msgSend` will be chosen based on the
/// return type. For more information, see Apple's documenation:
/// https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html#//apple_ref/doc/uid/TP40001418-CH1g-88778
///
/// It is recommended to use the `msg_send!` macro rather than calling this
/// method directly.
unsafe fn send<T, R>(self, obj: *mut T, sel: Sel) -> R
where T: Message, R: Any;
/// Sends a message to the superclass of an instance of a class with self
/// as the arguments.
unsafe fn send_super<T, R>(self, obj: *mut T, superclass: &Class, sel: Sel) -> R
where T: Message, R: Any;
}
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, R: Any {
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;
objc_try!({
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, R: Any {
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;
objc_try!({
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!(result == &*obj);
}
#[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_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);
}
}
}