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 use std::ffi::{CStr, CString};
7 use std::fmt;
8 use std::os::raw::{c_char, c_int, c_uint, c_void};
9 use std::ptr;
10 use std::str;
11 use malloc_buf::MallocBuffer;
12 
13 use encode;
14 use {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"))]
20 pub type BOOL = ::std::os::raw::c_schar;
21 /// The equivalent of true for Objective-C's `BOOL` type.
22 #[cfg(not(target_arch = "aarch64"))]
23 pub const YES: BOOL = 1;
24 /// The equivalent of false for Objective-C's `BOOL` type.
25 #[cfg(not(target_arch = "aarch64"))]
26 pub const NO: BOOL = 0;
27 
28 #[cfg(target_arch = "aarch64")]
29 pub type BOOL = bool;
30 #[cfg(target_arch = "aarch64")]
31 pub const YES: BOOL = true;
32 #[cfg(target_arch = "aarch64")]
33 pub const NO: BOOL = false;
34 
35 /// A type that represents a method selector.
36 #[repr(C)]
37 pub 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.
43 type PrivateMarker = [u8; 0];
44 
45 /// A type that represents an instance variable.
46 #[repr(C)]
47 pub struct Ivar {
48     _priv: PrivateMarker,
49 }
50 
51 /// A type that represents a method in a class definition.
52 #[repr(C)]
53 pub struct Method {
54     _priv: PrivateMarker,
55 }
56 
57 /// A type that represents an Objective-C class.
58 #[repr(C)]
59 pub struct Class {
60     _priv: PrivateMarker,
61 }
62 
63 /// A type that represents an Objective-C protocol.
64 #[repr(C)]
65 pub struct Protocol {
66     _priv: PrivateMarker
67 }
68 
69 /// A type that represents an instance of a class.
70 #[repr(C)]
71 pub struct Object {
72     _priv: PrivateMarker,
73 }
74 
75 /// A pointer to the start of a method implementation.
76 pub type Imp = unsafe extern fn();
77 
78 #[link(name = "objc", kind = "dylib")]
79 extern {
sel_registerName(name: *const c_char) -> Sel80     pub fn sel_registerName(name: *const c_char) -> Sel;
sel_getName(sel: Sel) -> *const c_char81     pub fn sel_getName(sel: Sel) -> *const c_char;
82 
class_getName(cls: *const Class) -> *const c_char83     pub fn class_getName(cls: *const Class) -> *const c_char;
class_getSuperclass(cls: *const Class) -> *const Class84     pub fn class_getSuperclass(cls: *const Class) -> *const Class;
class_getInstanceSize(cls: *const Class) -> usize85     pub fn class_getInstanceSize(cls: *const Class) -> usize;
class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method86     pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar87     pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method88     pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method;
class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar89     pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> BOOL90     pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> BOOL;
class_addIvar(cls: *mut Class, name: *const c_char, size: usize, alignment: u8, types: *const c_char) -> BOOL91     pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: usize, alignment: u8, types: *const c_char) -> BOOL;
class_addProtocol(cls: *mut Class, proto: *const Protocol) -> BOOL92     pub fn class_addProtocol(cls: *mut Class, proto: *const Protocol) -> BOOL;
class_conformsToProtocol(cls: *const Class, proto: *const Protocol) -> BOOL93     pub fn class_conformsToProtocol(cls: *const Class, proto: *const Protocol) -> BOOL;
class_copyProtocolList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Protocol94     pub fn class_copyProtocolList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Protocol;
95 
objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: usize) -> *mut Class96     pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: usize) -> *mut Class;
objc_disposeClassPair(cls: *mut Class)97     pub fn objc_disposeClassPair(cls: *mut Class);
objc_registerClassPair(cls: *mut Class)98     pub fn objc_registerClassPair(cls: *mut Class);
99 
class_createInstance(cls: *const Class, extraBytes: usize) -> *mut Object100     pub fn class_createInstance(cls: *const Class, extraBytes: usize) -> *mut Object;
object_dispose(obj: *mut Object) -> *mut Object101     pub fn object_dispose(obj: *mut Object) -> *mut Object;
object_getClass(obj: *const Object) -> *const Class102     pub fn object_getClass(obj: *const Object) -> *const Class;
103 
objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int104     pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class105     pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
objc_getClass(name: *const c_char) -> *const Class106     pub fn objc_getClass(name: *const c_char) -> *const Class;
objc_getProtocol(name: *const c_char) -> *const Protocol107     pub fn objc_getProtocol(name: *const c_char) -> *const Protocol;
objc_copyProtocolList(outCount: *mut c_uint) -> *mut *const Protocol108     pub fn objc_copyProtocolList(outCount: *mut c_uint) -> *mut *const Protocol;
objc_allocateProtocol(name: *const c_char) -> *mut Protocol109     pub fn objc_allocateProtocol(name: *const c_char) -> *mut Protocol;
objc_registerProtocol(proto: *mut Protocol)110     pub fn objc_registerProtocol(proto: *mut Protocol);
111 
objc_autoreleasePoolPush() -> *mut c_void112     pub fn objc_autoreleasePoolPush() -> *mut c_void;
objc_autoreleasePoolPop(context: *mut c_void)113     pub fn objc_autoreleasePoolPop(context: *mut c_void);
114 
protocol_addMethodDescription(proto: *mut Protocol, name: Sel, types: *const c_char, isRequiredMethod: BOOL, isInstanceMethod: BOOL)115     pub fn protocol_addMethodDescription(proto: *mut Protocol, name: Sel, types: *const c_char, isRequiredMethod: BOOL,
116                                          isInstanceMethod: BOOL);
protocol_addProtocol(proto: *mut Protocol, addition: *const Protocol)117     pub fn protocol_addProtocol(proto: *mut Protocol, addition: *const Protocol);
protocol_getName(proto: *const Protocol) -> *const c_char118     pub fn protocol_getName(proto: *const Protocol) -> *const c_char;
protocol_isEqual(proto: *const Protocol, other: *const Protocol) -> BOOL119     pub fn protocol_isEqual(proto: *const Protocol, other: *const Protocol) -> BOOL;
protocol_copyProtocolList(proto: *const Protocol, outCount: *mut c_uint) -> *mut *const Protocol120     pub fn protocol_copyProtocolList(proto: *const Protocol, outCount: *mut c_uint) -> *mut *const Protocol;
protocol_conformsToProtocol(proto: *const Protocol, other: *const Protocol) -> BOOL121     pub fn protocol_conformsToProtocol(proto: *const Protocol, other: *const Protocol) -> BOOL;
122 
ivar_getName(ivar: *const Ivar) -> *const c_char123     pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
ivar_getOffset(ivar: *const Ivar) -> isize124     pub fn ivar_getOffset(ivar: *const Ivar) -> isize;
ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char125     pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;
126 
method_getName(method: *const Method) -> Sel127     pub fn method_getName(method: *const Method) -> Sel;
method_getImplementation(method: *const Method) -> Imp128     pub fn method_getImplementation(method: *const Method) -> Imp;
method_copyReturnType(method: *const Method) -> *mut c_char129     pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char130     pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
method_getNumberOfArguments(method: *const Method) -> c_uint131     pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
method_setImplementation(method: *mut Method, imp: Imp) -> Imp132     pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
method_exchangeImplementations(m1: *mut Method, m2: *mut Method)133     pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
134 
objc_retain(obj: *mut Object) -> *mut Object135     pub fn objc_retain(obj: *mut Object) -> *mut Object;
objc_release(obj: *mut Object)136     pub fn objc_release(obj: *mut Object);
objc_autorelease(obj: *mut Object)137     pub fn objc_autorelease(obj: *mut Object);
138 
objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object139     pub fn objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object;
objc_initWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object140     pub fn objc_initWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object;
objc_destroyWeak(location: *mut *mut Object)141     pub fn objc_destroyWeak(location: *mut *mut Object);
objc_copyWeak(to: *mut *mut Object, from: *mut *mut Object)142     pub fn objc_copyWeak(to: *mut *mut Object, from: *mut *mut Object);
143 }
144 
145 impl Sel {
146     /// Registers a method with the Objective-C runtime system,
147     /// maps the method name to a selector, and returns the selector value.
register(name: &str) -> Sel148     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.
name(&self) -> &str156     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]
from_ptr(ptr: *const c_void) -> Sel167     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]
as_ptr(&self) -> *const c_void175     pub fn as_ptr(&self) -> *const c_void {
176         self.ptr
177     }
178 }
179 
180 impl PartialEq for Sel {
eq(&self, other: &Sel) -> bool181     fn eq(&self, other: &Sel) -> bool {
182         self.ptr == other.ptr
183     }
184 }
185 
186 impl Eq for Sel { }
187 
188 // Sel is safe to share across threads because it is immutable
189 unsafe impl Sync for Sel { }
190 unsafe impl Send for Sel { }
191 
192 impl Copy for Sel { }
193 
194 impl Clone for Sel {
clone(&self) -> Sel195     fn clone(&self) -> Sel { *self }
196 }
197 
198 impl fmt::Debug for Sel {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result199     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200         write!(f, "{}", self.name())
201     }
202 }
203 
204 impl Ivar {
205     /// Returns the name of self.
name(&self) -> &str206     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.
offset(&self) -> isize214     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.
type_encoding(&self) -> Encoding222     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 
231 impl Method {
232     /// Returns the name of self.
name(&self) -> Sel233     pub fn name(&self) -> Sel {
234         unsafe {
235             method_getName(self)
236         }
237     }
238 
239     /// Returns the `Encoding` of self's return type.
return_type(&self) -> Encoding240     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.
argument_type(&self, index: usize) -> Option<Encoding>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.
arguments_count(&self) -> usize261     pub fn arguments_count(&self) -> usize {
262         unsafe {
263             method_getNumberOfArguments(self) as usize
264         }
265     }
266 
267     /// Returns the implementation of self.
implementation(&self) -> Imp268     pub fn implementation(&self) -> Imp {
269         unsafe {
270             method_getImplementation(self)
271         }
272     }
273 }
274 
275 impl Class {
276     /// Returns the class definition of a specified class, or `None` if the
277     /// class is not registered with the Objective-C runtime.
get(name: &str) -> Option<&'static Class>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.
classes() -> MallocBuffer<&'static Class>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.
classes_count() -> usize296     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.
name(&self) -> &str303     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.
superclass(&self) -> Option<&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.
metaclass(&self) -> &Class319     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.
instance_size(&self) -> usize327     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.
instance_method(&self, sel: Sel) -> Option<&Method>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.
instance_variable(&self, name: &str) -> Option<&Ivar>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.
instance_methods(&self) -> MallocBuffer<&Method>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.
conforms_to(&self, proto: &Protocol) -> bool364     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.
adopted_protocols(&self) -> MallocBuffer<&Protocol>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.
instance_variables(&self) -> MallocBuffer<&Ivar>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 
387 impl PartialEq for Class {
eq(&self, other: &Class) -> bool388     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 
395 impl Eq for Class { }
396 
397 impl fmt::Debug for Class {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result398     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
399         write!(f, "{}", self.name())
400     }
401 }
402 
403 impl Protocol {
404     /// Returns the protocol definition of a specified protocol, or `None` if the
405     /// protocol is not registered with the Objective-C runtime.
get(name: &str) -> Option<&'static Protocol>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.
protocols() -> MallocBuffer<&'static Protocol>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.
adopted_protocols(&self) -> MallocBuffer<&Protocol>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.
conforms_to(&self, proto: &Protocol) -> bool433     pub fn conforms_to(&self, proto: &Protocol) -> bool {
434         unsafe { protocol_conformsToProtocol(self, proto) == YES }
435     }
436 
437     /// Returns the name of self.
name(&self) -> &str438     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 
446 impl PartialEq for Protocol {
eq(&self, other: &Protocol) -> bool447     fn eq(&self, other: &Protocol) -> bool {
448         unsafe { protocol_isEqual(self, other) == YES }
449     }
450 }
451 
452 impl Eq for Protocol { }
453 
454 impl fmt::Debug for Protocol {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result455     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456         write!(f, "{}", self.name())
457     }
458 }
459 
460 impl Object {
461     /// Returns the class of self.
class(&self) -> &Class462     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`.
get_ivar<T>(&self, name: &str) -> &T where T: Encode472     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`.
get_mut_ivar<T>(&mut self, name: &str) -> &mut T where T: Encode494     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`.
set_ivar<T>(&mut self, name: &str, value: T) where T: Encode517     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 
523 impl fmt::Debug for Object {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result524     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
525         write!(f, "<{:?}: {:p}>", self.class(), self)
526     }
527 }
528 
529 #[cfg(test)]
530 mod tests {
531     use test_utils;
532     use Encode;
533     use super::{Class, Protocol, Sel};
534 
535     #[test]
test_ivar()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]
test_method()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]
test_class()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]
test_classes()579     fn test_classes() {
580         assert!(Class::classes_count() > 0);
581         let classes = Class::classes();
582         assert!(classes.len() > 0);
583     }
584 
585     #[test]
test_protocol()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]
test_protocol_method()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]
test_subprotocols()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]
test_protocols()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]
test_object()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 }
633