makepad_objc_sys/
runtime.rs

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    /// Registers a method with the Objective-C runtime system,
134    /// maps the method name to a selector, and returns the selector value.
135    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    /// Returns the name of the method specified by self.
143    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    /// Wraps a raw pointer to a selector into a `Sel` object.
151    ///
152    /// This is almost never what you want; use `Sel::register()` instead.
153    #[inline]
154    pub unsafe fn from_ptr(ptr: *const c_void) -> Sel {
155        Sel {
156            ptr,
157        }
158    }
159
160    /// Returns a pointer to the raw selector.
161    #[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
175// Sel is safe to share across threads because it is immutable
176unsafe 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    /// Returns the name of self.
193    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    /// Returns the offset of self.
201    pub fn offset(&self) -> isize {
202        
203        unsafe {
204            ivar_getOffset(self)
205        }
206    }
207
208    /// Returns the `Encoding` of self.
209    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    /// Returns the name of self.
220    pub fn name(&self) -> Sel {
221        unsafe {
222            method_getName(self)
223        }
224    }
225
226    /// Returns the `Encoding` of self's return type.
227    pub fn return_type(&self) -> Encoding {
228        unsafe {
229            let encoding = method_copyReturnType(self);
230            encode::from_malloc_str(encoding)
231        }
232    }
233
234    /// Returns the `Encoding` of a single parameter type of self, or
235    /// `None` if self has no parameter at the given index.
236    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    /// Returns the number of arguments accepted by self.
248    pub fn arguments_count(&self) -> usize {
249        unsafe {
250            method_getNumberOfArguments(self) as usize
251        }
252    }
253
254    /// Returns the implementation of self.
255    pub fn implementation(&self) -> Imp {
256        unsafe {
257            method_getImplementation(self)
258        }
259    }
260}
261
262impl Class {
263    /// Returns the class definition of a specified class, or `None` if the
264    /// class is not registered with the Objective-C runtime.
265    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    /// Obtains the list of registered class definitions.
274    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    /// Returns the total number of registered classes.
283    pub fn classes_count() -> usize {
284        unsafe {
285            objc_getClassList(ptr::null_mut(), 0) as usize
286        }
287    }
288
289    /// Returns the name of self.
290    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    /// Returns the superclass of self, or `None` if self is a root class.
298    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    /// Returns the metaclass of self.
306    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    /// Returns the size of instances of self.
314    pub fn instance_size(&self) -> usize {
315        unsafe {
316            class_getInstanceSize(self)
317        }
318    }
319
320    /// Returns a specified instance method for self, or `None` if self and
321    /// its superclasses do not contain an instance method with the
322    /// specified selector.
323    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    /// Returns the ivar for a specified instance variable of self, or `None`
331    /// if self has no ivar with the given name.
332    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    /// Describes the instance methods implemented by self.
341    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    /// Checks whether this class conforms to the specified protocol.
351    pub fn conforms_to(&self, proto: &Protocol) -> bool {
352        unsafe { class_conformsToProtocol(self, proto) == YES }
353    }
354
355    /// Get a list of the protocols to which this class conforms.
356    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    /// Describes the instance variables declared by self.
365    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    /// Returns the protocol definition of a specified protocol, or `None` if the
392    /// protocol is not registered with the Objective-C runtime.
393    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    /// Obtains the list of registered protocol definitions.
402    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    /// Get a list of the protocols to which this protocol conforms.
411    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    /// Checks whether this protocol conforms to the specified protocol.
420    pub fn conforms_to(&self, proto: &Protocol) -> bool {
421        unsafe { protocol_conformsToProtocol(self, proto) == YES }
422    }
423
424    /// Returns the name of self.
425    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    /// Returns the class of self.
449    pub fn class(&self) -> &Class {
450        unsafe {
451            &*object_getClass(self)
452        }
453    }
454
455    /// Returns a reference to the ivar of self with the given name.
456    /// Panics if self has no ivar with the given name.
457    /// Unsafe because the caller must ensure that the ivar is actually
458    /// of type `T`.
459    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    /// Returns a mutable reference to the ivar of self with the given name.
478    /// Panics if self has no ivar with the given name.
479    /// Unsafe because the caller must ensure that the ivar is actually
480    /// of type `T`.
481    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    /// Sets the value of the ivar of self with the given name.
501    /// Panics if self has no ivar with the given name.
502    /// Unsafe because the caller must ensure that the ivar is actually
503    /// of type `T`.
504    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/*
516#[cfg(test)]
517mod tests {
518    use test_utils;
519    use Encode;
520    use super::{Class, Protocol, Sel};
521
522    #[test]
523    fn test_ivar() {
524        let cls = test_utils::custom_class();
525        let ivar = cls.instance_variable("_foo").unwrap();
526        assert!(ivar.name() == "_foo");
527        assert!(ivar.type_encoding() == <u32>::encode());
528        assert!(ivar.offset() > 0);
529
530        let ivars = cls.instance_variables();
531        assert!(ivars.len() > 0);
532    }
533
534    #[test]
535    fn test_method() {
536        let cls = test_utils::custom_class();
537        let sel = Sel::register("foo");
538        let method = cls.instance_method(sel).unwrap();
539        assert!(method.name().name() == "foo");
540        assert!(method.arguments_count() == 2);
541        assert!(method.return_type() == <u32>::encode());
542        assert!(method.argument_type(1).unwrap() == Sel::encode());
543
544        let methods = cls.instance_methods();
545        assert!(methods.len() > 0);
546    }
547
548    #[test]
549    fn test_class() {
550        let cls = test_utils::custom_class();
551        assert!(cls.name() == "CustomObject");
552        assert!(cls.instance_size() > 0);
553        assert!(cls.superclass().is_none());
554
555        assert!(Class::get(cls.name()) == Some(cls));
556
557        let metaclass = cls.metaclass();
558        // The metaclass of a root class is a subclass of the root class
559        assert!(metaclass.superclass().unwrap() == cls);
560
561        let subclass = test_utils::custom_subclass();
562        assert!(subclass.superclass().unwrap() == cls);
563    }
564
565    #[test]
566    fn test_classes() {
567        assert!(Class::classes_count() > 0);
568        let classes = Class::classes();
569        assert!(classes.len() > 0);
570    }
571
572    #[test]
573    fn test_protocol() {
574        let proto = test_utils::custom_protocol();
575        assert!(proto.name() == "CustomProtocol");
576        let class = test_utils::custom_class();
577        assert!(class.conforms_to(proto));
578        let class_protocols = class.adopted_protocols();
579        assert!(class_protocols.len() > 0);
580    }
581
582    #[test]
583    fn test_protocol_method() {
584        let class = test_utils::custom_class();
585        let result: i32 = unsafe {
586            msg_send![class, addNumber:1 toNumber:2]
587        };
588        assert_eq!(result, 3);
589    }
590
591    #[test]
592    fn test_subprotocols() {
593        let sub_proto = test_utils::custom_subprotocol();
594        let super_proto = test_utils::custom_protocol();
595        assert!(sub_proto.conforms_to(super_proto));
596        let adopted_protocols = sub_proto.adopted_protocols();
597        assert_eq!(adopted_protocols[0], super_proto);
598    }
599
600    #[test]
601    fn test_protocols() {
602        // Ensure that a protocol has been registered on linux
603        let _ = test_utils::custom_protocol();
604
605        let protocols = Protocol::protocols();
606        assert!(protocols.len() > 0);
607    }
608
609    #[test]
610    fn test_object() {
611        let mut obj = test_utils::custom_object();
612        assert!(obj.class() == test_utils::custom_class());
613        let result: u32 = unsafe {
614            obj.set_ivar("_foo", 4u32);
615            *obj.get_ivar("_foo")
616        };
617        assert!(result == 4);
618    }
619}*/