1use std::ffi::{CStr, CString};
2use std::fmt;
3use std::os::raw::{c_char, c_int, c_uint, c_void};
4use std::ptr;
5use std::str;
6use malloc_buf::MallocBuffer;
7
8use encode;
9use {Encode, Encoding};
10
11pub type ObjcId = *mut Object;
12#[allow(non_upper_case_globals)]
13pub const nil: ObjcId = 0 as ObjcId;
14
15#[cfg(not(target_arch = "aarch64"))]
16pub type BOOL = ::std::os::raw::c_schar;
17
18#[cfg(not(target_arch = "aarch64"))]
19pub const YES: BOOL = 1;
20
21#[cfg(not(target_arch = "aarch64"))]
22pub const NO: BOOL = 0;
23
24#[cfg(target_arch = "aarch64")]
25pub type BOOL = bool;
26#[cfg(target_arch = "aarch64")]
27pub const YES: BOOL = true;
28#[cfg(target_arch = "aarch64")]
29pub const NO: BOOL = false;
30
31#[repr(C)]
32pub struct Sel {
33 ptr: *const c_void,
34}
35
36type PrivateMarker = [u8; 0];
37
38#[repr(C)]
39pub struct Ivar {
40 _priv: PrivateMarker,
41}
42
43#[repr(C)]
44pub struct Method {
45 _priv: PrivateMarker,
46}
47
48#[repr(C)]
49pub struct Class {
50 _priv: PrivateMarker,
51}
52
53#[repr(C)]
54pub struct Protocol {
55 _priv: PrivateMarker
56}
57
58#[repr(C)]
59pub struct Object {
60 _priv: PrivateMarker,
61}
62
63pub type Imp = unsafe extern "C" fn();
64
65#[link(name = "objc", kind = "dylib")]
66extern "C"{
67 pub fn sel_registerName(name: *const c_char) -> Sel;
68 pub fn sel_getName(sel: Sel) -> *const c_char;
69
70 pub fn class_getName(cls: *const Class) -> *const c_char;
71 pub fn class_getSuperclass(cls: *const Class) -> *const Class;
72 pub fn class_getInstanceSize(cls: *const Class) -> usize;
73 pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
74 pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
75 pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method;
76 pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
77 pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> BOOL;
78 pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: usize, alignment: u8, types: *const c_char) -> BOOL;
79 pub fn class_addProtocol(cls: *mut Class, proto: *const Protocol) -> BOOL;
80 pub fn class_conformsToProtocol(cls: *const Class, proto: *const Protocol) -> BOOL;
81 pub fn class_copyProtocolList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Protocol;
82
83 pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: usize) -> *mut Class;
84 pub fn objc_disposeClassPair(cls: *mut Class);
85 pub fn objc_registerClassPair(cls: *mut Class);
86
87 pub fn class_createInstance(cls: *const Class, extraBytes: usize) -> *mut Object;
88 pub fn object_dispose(obj: *mut Object) -> *mut Object;
89 pub fn object_getClass(obj: *const Object) -> *const Class;
90
91 pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int;
92 pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class;
93 pub fn objc_getClass(name: *const c_char) -> *const Class;
94 pub fn objc_getProtocol(name: *const c_char) -> *const Protocol;
95 pub fn objc_copyProtocolList(outCount: *mut c_uint) -> *mut *const Protocol;
96 pub fn objc_allocateProtocol(name: *const c_char) -> *mut Protocol;
97 pub fn objc_registerProtocol(proto: *mut Protocol);
98
99 pub fn objc_autoreleasePoolPush() -> *mut c_void;
100 pub fn objc_autoreleasePoolPop(context: *mut c_void);
101
102 pub fn protocol_addMethodDescription(proto: *mut Protocol, name: Sel, types: *const c_char, isRequiredMethod: BOOL,
103 isInstanceMethod: BOOL);
104 pub fn protocol_addProtocol(proto: *mut Protocol, addition: *const Protocol);
105 pub fn protocol_getName(proto: *const Protocol) -> *const c_char;
106 pub fn protocol_isEqual(proto: *const Protocol, other: *const Protocol) -> BOOL;
107 pub fn protocol_copyProtocolList(proto: *const Protocol, outCount: *mut c_uint) -> *mut *const Protocol;
108 pub fn protocol_conformsToProtocol(proto: *const Protocol, other: *const Protocol) -> BOOL;
109
110 pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
111 pub fn ivar_getOffset(ivar: *const Ivar) -> isize;
112 pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;
113
114 pub fn method_getName(method: *const Method) -> Sel;
115 pub fn method_getImplementation(method: *const Method) -> Imp;
116 pub fn method_copyReturnType(method: *const Method) -> *mut c_char;
117 pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char;
118 pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
119 pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
120 pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
121
122 pub fn objc_retain(obj: *mut Object) -> *mut Object;
123 pub fn objc_release(obj: *mut Object);
124 pub fn objc_autorelease(obj: *mut Object);
125
126 pub fn objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object;
127 pub fn objc_initWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object;
128 pub fn objc_destroyWeak(location: *mut *mut Object);
129 pub fn objc_copyWeak(to: *mut *mut Object, from: *mut *mut Object);
130}
131
132impl Sel {
133 pub fn register(name: &str) -> Sel {
136 let name = CString::new(name).unwrap();
137 unsafe {
138 sel_registerName(name.as_ptr())
139 }
140 }
141
142 pub fn name(&self) -> &str {
144 let name = unsafe {
145 CStr::from_ptr(sel_getName(*self))
146 };
147 str::from_utf8(name.to_bytes()).unwrap()
148 }
149
150 #[inline]
154 pub unsafe fn from_ptr(ptr: *const c_void) -> Sel {
155 Sel {
156 ptr,
157 }
158 }
159
160 #[inline]
162 pub fn as_ptr(&self) -> *const c_void {
163 self.ptr
164 }
165}
166
167impl PartialEq for Sel {
168 fn eq(&self, other: &Sel) -> bool {
169 self.ptr == other.ptr
170 }
171}
172
173impl Eq for Sel { }
174
175unsafe impl Sync for Sel { }
177unsafe impl Send for Sel { }
178
179impl Copy for Sel { }
180
181impl Clone for Sel {
182 fn clone(&self) -> Sel { *self }
183}
184
185impl fmt::Debug for Sel {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 write!(f, "{}", self.name())
188 }
189}
190
191impl Ivar {
192 pub fn name(&self) -> &str {
194 let name = unsafe {
195 CStr::from_ptr(ivar_getName(self))
196 };
197 str::from_utf8(name.to_bytes()).unwrap()
198 }
199
200 pub fn offset(&self) -> isize {
202
203 unsafe {
204 ivar_getOffset(self)
205 }
206 }
207
208 pub fn type_encoding(&self) -> Encoding {
210 let encoding = unsafe {
211 CStr::from_ptr(ivar_getTypeEncoding(self))
212 };
213 let s = str::from_utf8(encoding.to_bytes()).unwrap();
214 encode::from_str(s)
215 }
216}
217
218impl Method {
219 pub fn name(&self) -> Sel {
221 unsafe {
222 method_getName(self)
223 }
224 }
225
226 pub fn return_type(&self) -> Encoding {
228 unsafe {
229 let encoding = method_copyReturnType(self);
230 encode::from_malloc_str(encoding)
231 }
232 }
233
234 pub fn argument_type(&self, index: usize) -> Option<Encoding> {
237 unsafe {
238 let encoding = method_copyArgumentType(self, index as c_uint);
239 if encoding.is_null() {
240 None
241 } else {
242 Some(encode::from_malloc_str(encoding))
243 }
244 }
245 }
246
247 pub fn arguments_count(&self) -> usize {
249 unsafe {
250 method_getNumberOfArguments(self) as usize
251 }
252 }
253
254 pub fn implementation(&self) -> Imp {
256 unsafe {
257 method_getImplementation(self)
258 }
259 }
260}
261
262impl Class {
263 pub fn get(name: &str) -> Option<&'static Class> {
266 let name = CString::new(name).unwrap();
267 unsafe {
268 let cls = objc_getClass(name.as_ptr());
269 if cls.is_null() { None } else { Some(&*cls) }
270 }
271 }
272
273 pub fn classes() -> MallocBuffer<&'static Class> {
275 unsafe {
276 let mut count: c_uint = 0;
277 let classes = objc_copyClassList(&mut count);
278 MallocBuffer::new(classes as *mut _, count as usize).unwrap()
279 }
280 }
281
282 pub fn classes_count() -> usize {
284 unsafe {
285 objc_getClassList(ptr::null_mut(), 0) as usize
286 }
287 }
288
289 pub fn name(&self) -> &str {
291 let name = unsafe {
292 CStr::from_ptr(class_getName(self))
293 };
294 str::from_utf8(name.to_bytes()).unwrap()
295 }
296
297 pub fn superclass(&self) -> Option<&Class> {
299 unsafe {
300 let superclass = class_getSuperclass(self);
301 if superclass.is_null() { None } else { Some(&*superclass) }
302 }
303 }
304
305 pub fn metaclass(&self) -> &Class {
307 unsafe {
308 let self_ptr: *const Class = self;
309 &*object_getClass(self_ptr as *const Object)
310 }
311 }
312
313 pub fn instance_size(&self) -> usize {
315 unsafe {
316 class_getInstanceSize(self)
317 }
318 }
319
320 pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
324 unsafe {
325 let method = class_getInstanceMethod(self, sel);
326 if method.is_null() { None } else { Some(&*method) }
327 }
328 }
329
330 pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
333 let name = CString::new(name).unwrap();
334 unsafe {
335 let ivar = class_getInstanceVariable(self, name.as_ptr());
336 if ivar.is_null() { None } else { Some(&*ivar) }
337 }
338 }
339
340 pub fn instance_methods(&self) -> MallocBuffer<&Method> {
342 unsafe {
343 let mut count: c_uint = 0;
344 let methods = class_copyMethodList(self, &mut count);
345 MallocBuffer::new(methods as *mut _, count as usize).unwrap()
346 }
347
348 }
349
350 pub fn conforms_to(&self, proto: &Protocol) -> bool {
352 unsafe { class_conformsToProtocol(self, proto) == YES }
353 }
354
355 pub fn adopted_protocols(&self) -> MallocBuffer<&Protocol> {
357 unsafe {
358 let mut count: c_uint = 0;
359 let protos = class_copyProtocolList(self, &mut count);
360 MallocBuffer::new(protos as *mut _, count as usize).unwrap()
361 }
362 }
363
364 pub fn instance_variables(&self) -> MallocBuffer<&Ivar> {
366 unsafe {
367 let mut count: c_uint = 0;
368 let ivars = class_copyIvarList(self, &mut count);
369 MallocBuffer::new(ivars as *mut _, count as usize).unwrap()
370 }
371 }
372}
373
374impl PartialEq for Class {
375 fn eq(&self, other: &Class) -> bool {
376 let self_ptr: *const Class = self;
377 let other_ptr: *const Class = other;
378 self_ptr == other_ptr
379 }
380}
381
382impl Eq for Class { }
383
384impl fmt::Debug for Class {
385 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
386 write!(f, "{}", self.name())
387 }
388}
389
390impl Protocol {
391 pub fn get(name: &str) -> Option<&'static Protocol> {
394 let name = CString::new(name).unwrap();
395 unsafe {
396 let proto = objc_getProtocol(name.as_ptr());
397 if proto.is_null() { None } else { Some(&*proto) }
398 }
399 }
400
401 pub fn protocols() -> MallocBuffer<&'static Protocol> {
403 unsafe {
404 let mut count: c_uint = 0;
405 let protocols = objc_copyProtocolList(&mut count);
406 MallocBuffer::new(protocols as *mut _, count as usize).unwrap()
407 }
408 }
409
410 pub fn adopted_protocols(&self) -> MallocBuffer<&Protocol> {
412 unsafe {
413 let mut count: c_uint = 0;
414 let protocols = protocol_copyProtocolList(self, &mut count);
415 MallocBuffer::new(protocols as *mut _, count as usize).unwrap()
416 }
417 }
418
419 pub fn conforms_to(&self, proto: &Protocol) -> bool {
421 unsafe { protocol_conformsToProtocol(self, proto) == YES }
422 }
423
424 pub fn name(&self) -> &str {
426 let name = unsafe {
427 CStr::from_ptr(protocol_getName(self))
428 };
429 str::from_utf8(name.to_bytes()).unwrap()
430 }
431}
432
433impl PartialEq for Protocol {
434 fn eq(&self, other: &Protocol) -> bool {
435 unsafe { protocol_isEqual(self, other) == YES }
436 }
437}
438
439impl Eq for Protocol { }
440
441impl fmt::Debug for Protocol {
442 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
443 write!(f, "{}", self.name())
444 }
445}
446
447impl Object {
448 pub fn class(&self) -> &Class {
450 unsafe {
451 &*object_getClass(self)
452 }
453 }
454
455 pub unsafe fn get_ivar<T>(&self, name: &str) -> &T where T: Encode {
460 let offset = {
461 let cls = self.class();
462 match cls.instance_variable(name) {
463 Some(ivar) => {
464 assert!(ivar.type_encoding() == T::encode());
465 ivar.offset()
466 }
467 None => panic!("Ivar {} not found on class {:?}", name, cls),
468 }
469 };
470 let ptr = {
471 let self_ptr: *const Object = self;
472 (self_ptr as *const u8).offset(offset) as *const T
473 };
474 &*ptr
475 }
476
477 pub unsafe fn get_mut_ivar<T>(&mut self, name: &str) -> &mut T
482 where T: Encode {
483 let offset = {
484 let cls = self.class();
485 match cls.instance_variable(name) {
486 Some(ivar) => {
487 assert!(ivar.type_encoding() == T::encode());
488 ivar.offset()
489 }
490 None => panic!("Ivar {} not found on class {:?}", name, cls),
491 }
492 };
493 let ptr = {
494 let self_ptr: *mut Object = self;
495 (self_ptr as *mut u8).offset(offset) as *mut T
496 };
497 &mut *ptr
498 }
499
500 pub unsafe fn set_ivar<T>(&mut self, name: &str, value: T)
505 where T: Encode {
506 *self.get_mut_ivar::<T>(name) = value;
507 }
508}
509
510impl fmt::Debug for Object {
511 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
512 write!(f, "<{:?}: {:p}>", self.class(), self)
513 }
514}
515