objc/
runtime.rs

1//! A Rust interface for the functionality of the Objective-C runtime.
2//!
3//! For more information on foreign functions, see Apple's documentation:
4//! <https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html>
5
6use std::ffi::{CStr, CString};
7use std::fmt;
8use std::os::raw::{c_char, c_int, c_uint, c_void};
9use std::ptr;
10use std::str;
11use malloc_buf::MallocBuffer;
12
13use encode;
14use {Encode, Encoding};
15
16/// The Objective-C `BOOL` type.
17///
18/// To convert an Objective-C `BOOL` into a Rust `bool`, compare it with `NO`.
19#[cfg(not(target_arch = "aarch64"))]
20pub type BOOL = ::std::os::raw::c_schar;
21/// The equivalent of true for Objective-C's `BOOL` type.
22#[cfg(not(target_arch = "aarch64"))]
23pub const YES: BOOL = 1;
24/// The equivalent of false for Objective-C's `BOOL` type.
25#[cfg(not(target_arch = "aarch64"))]
26pub const NO: BOOL = 0;
27
28#[cfg(target_arch = "aarch64")]
29pub type BOOL = bool;
30#[cfg(target_arch = "aarch64")]
31pub const YES: BOOL = true;
32#[cfg(target_arch = "aarch64")]
33pub const NO: BOOL = false;
34
35/// A type that represents a method selector.
36#[repr(C)]
37pub struct Sel {
38    ptr: *const c_void,
39}
40
41/// A marker type to be embedded into other types just so that they cannot be
42/// constructed externally.
43type PrivateMarker = [u8; 0];
44
45/// A type that represents an instance variable.
46#[repr(C)]
47pub struct Ivar {
48    _priv: PrivateMarker,
49}
50
51/// A type that represents a method in a class definition.
52#[repr(C)]
53pub struct Method {
54    _priv: PrivateMarker,
55}
56
57/// A type that represents an Objective-C class.
58#[repr(C)]
59pub struct Class {
60    _priv: PrivateMarker,
61}
62
63/// A type that represents an Objective-C protocol.
64#[repr(C)]
65pub struct Protocol {
66    _priv: PrivateMarker
67}
68
69/// A type that represents an instance of a class.
70#[repr(C)]
71pub struct Object {
72    _priv: PrivateMarker,
73}
74
75/// A pointer to the start of a method implementation.
76pub type Imp = unsafe extern fn();
77
78#[link(name = "objc", kind = "dylib")]
79extern {
80    pub fn sel_registerName(name: *const c_char) -> Sel;
81    pub fn sel_getName(sel: Sel) -> *const c_char;
82
83    pub fn class_getName(cls: *const Class) -> *const c_char;
84    pub fn class_getSuperclass(cls: *const Class) -> *const Class;
85    pub fn class_getInstanceSize(cls: *const Class) -> usize;
86    pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
87    pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
88    pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method;
89    pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
90    pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> BOOL;
91    pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: usize, alignment: u8, types: *const c_char) -> BOOL;
92    pub fn class_addProtocol(cls: *mut Class, proto: *const Protocol) -> BOOL;
93    pub fn class_conformsToProtocol(cls: *const Class, proto: *const Protocol) -> BOOL;
94    pub fn class_copyProtocolList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Protocol;
95
96    pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: usize) -> *mut Class;
97    pub fn objc_disposeClassPair(cls: *mut Class);
98    pub fn objc_registerClassPair(cls: *mut Class);
99
100    pub fn class_createInstance(cls: *const Class, extraBytes: usize) -> *mut Object;
101    pub fn object_dispose(obj: *mut Object) -> *mut Object;
102    pub fn object_getClass(obj: *const Object) -> *const Class;
103
104    pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
105    pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
106    pub fn objc_getClass(name: *const c_char) -> *const Class;
107    pub fn objc_getProtocol(name: *const c_char) -> *const Protocol;
108    pub fn objc_copyProtocolList(outCount: *mut c_uint) -> *mut *const Protocol;
109    pub fn objc_allocateProtocol(name: *const c_char) -> *mut Protocol;
110    pub fn objc_registerProtocol(proto: *mut Protocol);
111
112    pub fn objc_autoreleasePoolPush() -> *mut c_void;
113    pub fn objc_autoreleasePoolPop(context: *mut c_void);
114
115    pub fn protocol_addMethodDescription(proto: *mut Protocol, name: Sel, types: *const c_char, isRequiredMethod: BOOL,
116                                         isInstanceMethod: BOOL);
117    pub fn protocol_addProtocol(proto: *mut Protocol, addition: *const Protocol);
118    pub fn protocol_getName(proto: *const Protocol) -> *const c_char;
119    pub fn protocol_isEqual(proto: *const Protocol, other: *const Protocol) -> BOOL;
120    pub fn protocol_copyProtocolList(proto: *const Protocol, outCount: *mut c_uint) -> *mut *const Protocol;
121    pub fn protocol_conformsToProtocol(proto: *const Protocol, other: *const Protocol) -> BOOL;
122
123    pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
124    pub fn ivar_getOffset(ivar: *const Ivar) -> isize;
125    pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;
126
127    pub fn method_getName(method: *const Method) -> Sel;
128    pub fn method_getImplementation(method: *const Method) -> Imp;
129    pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
130    pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
131    pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
132    pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
133    pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
134
135    pub fn objc_retain(obj: *mut Object) -> *mut Object;
136    pub fn objc_release(obj: *mut Object);
137    pub fn objc_autorelease(obj: *mut Object);
138
139    pub fn objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object;
140    pub fn objc_initWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object;
141    pub fn objc_destroyWeak(location: *mut *mut Object);
142    pub fn objc_copyWeak(to: *mut *mut Object, from: *mut *mut Object);
143}
144
145impl Sel {
146    /// Registers a method with the Objective-C runtime system,
147    /// maps the method name to a selector, and returns the selector value.
148    pub fn register(name: &str) -> Sel {
149        let name = CString::new(name).unwrap();
150        unsafe {
151            sel_registerName(name.as_ptr())
152        }
153    }
154
155    /// Returns the name of the method specified by self.
156    pub fn name(&self) -> &str {
157        let name = unsafe {
158            CStr::from_ptr(sel_getName(*self))
159        };
160        str::from_utf8(name.to_bytes()).unwrap()
161    }
162
163    /// Wraps a raw pointer to a selector into a `Sel` object.
164    ///
165    /// This is almost never what you want; use `Sel::register()` instead.
166    #[inline]
167    pub unsafe fn from_ptr(ptr: *const c_void) -> Sel {
168        Sel {
169            ptr: ptr,
170        }
171    }
172
173    /// Returns a pointer to the raw selector.
174    #[inline]
175    pub fn as_ptr(&self) -> *const c_void {
176        self.ptr
177    }
178}
179
180impl PartialEq for Sel {
181    fn eq(&self, other: &Sel) -> bool {
182        self.ptr == other.ptr
183    }
184}
185
186impl Eq for Sel { }
187
188// Sel is safe to share across threads because it is immutable
189unsafe impl Sync for Sel { }
190unsafe impl Send for Sel { }
191
192impl Copy for Sel { }
193
194impl Clone for Sel {
195    fn clone(&self) -> Sel { *self }
196}
197
198impl fmt::Debug for Sel {
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        write!(f, "{}", self.name())
201    }
202}
203
204impl Ivar {
205    /// Returns the name of self.
206    pub fn name(&self) -> &str {
207        let name = unsafe {
208            CStr::from_ptr(ivar_getName(self))
209        };
210        str::from_utf8(name.to_bytes()).unwrap()
211    }
212
213    /// Returns the offset of self.
214    pub fn offset(&self) -> isize {
215        let offset = unsafe {
216            ivar_getOffset(self)
217        };
218        offset as isize
219    }
220
221    /// Returns the `Encoding` of self.
222    pub fn type_encoding(&self) -> Encoding {
223        let encoding = unsafe {
224            CStr::from_ptr(ivar_getTypeEncoding(self))
225        };
226        let s = str::from_utf8(encoding.to_bytes()).unwrap();
227        encode::from_str(s)
228    }
229}
230
231impl Method {
232    /// Returns the name of self.
233    pub fn name(&self) -> Sel {
234        unsafe {
235            method_getName(self)
236        }
237    }
238
239    /// Returns the `Encoding` of self's return type.
240    pub fn return_type(&self) -> Encoding {
241        unsafe {
242            let encoding = method_copyReturnType(self);
243            encode::from_malloc_str(encoding)
244        }
245    }
246
247    /// Returns the `Encoding` of a single parameter type of self, or
248    /// `None` if self has no parameter at the given index.
249    pub fn argument_type(&self, index: usize) -> Option<Encoding> {
250        unsafe {
251            let encoding = method_copyArgumentType(self, index as c_uint);
252            if encoding.is_null() {
253                None
254            } else {
255                Some(encode::from_malloc_str(encoding))
256            }
257        }
258    }
259
260    /// Returns the number of arguments accepted by self.
261    pub fn arguments_count(&self) -> usize {
262        unsafe {
263            method_getNumberOfArguments(self) as usize
264        }
265    }
266
267    /// Returns the implementation of self.
268    pub fn implementation(&self) -> Imp {
269        unsafe {
270            method_getImplementation(self)
271        }
272    }
273}
274
275impl Class {
276    /// Returns the class definition of a specified class, or `None` if the
277    /// class is not registered with the Objective-C runtime.
278    pub fn get(name: &str) -> Option<&'static Class> {
279        let name = CString::new(name).unwrap();
280        unsafe {
281            let cls = objc_getClass(name.as_ptr());
282            if cls.is_null() { None } else { Some(&*cls) }
283        }
284    }
285
286    /// Obtains the list of registered class definitions.
287    pub fn classes() -> MallocBuffer<&'static Class> {
288        unsafe {
289            let mut count: c_uint = 0;
290            let classes = objc_copyClassList(&mut count);
291            MallocBuffer::new(classes as *mut _, count as usize).unwrap()
292        }
293    }
294
295    /// Returns the total number of registered classes.
296    pub fn classes_count() -> usize {
297        unsafe {
298            objc_getClassList(ptr::null_mut(), 0) as usize
299        }
300    }
301
302    /// Returns the name of self.
303    pub fn name(&self) -> &str {
304        let name = unsafe {
305            CStr::from_ptr(class_getName(self))
306        };
307        str::from_utf8(name.to_bytes()).unwrap()
308    }
309
310    /// Returns the superclass of self, or `None` if self is a root class.
311    pub fn superclass(&self) -> Option<&Class> {
312        unsafe {
313            let superclass = class_getSuperclass(self);
314            if superclass.is_null() { None } else { Some(&*superclass) }
315        }
316    }
317
318    /// Returns the metaclass of self.
319    pub fn metaclass(&self) -> &Class {
320        unsafe {
321            let self_ptr: *const Class = self;
322            &*object_getClass(self_ptr as *const Object)
323        }
324    }
325
326    /// Returns the size of instances of self.
327    pub fn instance_size(&self) -> usize {
328        unsafe {
329            class_getInstanceSize(self) as usize
330        }
331    }
332
333    /// Returns a specified instance method for self, or `None` if self and
334    /// its superclasses do not contain an instance method with the
335    /// specified selector.
336    pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
337        unsafe {
338            let method = class_getInstanceMethod(self, sel);
339            if method.is_null() { None } else { Some(&*method) }
340        }
341    }
342
343    /// Returns the ivar for a specified instance variable of self, or `None`
344    /// if self has no ivar with the given name.
345    pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
346        let name = CString::new(name).unwrap();
347        unsafe {
348            let ivar = class_getInstanceVariable(self, name.as_ptr());
349            if ivar.is_null() { None } else { Some(&*ivar) }
350        }
351    }
352
353    /// Describes the instance methods implemented by self.
354    pub fn instance_methods(&self) -> MallocBuffer<&Method> {
355        unsafe {
356            let mut count: c_uint = 0;
357            let methods = class_copyMethodList(self, &mut count);
358            MallocBuffer::new(methods as *mut _, count as usize).unwrap()
359        }
360
361    }
362
363    /// Checks whether this class conforms to the specified protocol.
364    pub fn conforms_to(&self, proto: &Protocol) -> bool {
365        unsafe { class_conformsToProtocol(self, proto) == YES }
366    }
367
368    /// Get a list of the protocols to which this class conforms.
369    pub fn adopted_protocols(&self) -> MallocBuffer<&Protocol> {
370        unsafe {
371            let mut count: c_uint = 0;
372            let protos = class_copyProtocolList(self, &mut count);
373            MallocBuffer::new(protos as *mut _, count as usize).unwrap()
374        }
375    }
376
377    /// Describes the instance variables declared by self.
378    pub fn instance_variables(&self) -> MallocBuffer<&Ivar> {
379        unsafe {
380            let mut count: c_uint = 0;
381            let ivars = class_copyIvarList(self, &mut count);
382            MallocBuffer::new(ivars as *mut _, count as usize).unwrap()
383        }
384    }
385}
386
387impl PartialEq for Class {
388    fn eq(&self, other: &Class) -> bool {
389        let self_ptr: *const Class = self;
390        let other_ptr: *const Class = other;
391        self_ptr == other_ptr
392    }
393}
394
395impl Eq for Class { }
396
397impl fmt::Debug for Class {
398    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
399        write!(f, "{}", self.name())
400    }
401}
402
403impl Protocol {
404    /// Returns the protocol definition of a specified protocol, or `None` if the
405    /// protocol is not registered with the Objective-C runtime.
406    pub fn get(name: &str) -> Option<&'static Protocol> {
407        let name = CString::new(name).unwrap();
408        unsafe {
409            let proto = objc_getProtocol(name.as_ptr());
410            if proto.is_null() { None } else { Some(&*proto) }
411        }
412    }
413
414    /// Obtains the list of registered protocol definitions.
415    pub fn protocols() -> MallocBuffer<&'static Protocol> {
416        unsafe {
417            let mut count: c_uint = 0;
418            let protocols = objc_copyProtocolList(&mut count);
419            MallocBuffer::new(protocols as *mut _, count as usize).unwrap()
420        }
421    }
422
423    /// Get a list of the protocols to which this protocol conforms.
424    pub fn adopted_protocols(&self) -> MallocBuffer<&Protocol> {
425        unsafe {
426            let mut count: c_uint = 0;
427            let protocols = protocol_copyProtocolList(self, &mut count);
428            MallocBuffer::new(protocols as *mut _, count as usize).unwrap()
429        }
430    }
431
432    /// Checks whether this protocol conforms to the specified protocol.
433    pub fn conforms_to(&self, proto: &Protocol) -> bool {
434        unsafe { protocol_conformsToProtocol(self, proto) == YES }
435    }
436
437    /// Returns the name of self.
438    pub fn name(&self) -> &str {
439        let name = unsafe {
440            CStr::from_ptr(protocol_getName(self))
441        };
442        str::from_utf8(name.to_bytes()).unwrap()
443    }
444}
445
446impl PartialEq for Protocol {
447    fn eq(&self, other: &Protocol) -> bool {
448        unsafe { protocol_isEqual(self, other) == YES }
449    }
450}
451
452impl Eq for Protocol { }
453
454impl fmt::Debug for Protocol {
455    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456        write!(f, "{}", self.name())
457    }
458}
459
460impl Object {
461    /// Returns the class of self.
462    pub fn class(&self) -> &Class {
463        unsafe {
464            &*object_getClass(self)
465        }
466    }
467
468    /// Returns a reference to the ivar of self with the given name.
469    /// Panics if self has no ivar with the given name.
470    /// Unsafe because the caller must ensure that the ivar is actually
471    /// of type `T`.
472    pub unsafe fn get_ivar<T>(&self, name: &str) -> &T where T: Encode {
473        let offset = {
474            let cls = self.class();
475            match cls.instance_variable(name) {
476                Some(ivar) => {
477                    assert!(ivar.type_encoding() == T::encode());
478                    ivar.offset()
479                }
480                None => panic!("Ivar {} not found on class {:?}", name, cls),
481            }
482        };
483        let ptr = {
484            let self_ptr: *const Object = self;
485            (self_ptr as *const u8).offset(offset) as *const T
486        };
487        &*ptr
488    }
489
490    /// Returns a mutable reference to the ivar of self with the given name.
491    /// Panics if self has no ivar with the given name.
492    /// Unsafe because the caller must ensure that the ivar is actually
493    /// of type `T`.
494    pub unsafe fn get_mut_ivar<T>(&mut self, name: &str) -> &mut T
495            where T: Encode {
496        let offset = {
497            let cls = self.class();
498            match cls.instance_variable(name) {
499                Some(ivar) => {
500                    assert!(ivar.type_encoding() == T::encode());
501                    ivar.offset()
502                }
503                None => panic!("Ivar {} not found on class {:?}", name, cls),
504            }
505        };
506        let ptr = {
507            let self_ptr: *mut Object = self;
508            (self_ptr as *mut u8).offset(offset) as *mut T
509        };
510        &mut *ptr
511    }
512
513    /// Sets the value of the ivar of self with the given name.
514    /// Panics if self has no ivar with the given name.
515    /// Unsafe because the caller must ensure that the ivar is actually
516    /// of type `T`.
517    pub unsafe fn set_ivar<T>(&mut self, name: &str, value: T)
518            where T: Encode {
519        *self.get_mut_ivar::<T>(name) = value;
520    }
521}
522
523impl fmt::Debug for Object {
524    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
525        write!(f, "<{:?}: {:p}>", self.class(), self)
526    }
527}
528
529#[cfg(test)]
530mod tests {
531    use test_utils;
532    use Encode;
533    use super::{Class, Protocol, Sel};
534
535    #[test]
536    fn test_ivar() {
537        let cls = test_utils::custom_class();
538        let ivar = cls.instance_variable("_foo").unwrap();
539        assert!(ivar.name() == "_foo");
540        assert!(ivar.type_encoding() == <u32>::encode());
541        assert!(ivar.offset() > 0);
542
543        let ivars = cls.instance_variables();
544        assert!(ivars.len() > 0);
545    }
546
547    #[test]
548    fn test_method() {
549        let cls = test_utils::custom_class();
550        let sel = Sel::register("foo");
551        let method = cls.instance_method(sel).unwrap();
552        assert!(method.name().name() == "foo");
553        assert!(method.arguments_count() == 2);
554        assert!(method.return_type() == <u32>::encode());
555        assert!(method.argument_type(1).unwrap() == Sel::encode());
556
557        let methods = cls.instance_methods();
558        assert!(methods.len() > 0);
559    }
560
561    #[test]
562    fn test_class() {
563        let cls = test_utils::custom_class();
564        assert!(cls.name() == "CustomObject");
565        assert!(cls.instance_size() > 0);
566        assert!(cls.superclass().is_none());
567
568        assert!(Class::get(cls.name()) == Some(cls));
569
570        let metaclass = cls.metaclass();
571        // The metaclass of a root class is a subclass of the root class
572        assert!(metaclass.superclass().unwrap() == cls);
573
574        let subclass = test_utils::custom_subclass();
575        assert!(subclass.superclass().unwrap() == cls);
576    }
577
578    #[test]
579    fn test_classes() {
580        assert!(Class::classes_count() > 0);
581        let classes = Class::classes();
582        assert!(classes.len() > 0);
583    }
584
585    #[test]
586    fn test_protocol() {
587        let proto = test_utils::custom_protocol();
588        assert!(proto.name() == "CustomProtocol");
589        let class = test_utils::custom_class();
590        assert!(class.conforms_to(proto));
591        let class_protocols = class.adopted_protocols();
592        assert!(class_protocols.len() > 0);
593    }
594
595    #[test]
596    fn test_protocol_method() {
597        let class = test_utils::custom_class();
598        let result: i32 = unsafe {
599            msg_send![class, addNumber:1 toNumber:2]
600        };
601        assert_eq!(result, 3);
602    }
603
604    #[test]
605    fn test_subprotocols() {
606        let sub_proto = test_utils::custom_subprotocol();
607        let super_proto = test_utils::custom_protocol();
608        assert!(sub_proto.conforms_to(super_proto));
609        let adopted_protocols = sub_proto.adopted_protocols();
610        assert_eq!(adopted_protocols[0], super_proto);
611    }
612
613    #[test]
614    fn test_protocols() {
615        // Ensure that a protocol has been registered on linux
616        let _ = test_utils::custom_protocol();
617
618        let protocols = Protocol::protocols();
619        assert!(protocols.len() > 0);
620    }
621
622    #[test]
623    fn test_object() {
624        let mut obj = test_utils::custom_object();
625        assert!(obj.class() == test_utils::custom_class());
626        let result: u32 = unsafe {
627            obj.set_ivar("_foo", 4u32);
628            *obj.get_ivar("_foo")
629        };
630        assert!(result == 4);
631    }
632}