zaplib_objc_sys/
declare.rs

1/*!
2Functionality for declaring Objective-C classes.
3
4Classes can be declared using the `ClassDecl` struct. Instance variables and
5methods can then be added before the class is ultimately registered.
6
7# Example
8
9The following example demonstrates declaring a class named `MyNumber` that has
10one ivar, a `u32` named `_number` and a `number` method that returns it:
11
12# #[macro_use] extern crate objc;
13# use objc::declare::ClassDecl;
14# use objc::runtime::{Class, Object, Sel};
15# fn main() {
16let superclass = class!(NSObject);
17let mut decl = ClassDecl::new("MyNumber", superclass).unwrap();
18
19// Add an instance variable
20decl.add_ivar::<u32>("_number");
21
22// Add an ObjC method for getting the number
23extern fn my_number_get(this: &Object, _cmd: Sel) -> u32 {
24    unsafe { *this.get_ivar("_number") }
25}
26unsafe {
27    decl.add_method(sel!(number),
28        my_number_get as extern fn(&Object, Sel) -> u32);
29}
30
31decl.register();
32# }
33*/
34
35use 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
42/// Types that can be used as the implementation of an Objective-C method.
43pub trait MethodImplementation {
44    /// The callee type of the method.
45    type Callee: Message;
46    /// The return type of the method.
47    type Ret: Encode;
48    /// The argument types of the method.
49    type Args: EncodeArguments;
50
51    /// Returns self as an `Imp` of a method.
52    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    // First two arguments are always self and the selector
95    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    // Alignments are required to be powers of 2
104    debug_assert!(align.count_ones() == 1);
105    // log2 of a power of 2 is the number of trailing zeros
106    align.trailing_zeros() as u8
107}
108
109/// A type for declaring a new class and adding new methods and ivars to it
110/// before registering it.
111pub 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    /// Constructs a `ClassDecl` with the given name and superclass.
128    /// Returns `None` if the class couldn't be allocated.
129    pub fn new(name: &str, superclass: &Class) -> Option<ClassDecl> {
130        ClassDecl::with_superclass(name, Some(superclass))
131    }
132
133    /**
134    Constructs a `ClassDecl` declaring a new root class with the given name.
135    Returns `None` if the class couldn't be allocated.
136
137    An implementation for `+initialize` must also be given; the runtime calls
138    this method for all classes, so it must be defined on root classes.
139
140    Note that implementing a root class is not a simple endeavor.
141    For example, your class probably cannot be passed to Cocoa code unless
142    the entire `NSObject` protocol is implemented.
143    Functionality it expects, like implementations of `-retain` and `-release`
144    used by ARC, will not be present otherwise.
145    */
146    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    /// Adds a method with the given name and implementation to self.
157    /// Panics if the method wasn't sucessfully added
158    /// or if the selector and function take different numbers of arguments.
159    /// Unsafe because the caller must ensure that the types match those that
160    /// are expected when the method is invoked from Objective-C.
161    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    /// Adds a class method with the given name and implementation to self.
176    /// Panics if the method wasn't sucessfully added
177    /// or if the selector and function take different numbers of arguments.
178    /// Unsafe because the caller must ensure that the types match those that
179    /// are expected when the method is invoked from Objective-C.
180    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    /// Adds an ivar with type `T` and the provided name to self.
196    /// Panics if the ivar wasn't successfully added.
197    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    /// Adds a protocol to self. Panics if the protocol wasn't successfully
210    /// added
211    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    /// Registers self, consuming it and returning a reference to the
217    /// newly registered `Class`.
218    pub fn register(self) -> &'static Class {
219        unsafe {
220            let cls = self.cls;
221            runtime::objc_registerClassPair(cls);
222            // Forget self otherwise the class will be disposed in drop
223            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
237/// A type for declaring a new protocol and adding new methods to it
238/// before registering it.
239pub struct ProtocolDecl {
240    proto: *mut Protocol,
241}
242
243impl ProtocolDecl {
244    /// Constructs a `ProtocolDecl` with the given name. Returns `None` if the
245    /// protocol couldn't be allocated.
246    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    /// Adds an instance method declaration with a given description to self.
278    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    /// Adds a class method declaration with a given description to self.
287    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    /// Adds a requirement on another protocol.
296    pub fn add_protocol(&mut self, proto: &Protocol) {
297        unsafe {
298            runtime::protocol_addProtocol(self.proto, proto);
299        }
300    }
301
302    /// Registers self, consuming it and returning a reference to the
303    /// newly registered `Protocol`.
304    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        // Registering the custom class is in test_utils
319        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}