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