1use 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#[cfg(not(target_arch = "aarch64"))]
20pub type BOOL = ::std::os::raw::c_schar;
21#[cfg(not(target_arch = "aarch64"))]
23pub const YES: BOOL = 1;
24#[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#[repr(C)]
37pub struct Sel {
38 ptr: *const c_void,
39}
40
41type PrivateMarker = [u8; 0];
44
45#[repr(C)]
47pub struct Ivar {
48 _priv: PrivateMarker,
49}
50
51#[repr(C)]
53pub struct Method {
54 _priv: PrivateMarker,
55}
56
57#[repr(C)]
59pub struct Class {
60 _priv: PrivateMarker,
61}
62
63#[repr(C)]
65pub struct Protocol {
66 _priv: PrivateMarker
67}
68
69#[repr(C)]
71pub struct Object {
72 _priv: PrivateMarker,
73}
74
75pub 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 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 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 #[inline]
167 pub unsafe fn from_ptr(ptr: *const c_void) -> Sel {
168 Sel {
169 ptr: ptr,
170 }
171 }
172
173 #[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
188unsafe 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 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 pub fn offset(&self) -> isize {
215 let offset = unsafe {
216 ivar_getOffset(self)
217 };
218 offset as isize
219 }
220
221 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 pub fn name(&self) -> Sel {
234 unsafe {
235 method_getName(self)
236 }
237 }
238
239 pub fn return_type(&self) -> Encoding {
241 unsafe {
242 let encoding = method_copyReturnType(self);
243 encode::from_malloc_str(encoding)
244 }
245 }
246
247 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 pub fn arguments_count(&self) -> usize {
262 unsafe {
263 method_getNumberOfArguments(self) as usize
264 }
265 }
266
267 pub fn implementation(&self) -> Imp {
269 unsafe {
270 method_getImplementation(self)
271 }
272 }
273}
274
275impl Class {
276 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 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 pub fn classes_count() -> usize {
297 unsafe {
298 objc_getClassList(ptr::null_mut(), 0) as usize
299 }
300 }
301
302 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 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 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 pub fn instance_size(&self) -> usize {
328 unsafe {
329 class_getInstanceSize(self) as usize
330 }
331 }
332
333 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 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 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 pub fn conforms_to(&self, proto: &Protocol) -> bool {
365 unsafe { class_conformsToProtocol(self, proto) == YES }
366 }
367
368 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 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 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 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 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 pub fn conforms_to(&self, proto: &Protocol) -> bool {
434 unsafe { protocol_conformsToProtocol(self, proto) == YES }
435 }
436
437 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 pub fn class(&self) -> &Class {
463 unsafe {
464 &*object_getClass(self)
465 }
466 }
467
468 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 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 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 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 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}