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