1#![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#[cfg(not(target_arch = "aarch64"))]
22pub type BOOL = ::std::os::raw::c_schar;
23#[cfg(not(target_arch = "aarch64"))]
25pub const YES: BOOL = 1;
26#[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#[repr(C)]
39pub struct Sel {
40 ptr: *const c_void,
41}
42
43type PrivateMarker = [u8; 0];
46
47#[repr(C)]
49pub struct Ivar {
50 _priv: PrivateMarker,
51}
52
53#[repr(C)]
55pub struct Method {
56 _priv: PrivateMarker,
57}
58
59#[repr(C)]
61pub struct Class {
62 _priv: PrivateMarker,
63}
64
65#[repr(C)]
67pub struct Protocol {
68 _priv: PrivateMarker
69}
70
71#[repr(C)]
73pub struct Object {
74 _priv: PrivateMarker,
75}
76
77pub 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 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 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 #[inline]
169 pub unsafe fn from_ptr(ptr: *const c_void) -> Sel {
170 Sel {
171 ptr: ptr,
172 }
173 }
174
175 #[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
190unsafe 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 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 pub fn offset(&self) -> isize {
217 let offset = unsafe {
218 ivar_getOffset(self)
219 };
220 offset as isize
221 }
222
223 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 pub fn name(&self) -> Sel {
236 unsafe {
237 method_getName(self)
238 }
239 }
240
241 pub fn return_type(&self) -> Encoding {
243 unsafe {
244 let encoding = method_copyReturnType(self);
245 encode::from_malloc_str(encoding)
246 }
247 }
248
249 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 pub fn arguments_count(&self) -> usize {
264 unsafe {
265 method_getNumberOfArguments(self) as usize
266 }
267 }
268
269 pub fn implementation(&self) -> Imp {
271 unsafe {
272 method_getImplementation(self)
273 }
274 }
275}
276
277impl Class {
278 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 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 pub fn classes_count() -> usize {
299 unsafe {
300 objc_getClassList(ptr::null_mut(), 0) as usize
301 }
302 }
303
304 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 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 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 pub fn instance_size(&self) -> usize {
330 unsafe {
331 class_getInstanceSize(self) as usize
332 }
333 }
334
335 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 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 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 pub fn conforms_to(&self, proto: &Protocol) -> bool {
367 unsafe { class_conformsToProtocol(self, proto) == YES }
368 }
369
370 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 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 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 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 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 pub fn conforms_to(&self, proto: &Protocol) -> bool {
436 unsafe { protocol_conformsToProtocol(self, proto) == YES }
437 }
438
439 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 pub fn class(&self) -> &Class {
465 unsafe {
466 &*object_getClass(self)
467 }
468 }
469
470 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 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 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 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 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}