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