1use std::ffi::CString;
38use std::mem;
39use std::ptr;
40
41use crate::runtime::{self, BOOL, Class, Imp, NO, Object, Protocol, Sel};
42use crate::{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 "C" fn(&T, Sel $(, $t)*) -> R, $($t),*);
72 method_decl_impl!(-T, R, extern "C" 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>) -> Option<ClassDecl> {
119 let name = CString::new(name).unwrap();
120 let super_ptr = superclass.map_or(ptr::null(), |c| c);
121 let cls = unsafe { runtime::objc_allocateClassPair(super_ptr, name.as_ptr(), 0) };
122 if cls.is_null() {
123 None
124 } else {
125 Some(ClassDecl { cls: cls })
126 }
127 }
128
129 pub fn new(name: &str, superclass: &Class) -> Option<ClassDecl> {
132 ClassDecl::with_superclass(name, Some(superclass))
133 }
134
135 pub fn root(name: &str, intitialize_fn: extern "C" fn(&Class, Sel)) -> Option<ClassDecl> {
149 let mut decl = ClassDecl::with_superclass(name, None);
150 if let Some(ref mut decl) = decl {
151 unsafe {
152 decl.add_class_method(sel!(initialize), intitialize_fn);
153 }
154 }
155 decl
156 }
157
158 pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
164 where
165 F: MethodImplementation<Callee = Object>,
166 {
167 let encs = F::Args::encodings();
168 let encs = encs.as_ref();
169 let sel_args = count_args(sel);
170 assert!(
171 sel_args == encs.len(),
172 "Selector accepts {} arguments, but function accepts {}",
173 sel_args,
174 encs.len(),
175 );
176
177 let types = method_type_encoding(&F::Ret::encode(), encs);
178 let success =
179 unsafe { runtime::class_addMethod(self.cls, sel, func.imp(), 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
190 F: MethodImplementation<Callee = Class>,
191 {
192 let encs = F::Args::encodings();
193 let encs = encs.as_ref();
194 let sel_args = count_args(sel);
195 assert!(
196 sel_args == encs.len(),
197 "Selector accepts {} arguments, but function accepts {}",
198 sel_args,
199 encs.len(),
200 );
201
202 let types = method_type_encoding(&F::Ret::encode(), encs);
203 let metaclass = unsafe { (*self.cls).metaclass() as *const _ as *mut _ };
204 let success =
205 unsafe { runtime::class_addMethod(metaclass, sel, func.imp(), types.as_ptr()) };
206 assert!(success != NO, "Failed to add class method {:?}", sel);
207 }
208
209 pub fn add_ivar<T>(&mut self, name: &str)
212 where
213 T: Encode,
214 {
215 let c_name = CString::new(name).unwrap();
216 let encoding = CString::new(T::encode().as_str()).unwrap();
217 let size = mem::size_of::<T>();
218 let align = log2_align_of::<T>();
219 let success = unsafe {
220 runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align, encoding.as_ptr())
221 };
222 assert!(success != NO, "Failed to add ivar {}", name);
223 }
224
225 pub fn add_protocol(&mut self, proto: &Protocol) {
228 let success = unsafe { runtime::class_addProtocol(self.cls, proto) };
229 assert!(success != NO, "Failed to add protocol {:?}", proto);
230 }
231
232 pub fn register(self) -> &'static Class {
235 unsafe {
236 let cls = self.cls;
237 runtime::objc_registerClassPair(cls);
238 mem::forget(self);
240 &*cls
241 }
242 }
243}
244
245impl Drop for ClassDecl {
246 fn drop(&mut self) {
247 unsafe {
248 runtime::objc_disposeClassPair(self.cls);
249 }
250 }
251}
252
253pub struct ProtocolDecl {
256 proto: *mut Protocol,
257}
258
259impl ProtocolDecl {
260 pub fn new(name: &str) -> Option<ProtocolDecl> {
263 let c_name = CString::new(name).unwrap();
264 let proto = unsafe { runtime::objc_allocateProtocol(c_name.as_ptr()) };
265 if proto.is_null() {
266 None
267 } else {
268 Some(ProtocolDecl { proto: proto })
269 }
270 }
271
272 fn add_method_description_common<Args, Ret>(
273 &mut self,
274 sel: Sel,
275 is_required: bool,
276 is_instance_method: bool,
277 ) where
278 Args: EncodeArguments,
279 Ret: Encode,
280 {
281 let encs = Args::encodings();
282 let encs = encs.as_ref();
283 let sel_args = count_args(sel);
284 assert!(
285 sel_args == encs.len(),
286 "Selector accepts {} arguments, but function accepts {}",
287 sel_args,
288 encs.len(),
289 );
290 let types = method_type_encoding(&Ret::encode(), encs);
291 unsafe {
292 runtime::protocol_addMethodDescription(
293 self.proto,
294 sel,
295 types.as_ptr(),
296 is_required as BOOL,
297 is_instance_method as BOOL,
298 );
299 }
300 }
301
302 pub fn add_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
304 where
305 Args: EncodeArguments,
306 Ret: Encode,
307 {
308 self.add_method_description_common::<Args, Ret>(sel, is_required, true)
309 }
310
311 pub fn add_class_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
313 where
314 Args: EncodeArguments,
315 Ret: Encode,
316 {
317 self.add_method_description_common::<Args, Ret>(sel, is_required, false)
318 }
319
320 pub fn add_protocol(&mut self, proto: &Protocol) {
322 unsafe {
323 runtime::protocol_addProtocol(self.proto, proto);
324 }
325 }
326
327 pub fn register(self) -> &'static Protocol {
330 unsafe {
331 runtime::objc_registerProtocol(self.proto);
332 &*self.proto
333 }
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use crate::test_utils;
340
341 #[test]
342 fn test_custom_class() {
343 let obj = test_utils::custom_object();
345 unsafe {
346 let _: () = msg_send![obj, setFoo:13u32];
347 let result: u32 = msg_send![obj, foo];
348 assert!(result == 13);
349 }
350 }
351
352 #[test]
353 fn test_class_method() {
354 let cls = test_utils::custom_class();
355 unsafe {
356 let result: u32 = msg_send![cls, classFoo];
357 assert!(result == 7);
358 }
359 }
360}