1use std::ffi::CString;
38use std::mem;
39use std::ptr;
40
41use runtime::{BOOL, Class, Imp, NO, Object, Protocol, Sel, self};
42use {Encode, EncodeArguments, Encoding, Message};
43
44pub trait MethodImplementation {
46 type Callee: Message;
48 type Ret: Encode;
50 type Args: EncodeArguments;
52
53 fn imp(self) -> Imp;
55}
56
57macro_rules! method_decl_impl {
58 (-$s:ident, $r:ident, $f:ty, $($t:ident),*) => (
59 impl<$s, $r $(, $t)*> MethodImplementation for $f
60 where $s: Message, $r: Encode $(, $t: Encode)* {
61 type Callee = $s;
62 type Ret = $r;
63 type Args = ($($t,)*);
64
65 fn imp(self) -> Imp {
66 unsafe { mem::transmute(self) }
67 }
68 }
69 );
70 ($($t:ident),*) => (
71 method_decl_impl!(-T, R, extern fn(&T, Sel $(, $t)*) -> R, $($t),*);
72 method_decl_impl!(-T, R, extern fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
73 );
74}
75
76method_decl_impl!();
77method_decl_impl!(A);
78method_decl_impl!(A, B);
79method_decl_impl!(A, B, C);
80method_decl_impl!(A, B, C, D);
81method_decl_impl!(A, B, C, D, E);
82method_decl_impl!(A, B, C, D, E, F);
83method_decl_impl!(A, B, C, D, E, F, G);
84method_decl_impl!(A, B, C, D, E, F, G, H);
85method_decl_impl!(A, B, C, D, E, F, G, H, I);
86method_decl_impl!(A, B, C, D, E, F, G, H, I, J);
87method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K);
88method_decl_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
89
90fn count_args(sel: Sel) -> usize {
91 sel.name().chars().filter(|&c| c == ':').count()
92}
93
94fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString {
95 let mut types = ret.as_str().to_owned();
96 types.push_str(<*mut Object>::encode().as_str());
98 types.push_str(Sel::encode().as_str());
99 types.extend(args.iter().map(|e| e.as_str()));
100 CString::new(types).unwrap()
101}
102
103fn log2_align_of<T>() -> u8 {
104 let align = mem::align_of::<T>();
105 debug_assert!(align.count_ones() == 1);
107 align.trailing_zeros() as u8
109}
110
111pub struct ClassDecl {
114 cls: *mut Class,
115}
116
117impl ClassDecl {
118 fn with_superclass(name: &str, superclass: Option<&Class>)
119 -> Option<ClassDecl> {
120 let name = CString::new(name).unwrap();
121 let super_ptr = superclass.map_or(ptr::null(), |c| c);
122 let cls = unsafe {
123 runtime::objc_allocateClassPair(super_ptr, name.as_ptr(), 0)
124 };
125 if cls.is_null() {
126 None
127 } else {
128 Some(ClassDecl { cls: cls })
129 }
130 }
131
132 pub fn new(name: &str, superclass: &Class) -> Option<ClassDecl> {
135 ClassDecl::with_superclass(name, Some(superclass))
136 }
137
138 pub fn root(name: &str, intitialize_fn: extern fn(&Class, Sel))
152 -> Option<ClassDecl> {
153 let mut decl = ClassDecl::with_superclass(name, None);
154 if let Some(ref mut decl) = decl {
155 unsafe {
156 decl.add_class_method(sel!(initialize), intitialize_fn);
157 }
158 }
159 decl
160 }
161
162 pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
168 where F: MethodImplementation<Callee=Object> {
169 let encs = F::Args::encodings();
170 let encs = encs.as_ref();
171 let sel_args = count_args(sel);
172 assert!(sel_args == encs.len(),
173 "Selector accepts {} arguments, but function accepts {}",
174 sel_args, encs.len(),
175 );
176
177 let types = method_type_encoding(&F::Ret::encode(), encs);
178 let success = runtime::class_addMethod(self.cls, sel, func.imp(),
179 types.as_ptr());
180 assert!(success != NO, "Failed to add method {:?}", sel);
181 }
182
183 pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
189 where F: MethodImplementation<Callee=Class> {
190 let encs = F::Args::encodings();
191 let encs = encs.as_ref();
192 let sel_args = count_args(sel);
193 assert!(sel_args == encs.len(),
194 "Selector accepts {} arguments, but function accepts {}",
195 sel_args, encs.len(),
196 );
197
198 let types = method_type_encoding(&F::Ret::encode(), encs);
199 let metaclass = (*self.cls).metaclass() as *const _ as *mut _;
200 let success = runtime::class_addMethod(metaclass, sel, func.imp(),
201 types.as_ptr());
202 assert!(success != NO, "Failed to add class method {:?}", sel);
203 }
204
205 pub fn add_ivar<T>(&mut self, name: &str) where T: Encode {
208 let c_name = CString::new(name).unwrap();
209 let encoding = CString::new(T::encode().as_str()).unwrap();
210 let size = mem::size_of::<T>();
211 let align = log2_align_of::<T>();
212 let success = unsafe {
213 runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align,
214 encoding.as_ptr())
215 };
216 assert!(success != NO, "Failed to add ivar {}", name);
217 }
218
219 pub fn add_protocol(&mut self, proto: &Protocol) {
222 let success = unsafe { runtime::class_addProtocol(self.cls, proto) };
223 assert!(success != NO, "Failed to add protocol {:?}", proto);
224 }
225
226 pub fn register(self) -> &'static Class {
229 unsafe {
230 let cls = self.cls;
231 runtime::objc_registerClassPair(cls);
232 mem::forget(self);
234 &*cls
235 }
236 }
237}
238
239impl Drop for ClassDecl {
240 fn drop(&mut self) {
241 unsafe {
242 runtime::objc_disposeClassPair(self.cls);
243 }
244 }
245}
246
247pub struct ProtocolDecl {
250 proto: *mut Protocol
251}
252
253impl ProtocolDecl {
254 pub fn new(name: &str) -> Option<ProtocolDecl> {
257 let c_name = CString::new(name).unwrap();
258 let proto = unsafe {
259 runtime::objc_allocateProtocol(c_name.as_ptr())
260 };
261 if proto.is_null() {
262 None
263 } else {
264 Some(ProtocolDecl { proto: proto })
265 }
266 }
267
268 fn add_method_description_common<Args, Ret>(&mut self, sel: Sel, is_required: bool,
269 is_instance_method: bool)
270 where Args: EncodeArguments,
271 Ret: Encode {
272 let encs = Args::encodings();
273 let encs = encs.as_ref();
274 let sel_args = count_args(sel);
275 assert!(sel_args == encs.len(),
276 "Selector accepts {} arguments, but function accepts {}",
277 sel_args, encs.len(),
278 );
279 let types = method_type_encoding(&Ret::encode(), encs);
280 unsafe {
281 runtime::protocol_addMethodDescription(
282 self.proto, sel, types.as_ptr(), is_required as BOOL, is_instance_method as BOOL);
283 }
284 }
285
286 pub fn add_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
288 where Args: EncodeArguments,
289 Ret: Encode {
290 self.add_method_description_common::<Args, Ret>(sel, is_required, true)
291 }
292
293 pub fn add_class_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
295 where Args: EncodeArguments,
296 Ret: Encode {
297 self.add_method_description_common::<Args, Ret>(sel, is_required, false)
298 }
299
300 pub fn add_protocol(&mut self, proto: &Protocol) {
302 unsafe {
303 runtime::protocol_addProtocol(self.proto, proto);
304 }
305 }
306
307 pub fn register(self) -> &'static Protocol {
310 unsafe {
311 runtime::objc_registerProtocol(self.proto);
312 &*self.proto
313 }
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use test_utils;
320
321 #[test]
322 fn test_custom_class() {
323 let obj = test_utils::custom_object();
325 unsafe {
326 let _: () = msg_send![obj, setFoo:13u32];
327 let result: u32 = msg_send![obj, foo];
328 assert!(result == 13);
329 }
330 }
331
332 #[test]
333 fn test_class_method() {
334 let cls = test_utils::custom_class();
335 unsafe {
336 let result: u32 = msg_send![cls, classFoo];
337 assert!(result == 7);
338 }
339 }
340}