1#![allow(missing_docs)]
7
8use malloc_buf::Malloc;
9use std::ffi::{CStr, CString};
10use std::fmt;
11use std::os::raw::{c_char, c_int, c_uint, c_void};
12use std::ptr;
13use std::str;
14
15use crate::encode;
16use crate::{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 "C" fn();
79
80#[link(name = "objc", kind = "dylib")]
81unsafe extern "C" {
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(
94 cls: *mut Class,
95 name: *const c_char,
96 size: usize,
97 alignment: u8,
98 types: *const c_char,
99 ) -> BOOL;
100 pub fn class_addProtocol(cls: *mut Class, proto: *const Protocol) -> BOOL;
101 pub fn class_conformsToProtocol(cls: *const Class, proto: *const Protocol) -> BOOL;
102 pub fn class_copyProtocolList(cls: *const Class, outCount: *mut c_uint)
103 -> *mut *const Protocol;
104
105 pub fn objc_allocateClassPair(
106 superclass: *const Class,
107 name: *const c_char,
108 extraBytes: usize,
109 ) -> *mut Class;
110 pub fn objc_disposeClassPair(cls: *mut Class);
111 pub fn objc_registerClassPair(cls: *mut Class);
112
113 pub fn class_createInstance(cls: *const Class, extraBytes: usize) -> *mut Object;
114 pub fn object_dispose(obj: *mut Object) -> *mut Object;
115 pub fn object_getClass(obj: *const Object) -> *const Class;
116
117 pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
118 pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
119 pub fn objc_getClass(name: *const c_char) -> *const Class;
120 pub fn objc_getProtocol(name: *const c_char) -> *const Protocol;
121 pub fn objc_copyProtocolList(outCount: *mut c_uint) -> *mut *const Protocol;
122 pub fn objc_allocateProtocol(name: *const c_char) -> *mut Protocol;
123 pub fn objc_registerProtocol(proto: *mut Protocol);
124
125 pub fn objc_autoreleasePoolPush() -> *mut c_void;
126 pub fn objc_autoreleasePoolPop(context: *mut c_void);
127
128 pub fn protocol_addMethodDescription(
129 proto: *mut Protocol,
130 name: Sel,
131 types: *const c_char,
132 isRequiredMethod: BOOL,
133 isInstanceMethod: BOOL,
134 );
135 pub fn protocol_addProtocol(proto: *mut Protocol, addition: *const Protocol);
136 pub fn protocol_getName(proto: *const Protocol) -> *const c_char;
137 pub fn protocol_isEqual(proto: *const Protocol, other: *const Protocol) -> BOOL;
138 pub fn protocol_copyProtocolList(
139 proto: *const Protocol,
140 outCount: *mut c_uint,
141 ) -> *mut *const Protocol;
142 pub fn protocol_conformsToProtocol(proto: *const Protocol, other: *const Protocol) -> BOOL;
143
144 pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
145 pub fn ivar_getOffset(ivar: *const Ivar) -> isize;
146 pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;
147
148 pub fn method_getName(method: *const Method) -> Sel;
149 pub fn method_getImplementation(method: *const Method) -> Imp;
150 pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
151 pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
152 pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
153 pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
154 pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
155
156 pub fn objc_retain(obj: *mut Object) -> *mut Object;
157 pub fn objc_release(obj: *mut Object);
158 pub fn objc_autorelease(obj: *mut Object);
159
160 pub fn objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object;
161 pub fn objc_initWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object;
162 pub fn objc_destroyWeak(location: *mut *mut Object);
163 pub fn objc_copyWeak(to: *mut *mut Object, from: *mut *mut Object);
164}
165
166impl Sel {
167 pub fn register(name: &str) -> Sel {
170 let name = CString::new(name).unwrap();
171 unsafe { sel_registerName(name.as_ptr()) }
172 }
173
174 pub fn name(&self) -> &str {
176 let name = unsafe { CStr::from_ptr(sel_getName(*self)) };
177 str::from_utf8(name.to_bytes()).unwrap()
178 }
179
180 #[inline]
184 pub unsafe fn from_ptr(ptr: *const c_void) -> Sel {
185 Sel { ptr: ptr }
186 }
187
188 #[inline]
190 pub fn as_ptr(&self) -> *const c_void {
191 self.ptr
192 }
193}
194
195impl PartialEq for Sel {
196 fn eq(&self, other: &Sel) -> bool {
197 self.ptr == other.ptr
198 }
199}
200
201impl Eq for Sel {}
202
203unsafe impl Sync for Sel {}
205unsafe impl Send for Sel {}
206
207impl Copy for Sel {}
208
209impl Clone for Sel {
210 fn clone(&self) -> Sel {
211 *self
212 }
213}
214
215impl fmt::Debug for Sel {
216 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
217 write!(f, "{}", self.name())
218 }
219}
220
221impl Ivar {
222 pub fn name(&self) -> &str {
224 let name = unsafe { CStr::from_ptr(ivar_getName(self)) };
225 str::from_utf8(name.to_bytes()).unwrap()
226 }
227
228 pub fn offset(&self) -> isize {
230 let offset = unsafe { ivar_getOffset(self) };
231 offset as isize
232 }
233
234 pub fn type_encoding(&self) -> Encoding {
236 let encoding = unsafe { CStr::from_ptr(ivar_getTypeEncoding(self)) };
237 let s = str::from_utf8(encoding.to_bytes()).unwrap();
238 encode::from_str(s)
239 }
240}
241
242impl Method {
243 pub fn name(&self) -> Sel {
245 unsafe { method_getName(self) }
246 }
247
248 pub fn return_type(&self) -> Encoding {
250 unsafe {
251 let encoding = method_copyReturnType(self);
252 encode::from_malloc_str(encoding)
253 }
254 }
255
256 pub fn argument_type(&self, index: usize) -> Option<Encoding> {
259 unsafe {
260 let encoding = method_copyArgumentType(self, index as c_uint);
261 if encoding.is_null() {
262 None
263 } else {
264 Some(encode::from_malloc_str(encoding))
265 }
266 }
267 }
268
269 pub fn arguments_count(&self) -> usize {
271 unsafe { method_getNumberOfArguments(self) as usize }
272 }
273
274 pub fn implementation(&self) -> Imp {
276 unsafe { method_getImplementation(self) }
277 }
278}
279
280impl Class {
281 pub fn get(name: &str) -> Option<&'static Class> {
284 let name = CString::new(name).unwrap();
285 unsafe {
286 let cls = objc_getClass(name.as_ptr());
287 if cls.is_null() { None } else { Some(&*cls) }
288 }
289 }
290
291 pub fn classes() -> Malloc<[&'static Class]> {
293 unsafe {
294 let mut count: c_uint = 0;
295 let classes = objc_copyClassList(&mut count);
296 Malloc::from_array(classes as *mut _, count as usize)
297 }
298 }
299
300 pub fn classes_count() -> usize {
302 unsafe { objc_getClassList(ptr::null_mut(), 0) as usize }
303 }
304
305 pub fn name(&self) -> &str {
307 let name = unsafe { CStr::from_ptr(class_getName(self)) };
308 str::from_utf8(name.to_bytes()).unwrap()
309 }
310
311 pub fn superclass(&self) -> Option<&Class> {
313 unsafe {
314 let superclass = class_getSuperclass(self);
315 if superclass.is_null() {
316 None
317 } else {
318 Some(&*superclass)
319 }
320 }
321 }
322
323 pub fn metaclass(&self) -> &Class {
325 unsafe {
326 let self_ptr: *const Class = self;
327 &*object_getClass(self_ptr as *const Object)
328 }
329 }
330
331 pub fn instance_size(&self) -> usize {
333 unsafe { class_getInstanceSize(self) as usize }
334 }
335
336 pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
340 unsafe {
341 let method = class_getInstanceMethod(self, sel);
342 if method.is_null() {
343 None
344 } else {
345 Some(&*method)
346 }
347 }
348 }
349
350 pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
353 let name = CString::new(name).unwrap();
354 unsafe {
355 let ivar = class_getInstanceVariable(self, name.as_ptr());
356 if ivar.is_null() { None } else { Some(&*ivar) }
357 }
358 }
359
360 pub fn instance_methods(&self) -> Malloc<[&Method]> {
362 unsafe {
363 let mut count: c_uint = 0;
364 let methods = class_copyMethodList(self, &mut count);
365 Malloc::from_array(methods as *mut _, count as usize)
366 }
367 }
368
369 pub fn conforms_to(&self, proto: &Protocol) -> bool {
371 unsafe { class_conformsToProtocol(self, proto) == YES }
372 }
373
374 pub fn adopted_protocols(&self) -> Malloc<[&Protocol]> {
376 unsafe {
377 let mut count: c_uint = 0;
378 let protos = class_copyProtocolList(self, &mut count);
379 Malloc::from_array(protos as *mut _, count as usize)
380 }
381 }
382
383 pub fn instance_variables(&self) -> Malloc<[&Ivar]> {
385 unsafe {
386 let mut count: c_uint = 0;
387 let ivars = class_copyIvarList(self, &mut count);
388 Malloc::from_array(ivars as *mut _, count as usize)
389 }
390 }
391}
392
393impl PartialEq for Class {
394 fn eq(&self, other: &Class) -> bool {
395 let self_ptr: *const Class = self;
396 let other_ptr: *const Class = other;
397 self_ptr == other_ptr
398 }
399}
400
401impl Eq for Class {}
402
403impl fmt::Debug for Class {
404 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
405 write!(f, "{}", self.name())
406 }
407}
408
409impl Protocol {
410 pub fn get(name: &str) -> Option<&'static Protocol> {
413 let name = CString::new(name).unwrap();
414 unsafe {
415 let proto = objc_getProtocol(name.as_ptr());
416 if proto.is_null() { None } else { Some(&*proto) }
417 }
418 }
419
420 pub fn protocols() -> Malloc<[&'static Protocol]> {
422 unsafe {
423 let mut count: c_uint = 0;
424 let protocols = objc_copyProtocolList(&mut count);
425 Malloc::from_array(protocols as *mut _, count as usize)
426 }
427 }
428
429 pub fn adopted_protocols(&self) -> Malloc<[&Protocol]> {
431 unsafe {
432 let mut count: c_uint = 0;
433 let protocols = protocol_copyProtocolList(self, &mut count);
434 Malloc::from_array(protocols as *mut _, count as usize)
435 }
436 }
437
438 pub fn conforms_to(&self, proto: &Protocol) -> bool {
440 unsafe { protocol_conformsToProtocol(self, proto) == YES }
441 }
442
443 pub fn name(&self) -> &str {
445 let name = unsafe { CStr::from_ptr(protocol_getName(self)) };
446 str::from_utf8(name.to_bytes()).unwrap()
447 }
448}
449
450impl PartialEq for Protocol {
451 fn eq(&self, other: &Protocol) -> bool {
452 unsafe { protocol_isEqual(self, other) == YES }
453 }
454}
455
456impl Eq for Protocol {}
457
458impl fmt::Debug for Protocol {
459 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
460 write!(f, "{}", self.name())
461 }
462}
463
464impl Object {
465 pub fn class(&self) -> &Class {
467 unsafe { &*object_getClass(self) }
468 }
469
470 pub unsafe fn get_ivar<T>(&self, name: &str) -> &T
475 where
476 T: Encode,
477 {
478 let offset = {
479 let cls = self.class();
480 match cls.instance_variable(name) {
481 Some(ivar) => {
482 assert!(ivar.type_encoding() == T::encode());
483 ivar.offset()
484 }
485 None => panic!("Ivar {} not found on class {:?}", name, cls),
486 }
487 };
488 let ptr = unsafe {
489 let self_ptr: *const Object = self;
490 (self_ptr as *const u8).offset(offset) as *const T
491 };
492 unsafe { &*ptr }
493 }
494
495 pub unsafe fn get_mut_ivar<T>(&mut self, name: &str) -> &mut T
500 where
501 T: Encode,
502 {
503 let offset = {
504 let cls = self.class();
505 match cls.instance_variable(name) {
506 Some(ivar) => {
507 assert!(ivar.type_encoding() == T::encode());
508 ivar.offset()
509 }
510 None => panic!("Ivar {} not found on class {:?}", name, cls),
511 }
512 };
513 let ptr = unsafe {
514 let self_ptr: *mut Object = self;
515 (self_ptr as *mut u8).offset(offset) as *mut T
516 };
517 unsafe { &mut *ptr }
518 }
519
520 pub unsafe fn set_ivar<T>(&mut self, name: &str, value: T)
525 where
526 T: Encode,
527 {
528 unsafe {
529 *self.get_mut_ivar::<T>(name) = value;
530 }
531 }
532}
533
534impl fmt::Debug for Object {
535 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
536 write!(f, "<{:?}: {:p}>", self.class(), self)
537 }
538}
539
540#[cfg(test)]
541mod tests {
542 use super::{Class, Protocol, Sel};
543 use crate::Encode;
544 use crate::test_utils;
545
546 #[test]
547 fn test_ivar() {
548 let cls = test_utils::custom_class();
549 let ivar = cls.instance_variable("_foo").unwrap();
550 assert!(ivar.name() == "_foo");
551 assert!(ivar.type_encoding() == <u32>::encode());
552 assert!(ivar.offset() > 0);
553
554 let ivars = cls.instance_variables();
555 assert!(ivars.len() > 0);
556 }
557
558 #[test]
559 fn test_method() {
560 let cls = test_utils::custom_class();
561 let sel = Sel::register("foo");
562 let method = cls.instance_method(sel).unwrap();
563 assert!(method.name().name() == "foo");
564 assert!(method.arguments_count() == 2);
565 assert!(method.return_type() == <u32>::encode());
566 assert!(method.argument_type(1).unwrap() == Sel::encode());
567
568 let methods = cls.instance_methods();
569 assert!(methods.len() > 0);
570 }
571
572 #[test]
573 fn test_class() {
574 let cls = test_utils::custom_class();
575 assert!(cls.name() == "CustomObject");
576 assert!(cls.instance_size() > 0);
577 assert!(cls.superclass().is_none());
578
579 assert!(Class::get(cls.name()) == Some(cls));
580
581 let metaclass = cls.metaclass();
582 assert!(metaclass.superclass().unwrap() == cls);
584
585 let subclass = test_utils::custom_subclass();
586 assert!(subclass.superclass().unwrap() == cls);
587 }
588
589 #[test]
590 fn test_classes() {
591 assert!(Class::classes_count() > 0);
592 let classes = Class::classes();
593 assert!(classes.len() > 0);
594 }
595
596 #[test]
597 fn test_protocol() {
598 let proto = test_utils::custom_protocol();
599 assert!(proto.name() == "CustomProtocol");
600 let class = test_utils::custom_class();
601 assert!(class.conforms_to(proto));
602 let class_protocols = class.adopted_protocols();
603 assert!(class_protocols.len() > 0);
604 }
605
606 #[test]
607 fn test_protocol_method() {
608 let class = test_utils::custom_class();
609 let result: i32 = unsafe { msg_send![class, addNumber:1 toNumber:2] };
610 assert_eq!(result, 3);
611 }
612
613 #[test]
614 fn test_subprotocols() {
615 let sub_proto = test_utils::custom_subprotocol();
616 let super_proto = test_utils::custom_protocol();
617 assert!(sub_proto.conforms_to(super_proto));
618 let adopted_protocols = sub_proto.adopted_protocols();
619 assert_eq!(adopted_protocols[0], super_proto);
620 }
621
622 #[test]
623 fn test_protocols() {
624 let _ = test_utils::custom_protocol();
626
627 let protocols = Protocol::protocols();
628 assert!(protocols.len() > 0);
629 }
630
631 #[test]
632 fn test_object() {
633 let mut obj = test_utils::custom_object();
634 assert!(obj.class() == test_utils::custom_class());
635 let result: u32 = unsafe {
636 obj.set_ivar("_foo", 4u32);
637 *obj.get_ivar("_foo")
638 };
639 assert!(result == 4);
640 }
641}