diff --git a/Cargo.toml b/Cargo.toml index 75b073626..35b2465f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "objc_foundation", "objc_foundation_derive", "objc_id", + "objc_sys", "objc_test_utils", ] exclude = ["objc/tests-ios"] diff --git a/objc/Cargo.toml b/objc/Cargo.toml index e28336916..a9487ba07 100644 --- a/objc/Cargo.toml +++ b/objc/Cargo.toml @@ -28,5 +28,6 @@ unstable_autoreleasesafe = [] [dependencies] malloc_buf = "1.0" +objc_sys = { path = "../objc_sys" } objc-encode = { path = "../objc_encode", version = "1.0" } objc_exception = { path = "../objc_exception", version = "0.1", optional = true } diff --git a/objc/src/cache.rs b/objc/src/cache.rs index 4cfb1ae96..bf9080f53 100644 --- a/objc/src/cache.rs +++ b/objc/src/cache.rs @@ -26,9 +26,9 @@ impl CachedSel { // It should be fine to use `Relaxed` ordering here because `sel_registerName` is // thread-safe. if ptr.is_null() { - let sel = runtime::sel_registerName(name.as_ptr() as *const _); - self.ptr.store(sel.as_ptr() as *mut _, Ordering::Relaxed); - sel + let ptr = runtime::sel_registerName(name.as_ptr() as *const _); + self.ptr.store(ptr as *mut _, Ordering::Relaxed); + Sel::from_ptr(ptr as *const _) } else { Sel::from_ptr(ptr) } @@ -56,7 +56,7 @@ impl CachedClass { // `Relaxed` should be fine since `objc_getClass` is thread-safe. let ptr = self.ptr.load(Ordering::Relaxed); if ptr.is_null() { - let cls = runtime::objc_getClass(name.as_ptr() as *const _); + let cls = runtime::objc_getClass(name.as_ptr() as *const _) as *const Class; self.ptr.store(cls as *mut _, Ordering::Relaxed); cls.as_ref() } else { diff --git a/objc/src/declare.rs b/objc/src/declare.rs index 099fb7519..072cc4345 100644 --- a/objc/src/declare.rs +++ b/objc/src/declare.rs @@ -118,12 +118,12 @@ pub struct ClassDecl { impl ClassDecl { fn with_superclass(name: &str, superclass: Option<&Class>) -> Option { let name = CString::new(name).unwrap(); - let super_ptr = superclass.map_or(ptr::null(), |c| c); + let super_ptr = superclass.map_or(ptr::null(), |c| c) as _; let cls = unsafe { runtime::objc_allocateClassPair(super_ptr, name.as_ptr(), 0) }; if cls.is_null() { None } else { - Some(ClassDecl { cls }) + Some(ClassDecl { cls: cls as _ }) } } @@ -182,7 +182,12 @@ impl ClassDecl { ); let types = method_type_encoding(&F::Ret::ENCODING, encs); - let success = runtime::class_addMethod(self.cls, sel, func.imp(), types.as_ptr()); + let success = runtime::class_addMethod( + self.cls as _, + sel.as_ptr() as _, + Some(func.imp()), + types.as_ptr(), + ); assert!(success != NO, "Failed to add method {:?}", sel); } @@ -212,7 +217,12 @@ impl ClassDecl { let types = method_type_encoding(&F::Ret::ENCODING, encs); let metaclass = (*self.cls).metaclass() as *const _ as *mut _; - let success = runtime::class_addMethod(metaclass, sel, func.imp(), types.as_ptr()); + let success = runtime::class_addMethod( + metaclass, + sel.as_ptr() as _, + Some(func.imp()), + types.as_ptr(), + ); assert!(success != NO, "Failed to add class method {:?}", sel); } @@ -227,7 +237,13 @@ impl ClassDecl { let size = mem::size_of::(); let align = log2_align_of::(); let success = unsafe { - runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align, encoding.as_ptr()) + runtime::class_addIvar( + self.cls as _, + c_name.as_ptr(), + size, + align, + encoding.as_ptr(), + ) }; assert!(success != NO, "Failed to add ivar {}", name); } @@ -238,7 +254,7 @@ impl ClassDecl { /// /// If the protocol wasn't successfully added. pub fn add_protocol(&mut self, proto: &Protocol) { - let success = unsafe { runtime::class_addProtocol(self.cls, proto) }; + let success = unsafe { runtime::class_addProtocol(self.cls as _, proto.as_ptr()) }; assert!(success != NO, "Failed to add protocol {:?}", proto); } @@ -247,7 +263,7 @@ impl ClassDecl { pub fn register(self) -> &'static Class { unsafe { let cls = self.cls; - runtime::objc_registerClassPair(cls); + runtime::objc_registerClassPair(cls as _); // Forget self otherwise the class will be disposed in drop mem::forget(self); &*cls @@ -258,7 +274,7 @@ impl ClassDecl { impl Drop for ClassDecl { fn drop(&mut self) { unsafe { - runtime::objc_disposeClassPair(self.cls); + runtime::objc_disposeClassPair(self.cls as _); } } } @@ -275,7 +291,7 @@ impl ProtocolDecl { /// Returns [`None`] if the protocol couldn't be allocated. pub fn new(name: &str) -> Option { let c_name = CString::new(name).unwrap(); - let proto = unsafe { runtime::objc_allocateProtocol(c_name.as_ptr()) }; + let proto = unsafe { runtime::objc_allocateProtocol(c_name.as_ptr()) } as *mut Protocol; if proto.is_null() { None } else { @@ -303,8 +319,8 @@ impl ProtocolDecl { let types = method_type_encoding(&Ret::ENCODING, encs); unsafe { runtime::protocol_addMethodDescription( - self.proto, - sel, + self.proto as _, + sel.as_ptr() as _, types.as_ptr(), is_required as BOOL, is_instance_method as BOOL, @@ -333,7 +349,7 @@ impl ProtocolDecl { /// Adds a requirement on another protocol. pub fn add_protocol(&mut self, proto: &Protocol) { unsafe { - runtime::protocol_addProtocol(self.proto, proto); + runtime::protocol_addProtocol(self.proto as _, proto.as_ptr()); } } @@ -341,7 +357,7 @@ impl ProtocolDecl { /// to the newly registered [`Protocol`]. pub fn register(self) -> &'static Protocol { unsafe { - runtime::objc_registerProtocol(self.proto); + runtime::objc_registerProtocol(self.proto as _); &*self.proto } } diff --git a/objc/src/message/apple/arm.rs b/objc/src/message/apple/arm.rs index c7ec6345d..370d7453a 100644 --- a/objc/src/message/apple/arm.rs +++ b/objc/src/message/apple/arm.rs @@ -1,18 +1,10 @@ use core::mem; +use objc_sys::{objc_msgSend, objc_msgSendSuper, objc_msgSendSuper_stret, objc_msgSend_stret}; use super::MsgSendFn; use crate::runtime::Imp; use crate::{Encode, Encoding}; -// TODO: C-unwind -extern "C" { - fn objc_msgSend(); - fn objc_msgSend_stret(); - - fn objc_msgSendSuper(); - fn objc_msgSendSuper_stret(); -} - /// Double-word sized fundamental data types don't use stret, but any /// composite type larger than 4 bytes does. /// diff --git a/objc/src/message/apple/arm64.rs b/objc/src/message/apple/arm64.rs index a8be08411..bd83d2d26 100644 --- a/objc/src/message/apple/arm64.rs +++ b/objc/src/message/apple/arm64.rs @@ -1,14 +1,9 @@ +use objc_sys::{objc_msgSend, objc_msgSendSuper}; + use super::MsgSendFn; use crate::runtime::Imp; use crate::Encode; -// TODO: C-unwind -extern "C" { - fn objc_msgSend(); - - fn objc_msgSendSuper(); -} - /// `objc_msgSend_stret` is not even available in arm64. /// /// diff --git a/objc/src/message/apple/mod.rs b/objc/src/message/apple/mod.rs index c0b7c1ced..792ef0659 100644 --- a/objc/src/message/apple/mod.rs +++ b/objc/src/message/apple/mod.rs @@ -1,4 +1,6 @@ -use super::{Encode, Message, MessageArguments, MessageError, Super}; +use objc_sys::objc_super; + +use super::{Encode, Message, MessageArguments, MessageError}; use crate::runtime::{Class, Imp, Object, Sel}; #[cfg(target_arch = "x86")] @@ -43,11 +45,11 @@ where A: MessageArguments, R: Encode, { - let sup = Super { - receiver: obj as *mut T as *mut Object, - superclass, + let sup = objc_super { + receiver: obj as *mut T as *mut Object as *mut _, + super_class: superclass as *const Class as *const _, }; - let receiver = &sup as *const Super as *mut Object; + let receiver = &sup as *const objc_super as *mut Object; let msg_send_fn = R::MSG_SEND_SUPER; objc_try!({ A::invoke(msg_send_fn, receiver, sel, args) }) } diff --git a/objc/src/message/apple/x86.rs b/objc/src/message/apple/x86.rs index 3444863c0..9398acddf 100644 --- a/objc/src/message/apple/x86.rs +++ b/objc/src/message/apple/x86.rs @@ -1,19 +1,13 @@ use core::mem; +use objc_sys::{ + objc_msgSend, objc_msgSendSuper, objc_msgSendSuper_stret, objc_msgSend_fpret, + objc_msgSend_stret, +}; use super::MsgSendFn; use crate::runtime::Imp; use crate::{Encode, Encoding}; -// TODO: C-unwind -extern "C" { - fn objc_msgSend(); - fn objc_msgSend_fpret(); - fn objc_msgSend_stret(); - - fn objc_msgSendSuper(); - fn objc_msgSendSuper_stret(); -} - /// Structures 1 or 2 bytes in size are placed in EAX. /// Structures 4 or 8 bytes in size are placed in: EAX and EDX. /// Structures of other sizes are placed at the address supplied by the caller. diff --git a/objc/src/message/apple/x86_64.rs b/objc/src/message/apple/x86_64.rs index b8f333d89..fec88daa3 100644 --- a/objc/src/message/apple/x86_64.rs +++ b/objc/src/message/apple/x86_64.rs @@ -1,24 +1,17 @@ use core::mem; +use objc_sys::{objc_msgSend, objc_msgSendSuper, objc_msgSendSuper_stret, objc_msgSend_stret}; use super::MsgSendFn; use crate::runtime::Imp; use crate::Encode; -// TODO: C-unwind -extern "C" { - fn objc_msgSend(); - fn objc_msgSend_stret(); - - fn objc_msgSendSuper(); - fn objc_msgSendSuper_stret(); -} - /// If the size of an object is larger than two eightbytes, it has class /// MEMORY. If the type has class MEMORY, then the caller provides space for /// the return value and passes the address of this storage. /// /// impl MsgSendFn for T { + // TODO: Should we use objc_msgSend_fpret and objc_msgSend_fp2ret ? const MSG_SEND: Imp = { if mem::size_of::() <= 16 { objc_msgSend diff --git a/objc/src/message/gnustep.rs b/objc/src/message/gnustep.rs index fb901cb54..2d0474184 100644 --- a/objc/src/message/gnustep.rs +++ b/objc/src/message/gnustep.rs @@ -1,12 +1,8 @@ use core::mem; +use objc_sys::{objc_msg_lookup, objc_msg_lookup_super, objc_super}; -use super::{Encode, Message, MessageArguments, MessageError, Super}; -use crate::runtime::{Class, Imp, Object, Sel}; - -extern "C" { - fn objc_msg_lookup(receiver: *mut Object, op: Sel) -> Imp; - fn objc_msg_lookup_super(sup: *const Super, sel: Sel) -> Imp; -} +use super::{Encode, Message, MessageArguments, MessageError}; +use crate::runtime::{Class, Object, Sel}; pub unsafe fn send_unverified(obj: *const T, sel: Sel, args: A) -> Result where @@ -19,8 +15,8 @@ where } let receiver = obj as *mut T as *mut Object; - let msg_send_fn = objc_msg_lookup(receiver, sel); - objc_try!({ A::invoke(msg_send_fn, receiver, sel, args) }) + let msg_send_fn = objc_msg_lookup(receiver as *mut _, sel.as_ptr() as *const _); + objc_try!({ A::invoke(msg_send_fn.expect("Null IMP"), receiver, sel, args) }) } pub unsafe fn send_super_unverified( @@ -35,10 +31,10 @@ where R: Encode, { let receiver = obj as *mut T as *mut Object; - let sup = Super { - receiver: receiver, - superclass: superclass, + let sup = objc_super { + receiver: receiver as *mut _, + super_class: superclass as *const Class as *const _, }; - let msg_send_fn = objc_msg_lookup_super(&sup, sel); - objc_try!({ A::invoke(msg_send_fn, receiver, sel, args) }) + let msg_send_fn = objc_msg_lookup_super(&sup, sel.as_ptr() as *const _); + objc_try!({ A::invoke(msg_send_fn.expect("Null IMP"), receiver, sel, args) }) } diff --git a/objc/src/message/mod.rs b/objc/src/message/mod.rs index 8a54f7b86..30e022a81 100644 --- a/objc/src/message/mod.rs +++ b/objc/src/message/mod.rs @@ -39,15 +39,6 @@ mod platform; use self::platform::{send_super_unverified, send_unverified}; use self::verify::{verify_message_signature, VerificationError}; -/// Specifies the superclass of an instance. -#[repr(C)] -struct Super { - /// Specifies an instance of a class. - pub receiver: *mut Object, - /// Specifies the particular superclass of the instance to message. - pub superclass: *const Class, -} - /// This trait marks types that can be sent Objective-C messages. /// /// Examples include objects, classes, and blocks. diff --git a/objc/src/rc/strong.rs b/objc/src/rc/strong.rs index 67eca8824..eb2264443 100644 --- a/objc/src/rc/strong.rs +++ b/objc/src/rc/strong.rs @@ -27,7 +27,7 @@ impl StrongPtr { /// /// The caller must ensure the given object pointer is valid. pub unsafe fn retain(ptr: *mut Object) -> Self { - StrongPtr(runtime::objc_retain(ptr)) + Self(runtime::objc_retain(ptr as _) as _) } /// Autoreleases self, meaning that the object is not immediately released, @@ -37,7 +37,7 @@ impl StrongPtr { let ptr = self.0; mem::forget(self); unsafe { - runtime::objc_autorelease(ptr); + runtime::objc_autorelease(ptr as _); } ptr } @@ -51,7 +51,7 @@ impl StrongPtr { impl Drop for StrongPtr { fn drop(&mut self) { unsafe { - runtime::objc_release(self.0); + runtime::objc_release(self.0 as _); } } } diff --git a/objc/src/rc/weak.rs b/objc/src/rc/weak.rs index da24cf02b..448810a2e 100644 --- a/objc/src/rc/weak.rs +++ b/objc/src/rc/weak.rs @@ -21,7 +21,7 @@ impl WeakPtr { /// The caller must ensure the given object pointer is valid. pub unsafe fn new(obj: *mut Object) -> Self { let ptr = Box::new(UnsafeCell::new(ptr::null_mut())); - runtime::objc_initWeak(ptr.get(), obj); + runtime::objc_initWeak(ptr.get() as _, obj as _); WeakPtr(ptr) } @@ -29,8 +29,8 @@ impl WeakPtr { /// If the object has been deallocated, the returned pointer will be null. pub fn load(&self) -> StrongPtr { unsafe { - let ptr = runtime::objc_loadWeakRetained(self.0.get()); - StrongPtr::new(ptr) + let ptr = runtime::objc_loadWeakRetained(self.0.get() as _); + StrongPtr::new(ptr as _) } } } @@ -38,7 +38,7 @@ impl WeakPtr { impl Drop for WeakPtr { fn drop(&mut self) { unsafe { - runtime::objc_destroyWeak(self.0.get()); + runtime::objc_destroyWeak(self.0.get() as _); } } } @@ -47,7 +47,7 @@ impl Clone for WeakPtr { fn clone(&self) -> Self { let ptr = Box::new(UnsafeCell::new(ptr::null_mut())); unsafe { - runtime::objc_copyWeak(ptr.get(), self.0.get()); + runtime::objc_copyWeak(ptr.get() as _, self.0.get() as _); } WeakPtr(ptr) } diff --git a/objc/src/runtime.rs b/objc/src/runtime.rs index c402bbd71..bb3367197 100644 --- a/objc/src/runtime.rs +++ b/objc/src/runtime.rs @@ -9,173 +9,72 @@ use core::ptr; use core::str; use malloc_buf::Malloc; use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_int, c_uint}; +use std::os::raw::c_uint; use crate::Encode; -/// The Objective-C `BOOL` type. -/// -/// To convert an Objective-C `BOOL` into a Rust [`bool`], compare it with [`NO`]. -#[cfg(all(target_vendor = "apple", not(target_arch = "aarch64")))] -pub type BOOL = ::std::os::raw::c_schar; -#[cfg(all(not(target_vendor = "apple"), not(target_arch = "aarch64")))] -pub type BOOL = u8; - -/// The equivalent of true for Objective-C's [`BOOL`] type. -#[cfg(not(target_arch = "aarch64"))] -pub const YES: BOOL = 1; -/// The equivalent of false for Objective-C's [`BOOL`] type. -#[cfg(not(target_arch = "aarch64"))] -pub const NO: BOOL = 0; - -#[cfg(target_arch = "aarch64")] -pub type BOOL = bool; -#[cfg(target_arch = "aarch64")] -pub const YES: BOOL = true; -#[cfg(target_arch = "aarch64")] -pub const NO: BOOL = false; +use objc_sys; +#[allow(deprecated)] +pub use objc_sys::object_dispose; +pub use objc_sys::{ + class_addIvar, class_addMethod, class_addProtocol, class_conformsToProtocol, + class_copyIvarList, class_copyMethodList, class_copyProtocolList, class_createInstance, + class_getInstanceMethod, class_getInstanceSize, class_getInstanceVariable, class_getName, + class_getSuperclass, ivar_getName, ivar_getOffset, ivar_getTypeEncoding, + method_copyArgumentType, method_copyReturnType, method_exchangeImplementations, + method_getImplementation, method_getName, method_getNumberOfArguments, + method_setImplementation, objc_allocateClassPair, objc_allocateProtocol, objc_autorelease, + objc_autoreleasePoolPop, objc_autoreleasePoolPush, objc_copyClassList, objc_copyProtocolList, + objc_copyWeak, objc_destroyWeak, objc_disposeClassPair, objc_getClass, objc_getClassList, + objc_getProtocol, objc_initWeak, objc_loadWeakRetained, objc_registerClassPair, + objc_registerProtocol, objc_release, objc_retain, object_getClass, + protocol_addMethodDescription, protocol_addProtocol, protocol_conformsToProtocol, + protocol_copyProtocolList, protocol_getName, protocol_isEqual, sel_getName, sel_registerName, +}; +pub use objc_sys::{BOOL, NO, YES}; /// A type that represents a method selector. -#[repr(C)] +#[repr(transparent)] pub struct Sel { - ptr: *const c_void, + ptr: *const objc_sys::objc_selector, } -/// A marker type to be embedded into other types just so that they cannot be -/// constructed externally. -type PrivateMarker = [u8; 0]; - /// A type that represents an instance variable. #[repr(C)] -pub struct Ivar { - _priv: PrivateMarker, -} +pub struct Ivar(objc_sys::objc_ivar); /// A type that represents a method in a class definition. #[repr(C)] -pub struct Method { - _priv: PrivateMarker, -} +pub struct Method(objc_sys::objc_method); /// A type that represents an Objective-C class. #[repr(C)] -pub struct Class { - _priv: PrivateMarker, -} +pub struct Class(objc_sys::objc_class); /// A type that represents an Objective-C protocol. #[repr(C)] -pub struct Protocol { - _priv: PrivateMarker, -} +pub struct Protocol(objc_sys::Protocol); /// A type that represents an instance of a class. #[repr(C)] -pub struct Object { - _priv: PrivateMarker, -} +pub struct Object(objc_sys::objc_object); /// A pointer to the start of a method implementation. pub type Imp = unsafe extern "C" fn(); -#[allow(missing_docs)] -#[link(name = "objc", kind = "dylib")] -extern "C" { - pub fn sel_registerName(name: *const c_char) -> Sel; - pub fn sel_getName(sel: Sel) -> *const c_char; - - pub fn class_getName(cls: *const Class) -> *const c_char; - pub fn class_getSuperclass(cls: *const Class) -> *const Class; - pub fn class_getInstanceSize(cls: *const Class) -> usize; - pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method; - pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar; - pub fn class_copyMethodList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Method; - pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar; - pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> BOOL; - pub fn class_addIvar( - cls: *mut Class, - name: *const c_char, - size: usize, - alignment: u8, - types: *const c_char, - ) -> BOOL; - pub fn class_addProtocol(cls: *mut Class, proto: *const Protocol) -> BOOL; - pub fn class_conformsToProtocol(cls: *const Class, proto: *const Protocol) -> BOOL; - pub fn class_copyProtocolList(cls: *const Class, outCount: *mut c_uint) - -> *mut *const Protocol; - - pub fn objc_allocateClassPair( - superclass: *const Class, - name: *const c_char, - extraBytes: usize, - ) -> *mut Class; - pub fn objc_disposeClassPair(cls: *mut Class); - pub fn objc_registerClassPair(cls: *mut Class); - - pub fn class_createInstance(cls: *const Class, extraBytes: usize) -> *mut Object; - pub fn object_dispose(obj: *mut Object) -> *mut Object; - pub fn object_getClass(obj: *const Object) -> *const Class; - - pub fn objc_getClassList(buffer: *mut *const Class, bufferLen: c_int) -> c_int; - pub fn objc_copyClassList(outCount: *mut c_uint) -> *mut *const Class; - pub fn objc_getClass(name: *const c_char) -> *const Class; - pub fn objc_getProtocol(name: *const c_char) -> *const Protocol; - pub fn objc_copyProtocolList(outCount: *mut c_uint) -> *mut *const Protocol; - pub fn objc_allocateProtocol(name: *const c_char) -> *mut Protocol; - pub fn objc_registerProtocol(proto: *mut Protocol); - - pub fn objc_autoreleasePoolPush() -> *mut c_void; - pub fn objc_autoreleasePoolPop(context: *mut c_void); - - pub fn protocol_addMethodDescription( - proto: *mut Protocol, - name: Sel, - types: *const c_char, - isRequiredMethod: BOOL, - isInstanceMethod: BOOL, - ); - pub fn protocol_addProtocol(proto: *mut Protocol, addition: *const Protocol); - pub fn protocol_getName(proto: *const Protocol) -> *const c_char; - pub fn protocol_isEqual(proto: *const Protocol, other: *const Protocol) -> BOOL; - pub fn protocol_copyProtocolList( - proto: *const Protocol, - outCount: *mut c_uint, - ) -> *mut *const Protocol; - pub fn protocol_conformsToProtocol(proto: *const Protocol, other: *const Protocol) -> BOOL; - - pub fn ivar_getName(ivar: *const Ivar) -> *const c_char; - pub fn ivar_getOffset(ivar: *const Ivar) -> isize; - pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char; - - pub fn method_getName(method: *const Method) -> Sel; - pub fn method_getImplementation(method: *const Method) -> Imp; - pub fn method_copyReturnType(method: *const Method) -> *mut c_char; - pub fn method_copyArgumentType(method: *const Method, index: c_uint) -> *mut c_char; - pub fn method_getNumberOfArguments(method: *const Method) -> c_uint; - pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp; - pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method); - - pub fn objc_retain(obj: *mut Object) -> *mut Object; - pub fn objc_release(obj: *mut Object); - pub fn objc_autorelease(obj: *mut Object); - - pub fn objc_loadWeakRetained(location: *mut *mut Object) -> *mut Object; - pub fn objc_initWeak(location: *mut *mut Object, obj: *mut Object) -> *mut Object; - pub fn objc_destroyWeak(location: *mut *mut Object); - pub fn objc_copyWeak(to: *mut *mut Object, from: *mut *mut Object); -} - impl Sel { /// Registers a method with the Objective-C runtime system, /// maps the method name to a selector, and returns the selector value. - pub fn register(name: &str) -> Sel { + pub fn register(name: &str) -> Self { let name = CString::new(name).unwrap(); - unsafe { sel_registerName(name.as_ptr()) } + Self { + ptr: unsafe { sel_registerName(name.as_ptr()) }, + } } /// Returns the name of the method specified by self. pub fn name(&self) -> &str { - let name = unsafe { CStr::from_ptr(sel_getName(*self)) }; + let name = unsafe { CStr::from_ptr(sel_getName(self.ptr)) }; str::from_utf8(name.to_bytes()).unwrap() } @@ -187,14 +86,16 @@ impl Sel { /// /// This is almost never what you want; use [`Sel::register`] instead. #[inline] - pub unsafe fn from_ptr(ptr: *const c_void) -> Sel { - Sel { ptr } + pub unsafe fn from_ptr(ptr: *const c_void) -> Self { + Self { + ptr: ptr as *const objc_sys::objc_selector, + } } /// Returns a pointer to the raw selector. #[inline] pub fn as_ptr(&self) -> *const c_void { - self.ptr + self.ptr as *const c_void } } @@ -213,8 +114,8 @@ unsafe impl Send for Sel {} impl Copy for Sel {} impl Clone for Sel { - fn clone(&self) -> Sel { - *self + fn clone(&self) -> Self { + Self { ptr: self.ptr } } } @@ -225,35 +126,45 @@ impl fmt::Debug for Sel { } impl Ivar { + pub(crate) fn as_ptr(&self) -> *const objc_sys::objc_ivar { + self as *const Self as *const _ + } + /// Returns the name of self. pub fn name(&self) -> &str { - let name = unsafe { CStr::from_ptr(ivar_getName(self)) }; + let name = unsafe { CStr::from_ptr(ivar_getName(self.as_ptr())) }; str::from_utf8(name.to_bytes()).unwrap() } /// Returns the offset of self. pub fn offset(&self) -> isize { - let offset = unsafe { ivar_getOffset(self) }; + let offset = unsafe { ivar_getOffset(self.as_ptr()) }; offset as isize } /// Returns the `Encoding` of self. pub fn type_encoding(&self) -> &str { - let encoding = unsafe { CStr::from_ptr(ivar_getTypeEncoding(self)) }; + let encoding = unsafe { CStr::from_ptr(ivar_getTypeEncoding(self.as_ptr())) }; str::from_utf8(encoding.to_bytes()).unwrap() } } impl Method { + pub(crate) fn as_ptr(&self) -> *const objc_sys::objc_method { + self as *const Self as *const _ + } + /// Returns the name of self. pub fn name(&self) -> Sel { - unsafe { method_getName(self) } + Sel { + ptr: unsafe { method_getName(self.as_ptr()) }, + } } /// Returns the `Encoding` of self's return type. pub fn return_type(&self) -> Malloc { unsafe { - let encoding = method_copyReturnType(self); + let encoding = method_copyReturnType(self.as_ptr()); Malloc::from_c_str(encoding).unwrap() } } @@ -262,7 +173,7 @@ impl Method { /// [`None`] if self has no parameter at the given index. pub fn argument_type(&self, index: usize) -> Option> { unsafe { - let encoding = method_copyArgumentType(self, index as c_uint); + let encoding = method_copyArgumentType(self.as_ptr(), index as c_uint); if encoding.is_null() { None } else { @@ -273,32 +184,36 @@ impl Method { /// Returns the number of arguments accepted by self. pub fn arguments_count(&self) -> usize { - unsafe { method_getNumberOfArguments(self) as usize } + unsafe { method_getNumberOfArguments(self.as_ptr()) as usize } } /// Returns the implementation of self. pub fn implementation(&self) -> Imp { - unsafe { method_getImplementation(self) } + unsafe { method_getImplementation(self.as_ptr()).expect("Null IMP") } } } impl Class { + pub(crate) fn as_ptr(&self) -> *const objc_sys::objc_class { + self as *const Self as *const _ + } + /// Returns the class definition of a specified class, or [`None`] if the /// class is not registered with the Objective-C runtime. - pub fn get(name: &str) -> Option<&'static Class> { + pub fn get(name: &str) -> Option<&'static Self> { let name = CString::new(name).unwrap(); unsafe { let cls = objc_getClass(name.as_ptr()); if cls.is_null() { None } else { - Some(&*cls) + Some(&*(cls as *const Self)) } } } /// Obtains the list of registered class definitions. - pub fn classes() -> Malloc<[&'static Class]> { + pub fn classes() -> Malloc<[&'static Self]> { unsafe { let mut count: c_uint = 0; let classes = objc_copyClassList(&mut count); @@ -313,33 +228,30 @@ impl Class { /// Returns the name of the class. pub fn name(&self) -> &str { - let name = unsafe { CStr::from_ptr(class_getName(self)) }; + let name = unsafe { CStr::from_ptr(class_getName(self.as_ptr())) }; str::from_utf8(name.to_bytes()).unwrap() } /// Returns the superclass of self, or [`None`] if self is a root class. pub fn superclass(&self) -> Option<&Class> { unsafe { - let superclass = class_getSuperclass(self); + let superclass = class_getSuperclass(self.as_ptr()); if superclass.is_null() { None } else { - Some(&*superclass) + Some(&*(superclass as *const Self)) } } } /// Returns the metaclass of self. - pub fn metaclass(&self) -> &Class { - unsafe { - let self_ptr: *const Class = self; - &*object_getClass(self_ptr as *const Object) - } + pub fn metaclass(&self) -> &Self { + unsafe { &*(object_getClass(self.as_ptr() as *const _) as *const Self) } } /// Returns the size of instances of self. pub fn instance_size(&self) -> usize { - unsafe { class_getInstanceSize(self) as usize } + unsafe { class_getInstanceSize(self.as_ptr()) as usize } } /// Returns a specified instance method for self, or [`None`] if self and @@ -347,11 +259,11 @@ impl Class { /// selector. pub fn instance_method(&self, sel: Sel) -> Option<&Method> { unsafe { - let method = class_getInstanceMethod(self, sel); + let method = class_getInstanceMethod(self.as_ptr(), sel.ptr); if method.is_null() { None } else { - Some(&*method) + Some(&*(method as *const Method)) } } } @@ -361,11 +273,11 @@ impl Class { pub fn instance_variable(&self, name: &str) -> Option<&Ivar> { let name = CString::new(name).unwrap(); unsafe { - let ivar = class_getInstanceVariable(self, name.as_ptr()); + let ivar = class_getInstanceVariable(self.as_ptr(), name.as_ptr()); if ivar.is_null() { None } else { - Some(&*ivar) + Some(&*(ivar as *const Ivar)) } } } @@ -374,21 +286,21 @@ impl Class { pub fn instance_methods(&self) -> Malloc<[&Method]> { unsafe { let mut count: c_uint = 0; - let methods = class_copyMethodList(self, &mut count); + let methods = class_copyMethodList(self.as_ptr(), &mut count); Malloc::from_array(methods as *mut _, count as usize) } } /// Checks whether this class conforms to the specified protocol. pub fn conforms_to(&self, proto: &Protocol) -> bool { - unsafe { class_conformsToProtocol(self, proto) == YES } + unsafe { class_conformsToProtocol(self.as_ptr(), proto.as_ptr()) == YES } } /// Get a list of the protocols to which this class conforms. pub fn adopted_protocols(&self) -> Malloc<[&Protocol]> { unsafe { let mut count: c_uint = 0; - let protos = class_copyProtocolList(self, &mut count); + let protos = class_copyProtocolList(self.as_ptr(), &mut count); Malloc::from_array(protos as *mut _, count as usize) } } @@ -397,7 +309,7 @@ impl Class { pub fn instance_variables(&self) -> Malloc<[&Ivar]> { unsafe { let mut count: c_uint = 0; - let ivars = class_copyIvarList(self, &mut count); + let ivars = class_copyIvarList(self.as_ptr(), &mut count); Malloc::from_array(ivars as *mut _, count as usize) } } @@ -405,9 +317,7 @@ impl Class { impl PartialEq for Class { fn eq(&self, other: &Class) -> bool { - let self_ptr: *const Class = self; - let other_ptr: *const Class = other; - self_ptr == other_ptr + self.as_ptr() == other.as_ptr() } } @@ -420,6 +330,10 @@ impl fmt::Debug for Class { } impl Protocol { + pub(crate) fn as_ptr(&self) -> *const objc_sys::Protocol { + self as *const Self as *const _ + } + /// Returns the protocol definition of a specified protocol, or [`None`] /// if the protocol is not registered with the Objective-C runtime. pub fn get(name: &str) -> Option<&'static Protocol> { @@ -429,7 +343,7 @@ impl Protocol { if proto.is_null() { None } else { - Some(&*proto) + Some(&*(proto as *const Self)) } } } @@ -447,26 +361,26 @@ impl Protocol { pub fn adopted_protocols(&self) -> Malloc<[&Protocol]> { unsafe { let mut count: c_uint = 0; - let protocols = protocol_copyProtocolList(self, &mut count); + let protocols = protocol_copyProtocolList(self.as_ptr(), &mut count); Malloc::from_array(protocols as *mut _, count as usize) } } /// Checks whether this protocol conforms to the specified protocol. pub fn conforms_to(&self, proto: &Protocol) -> bool { - unsafe { protocol_conformsToProtocol(self, proto) == YES } + unsafe { protocol_conformsToProtocol(self.as_ptr(), proto.as_ptr()) == YES } } /// Returns the name of self. pub fn name(&self) -> &str { - let name = unsafe { CStr::from_ptr(protocol_getName(self)) }; + let name = unsafe { CStr::from_ptr(protocol_getName(self.as_ptr())) }; str::from_utf8(name.to_bytes()).unwrap() } } impl PartialEq for Protocol { fn eq(&self, other: &Protocol) -> bool { - unsafe { protocol_isEqual(self, other) == YES } + unsafe { protocol_isEqual(self.as_ptr(), other.as_ptr()) == YES } } } @@ -489,9 +403,13 @@ fn get_ivar_offset(cls: &Class, name: &str) -> isize { } impl Object { + pub(crate) fn as_ptr(&self) -> *const objc_sys::objc_object { + self as *const Self as *const _ + } + /// Returns the class of this object. pub fn class(&self) -> &Class { - unsafe { &*object_getClass(self) } + unsafe { &*(object_getClass(self.as_ptr()) as *const Class) } } /// Returns a shared reference to the ivar with the given name. @@ -543,7 +461,7 @@ impl Object { impl fmt::Debug for Object { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "<{:?}: {:p}>", self.class(), self) + write!(f, "<{:?}: {:p}>", self.class(), self.as_ptr()) } } diff --git a/objc/src/test_utils.rs b/objc/src/test_utils.rs index 7b2a80435..b7be60377 100644 --- a/objc/src/test_utils.rs +++ b/objc/src/test_utils.rs @@ -12,8 +12,9 @@ pub struct CustomObject { impl CustomObject { fn new(class: &Class) -> Self { - let obj = unsafe { runtime::class_createInstance(class, 0) }; - CustomObject { obj } + let ptr = class as *const Class as _; + let obj = unsafe { runtime::class_createInstance(ptr, 0) }; + CustomObject { obj: obj as _ } } } @@ -34,7 +35,8 @@ impl DerefMut for CustomObject { impl Drop for CustomObject { fn drop(&mut self) { unsafe { - runtime::object_dispose(self.obj); + #[allow(deprecated)] + runtime::object_dispose(self.obj as _); } } } diff --git a/objc_block/src/lib.rs b/objc_block/src/lib.rs index 5a32810e6..7d71eaa03 100644 --- a/objc_block/src/lib.rs +++ b/objc_block/src/lib.rs @@ -69,10 +69,7 @@ struct ClassInternal { _priv: [u8; 0], } -#[cfg_attr( - target_vendor = "apple", - link(name = "System", kind = "dylib") -)] +#[cfg_attr(target_vendor = "apple", link(name = "System", kind = "dylib"))] #[cfg_attr( not(target_vendor = "apple"), link(name = "BlocksRuntime", kind = "dylib") diff --git a/objc_sys/Cargo.toml b/objc_sys/Cargo.toml new file mode 100644 index 000000000..8da275f82 --- /dev/null +++ b/objc_sys/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "objc_sys" +version = "0.0.0" # Remember to update html_root_url in lib.rs +authors = ["Mads Marquart "] +edition = "2018" + +description = "Raw bindings to Objective-C runtimes" +keywords = ["objective-c", "macos", "ios", "objc_msgSend", "sys"] +categories = [ + "external-ffi-bindings", + # "no_std" # TODO + "os::macos-apis", +] +repository = "https://github.com/madsmtm/objc" +documentation = "https://docs.rs/objc_sys/" +license = "MIT" + +exclude = [ + # Used to help developers track changes by running bindgen against + # different revisions of Apple's open source `objc4`. + "helper-scripts/*", +] + +readme = "README.md" + +# Downstream users can customize the linking to libobjc! +# See https://doc.rust-lang.org/cargo/reference/build-scripts.html#overriding-build-scripts +links = "objc" +build = "build.rs" diff --git a/objc_sys/README.md b/objc_sys/README.md new file mode 100644 index 000000000..064b4507f --- /dev/null +++ b/objc_sys/README.md @@ -0,0 +1,100 @@ +# `objc_sys` + +[![Latest version](https://badgen.net/crates/v/objc_sys)](https://crates.io/crates/objc_sys) +[![License](https://badgen.net/badge/license/MIT/blue)](../LICENSE.txt) +[![Documentation](https://docs.rs/objc_sys/badge.svg)](https://docs.rs/objc_sys/) +[![CI Status](https://github.com/madsmtm/objc/workflows/CI/badge.svg)](https://github.com/madsmtm/objc/actions) + +Raw Rust bindings to core Objective-C runtimes and ABIs. + +## Runtime Support + +`objc_sys` currently supports two runtimes (support for [`ObjFW`] and +[`WinObjC`] may be added): +- Apple's [`objc4`] on `cfg(target_vendor = "apple")` targets. +- GNUStep's [`libobjc2`] on all other targets. See their [Objective-C Compiler + and Runtime FAQ][gnustep-faq]. + +This library will probably only ever support ["Modern"][modern] Objective-C +runtimes, since support for reference-counting primitives like `objc_retain` +and `objc_autoreleasePoolPop` is a vital requirement for most applications. + +Just so we're being clear, this rules out the GCC [`libobjc`][gcc-libobjc] +runtime (see [this][gcc-objc-support]), and the [`mulle-objc`] runtime. + +[`ObjFW`]: https://github.com/ObjFW/ObjFW +[`WinObjC`]: https://github.com/microsoft/WinObjC +[`objc4`]: https://opensource.apple.com/source/objc4/ +[`libobjc2`]: https://github.com/gnustep/libobjc2 +[gnustep-faq]: http://wiki.gnustep.org/index.php/Objective-C_Compiler_and_Runtime_FAQ +[modern]: https://en.wikipedia.org/wiki/Objective-C#Modern_Objective-C +[gcc-libobjc]: https://github.com/gcc-mirror/gcc/tree/master/libobjc +[gcc-objc-support]: https://gcc.gnu.org/onlinedocs/gcc/Standards.html#Objective-C-and-Objective-C_002b_002b-Languages +[`mulle-objc`]: https://github.com/mulle-objc/mulle-objc-runtime + + +## Required Versions + +At least `libobjc2` [version 1.7][libobjc2-1.7] or `objc4` +[version 493.9][objc4-493.9] is required. + +`objc4` version 493.9 is available with: +- **macOS 10.7** +- **iOS 5.0** +- **tvOS 9.0** +- **watchOS 1.0** +- **bridgeOS 2.0** + +So those are the **minimum supported Apple versions**. Functionality that was +added after these versions are not (yet?) available in `objc_sys`. + +[libobjc2-1.7]: https://github.com/gnustep/libobjc2/tree/1.7 +[objc4-493.9]: https://opensource.apple.com/source/objc4/ + + +## Configuring linking + +This crate defines the `links` key in `Cargo.toml` so it's possible to +change the linking to `libobjc`, see [the relevant cargo docs][overriding]. + +In the future, this crate may vendor the required source code to automatically +build and link to the runtimes. Choosing static vs. dynamic linking here may +also become an option. + +[overriding]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#overriding-build-scripts + + +## Objective-C compiler ABI configuration + +Objective-C compilers like `clang` and `gcc` requires configuring the calling +ABI to the runtime you're using: +- `clang` uses the [`-fobjc-runtime`] flag, of which there are a few different + [options][clang-objc-kinds]. +- `gcc` uses the [`-fgnu-runtime` or `-fnext-runtime`][gcc-flags] options. + Note that Modern Objective-C features are ill supported. + +This is relevant if you're building and linking to custom Objective-C sources +in a build script. In the future, this crate may expose build script metadata +to help with selecting these (and other required) flags. + +[`-fobjc-runtime`]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fobjc-runtime +[clang-objc-kinds]: https://clang.llvm.org/doxygen/classclang_1_1ObjCRuntime.html#af19fe070a7073df4ecc666b44137c4e5 +[gcc-flags]: https://gcc.gnu.org/onlinedocs/gcc/Objective-C-and-Objective-C_002b_002b-Dialect-Options.html + + +## Design choices + +It is recognized that the most primary consumer of this library will be macOS +and secondly iOS applications. Therefore it was chosen not to use `bindgen` in +our build script to not add compilation cost to those targets.1 + +Deprecated functions are also not included for future compability, since they +could be removed in any macOS release, and then our code would break. If you +have a need for these, please open an issue and we can discuss it! + +Some items (in particular the `objc_msgSend_X` family) have `cfg`s that prevent +their usage on different platforms; these are **semver-stable** in the sense +that they will only get less restrictive, never more. + +1 That said, most of this is created with the help of `bindgen`'s +commandline interface, so huge thanks to them! diff --git a/objc_sys/build.rs b/objc_sys/build.rs new file mode 100644 index 000000000..c2db6ca95 --- /dev/null +++ b/objc_sys/build.rs @@ -0,0 +1,23 @@ +use std::env; + +fn main() { + // Only rerun if this file changes; the script doesn't depend on our code + println!("cargo:rerun-if-changed=build.rs"); + // Link to libobjc + println!("cargo:rustc-link-lib=dylib=objc"); + + let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); + + // Adds useful #[cfg(apple)] and #[cfg(gnustep)] directives + if target_vendor == "apple" { + println!("cargo:rustc-cfg=apple"); + } else { + // TODO: Are there other possibilities than GNUStep? Airyx? Is it + // possible to use Apple's open source objc4 on other platforms? + println!("cargo:rustc-cfg=gnustep"); + // TODO: Should we vendor GNUStep's libobjc2? + // Using Cargo.toml: + // [target.'cfg(not(target_vendor = "apple"))'.build-dependencies] + // cc = "1.0" + } +} diff --git a/objc_sys/helper-scripts/gen-git.fish b/objc_sys/helper-scripts/gen-git.fish new file mode 100755 index 000000000..2a6eca1de --- /dev/null +++ b/objc_sys/helper-scripts/gen-git.fish @@ -0,0 +1,236 @@ +#!/usr/local/bin/fish + +# Yup, this is terrible, but was a great help in creating the correct implementations + +# Source repo should be a path to https://github.com/madsmtm/objc4-mirror.git +set source_repo $argv[1] +set to_repo $argv[2] + +git init $to_repo -b master + +cp headers.h $source_repo/runtime/headers.h + +set tags \ + # For these, headers.h needs to be set up differently + # macos-10.0 \ + # macos-10.1 \ + # macos-10.2 \ + # macos-10.3 \ + # macos-10.3.3 \ + # macos-10.4 \ + # macos-10.4.3 \ + # macos-10.4.4.x86 \ + # macos-10.4.6.ppc \ + macos-10.5 \ + macos-10.5.2 \ + macos-10.5.5 \ + macos-10.6 \ + macos-10.6.2 \ + macos-10.6.8 \ + # The actual minimum target we're interested in + macos-10.7 \ + macos-10.7.3 \ + macos-10.8 \ + macos-10.8.2 \ + macos-10.9 \ + macos-10.10 \ + macos-10.10.2 \ + macos-10.11 \ + macos-10.12 \ + macos-10.12.4 \ + macos-10.12.6 \ + macos-10.13 \ + macos-10.14 \ + macos-10.14.1 \ + macos-10.14.4 \ + macos-10.15 \ + macos-10.15.1 \ + macos-10.15.2 \ + macos-10.15.4 \ + macos-11.0.1 + +# Uncomment for easier debugging +# set tags master + +function bfn + echo "--blocklist-function=$argv[1]" +end + +function bty + echo "--blocklist-type=$argv[1]" +end + +function blocklisted + # Added in 10.8 + bfn objc_terminate + + # Added in 10.10 + bfn object_isClass + + # Removed in 10.11 + bfn objc_setFutureClass + + # Added in 10.12 + bfn object_setIvarWithStrongDefault + bfn object_setInstanceVariableWithStrongDefault + bfn protocol_copyPropertyList2 + + # Not present between 10.8 and 10.12, and declared OBJC_UNAVAILABLE + bty objc_objectptr_t + bfn objc_retainedObject + bfn objc_unretainedObject + bfn objc_unretainedPointer + + # Removed in 10.13 + bfn objc_sync_wait + bfn objc_sync_notify + bfn objc_sync_notifyAll + + # Added in 10.14 + bty objc_hook_getImageName + bfn objc_setHook_getImageName + + # Added in 10.14.4 + bty objc_hook_getClass + bfn objc_setHook_getClass + bfn _objc_realizeClassFromSwift + + # Added in 10.15, removed in 11.0.1 + bty objc_hook_setAssociatedObject + bfn objc_setHook_setAssociatedObject + + # Added in 10.15 + bty mach_header + bty objc_func_loadImage + bfn objc_addLoadImageFunc + + # Added in 11.0.1 + bty objc_hook_lazyClassNamer + bfn objc_setHook_lazyClassNamer +end + +# Marked with __[PLATFORM]_DEPRECATED +function deprecated + bfn class_setSuperclass # not recommended + bfn class_lookupMethod # use class_getMethodImplementation instead + bfn class_respondsToMethod # use class_respondsToSelector instead + bfn _objc_flush_caches # not recommended + + # Marked with OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE + bfn object_copyFromZone # use object_copy instead + bfn class_createInstanceFromZone # use class_createInstance instead +end + +# Marked with OBJC_OSX_AVAILABLE_OTHERS_UNAVAILABLE +function macos_only + bty objc_exception_handler + bfn objc_addExceptionHandler + bfn objc_removeExceptionHandler +end + +# Protected by #if !(TARGET_OS_OSX && __i386__) block +function 64bit_only + bfn objc_setHook_getClass + bfn objc_setHook_lazyClassNamer + bfn _objc_swiftMetadataInitializer + bfn _objc_realizeClassFromSwift +end + +# Marked with OBJC2_UNAVAILABLE +function unavailable + # message.h + bty marg_list + bfn objc_msgSendv + bfn objc_msgSendv_stret + bfn objc_msgSendv_fpret + + # runtime.h + bfn object_realloc + bfn object_reallocFromZone + bfn objc_getClasses + bfn objc_addClass + bfn objc_setClassHandler + bfn objc_setMultithreaded + bfn class_addMethods + bfn class_removeMethods + bfn _objc_resolve_categories_for_class + bfn class_poseAs + bfn method_getSizeOfArguments + bfn method_getArgumentInfo + bfn objc_getOrigClass + bfn class_nextMethodList + + bty objc_method_list + + bfn _alloc + bfn _copy + bfn _realloc + bfn _dealloc + bfn _zoneAlloc + bfn _zoneRealloc + bfn _zoneCopy + bfn _error + + # Marked with OBJC_OSX_DEPRECATED_OTHERS_UNAVAILABLE + bfn object_copyFromZone # use object_copy instead + bfn class_createInstanceFromZone # use class_createInstance instead +end + +# Marked with OBJC_ARC_UNAVAILABLE +function arc_unavailable + bfn object_copy + bfn object_dispose + bfn object_setInstanceVariable + bfn object_setInstanceVariableWithStrongDefault + bfn object_getInstanceVariable + bfn objc_getFutureClass + bfn objc_constructInstance + bfn objc_destructInstance +end + +function custom + echo "--raw-line=use crate::*;" + + # Defined in types.rs + bty BOOL + bty objc_class + bty objc_ivar + bty objc_method + bty objc_object + bty objc_property + bty objc_selector + + # Defined in message.rs + bfn class_getMethodImplementation + bfn class_getMethodImplementation_stret + + # Not used anywhere + bty objc_category +end + + +for tag in $tags + git -C $source_repo checkout $tag + + bindgen $source_repo/runtime/headers.h -o $to_repo/bindings.rs \ + --no-layout-tests \ + --no-doc-comments \ + --size_t-is-usize \ + --allowlist-function="(sel|object|class|objc|method|protocol|ivar|property|imp|_objc|_)_.*" \ + --blocklist-type=__darwin_size_t \ + --generate="functions" \ + (blocklisted) \ + (deprecated) \ + (macos_only) \ + (64bit_only) \ + (unavailable) \ + (arc_unavailable) \ + (custom) + + # --allowlist-var=".*OBJC.*" \ + + git -C $to_repo add bindings.rs + git -C $to_repo commit -a -m "From $tag" +end + +rm $source_repo/runtime/headers.h diff --git a/objc_sys/helper-scripts/gnustep-gen.fish b/objc_sys/helper-scripts/gnustep-gen.fish new file mode 100755 index 000000000..9087135f9 --- /dev/null +++ b/objc_sys/helper-scripts/gnustep-gen.fish @@ -0,0 +1,22 @@ +#!/usr/local/bin/fish + +# Yup, this is terrible + +# Source repo should be a path to https://github.com/gnustep/libobjc2.git +set source_repo $argv[1] +set out_file $argv[2] + +cp gnustep-headers.h $source_repo/objc/headers.h + +bindgen $source_repo/objc/headers.h \ + -o $out_file \ + --no-layout-tests \ + --no-doc-comments \ + --size_t-is-usize \ + --allowlist-function="(sel|object|class|objc|method|_?protocol|ivar|property|imp|_objc|)_.*" \ + --allowlist-var=".*OBJC.*" \ + --allowlist-var=".*objc.*" \ + # GNUStep-specific functions + --blocklist-function=".*_np" + +rm $source_repo/objc/headers.h diff --git a/objc_sys/helper-scripts/gnustep-headers.h b/objc_sys/helper-scripts/gnustep-headers.h new file mode 100644 index 000000000..6e2e59b1b --- /dev/null +++ b/objc_sys/helper-scripts/gnustep-headers.h @@ -0,0 +1,7 @@ +// Required to come before the below headers +#include "runtime.h" + +#include "capabilities.h" +#include "objc-arc.h" +#include "hooks.h" +#include "encoding.h" diff --git a/objc_sys/helper-scripts/headers.h b/objc_sys/helper-scripts/headers.h new file mode 100644 index 000000000..751790650 --- /dev/null +++ b/objc_sys/helper-scripts/headers.h @@ -0,0 +1,33 @@ +// Generate bindings for Objective-C version 2 +#define __OBJC2__ 1 + +// Make the objc_msgSend family not variadic +// +// See https://developer.apple.com/documentation/objectivec/objc_old_dispatch_prototypes?language=objc +#define OBJC_OLD_DISPATCH_PROTOTYPES 0 + + +// Public headers +// The order of these is important + +#include "objc-api.h" +#include "objc.h" +#include "runtime.h" +#include "objc-exception.h" +#include "objc-sync.h" + +// All of these are custom-defined in message.rs because of complex cfgs +// #include "message.h" + +// Not used, contains old GC stuff +// Could also be removed with #define OBJC_NO_GC_API 1 +// #include "objc-auto.h" + + +// Private headers + +// Contains debug objc_msgSend + some Property stuff +// #include "objc-abi.h" + +// Contains internals + ARC +// #include "objc-internal.h" diff --git a/objc_sys/src/class.rs b/objc_sys/src/class.rs new file mode 100644 index 000000000..3c8dfe84a --- /dev/null +++ b/objc_sys/src/class.rs @@ -0,0 +1,137 @@ +use std::os::raw::{c_char, c_int, c_uint}; + +use crate::{ + objc_ivar, objc_method, objc_object, objc_property, objc_property_attribute_t, objc_protocol, + objc_selector, OpaqueData, BOOL, IMP, +}; + +/// An opaque type that represents an Objective-C class. +#[repr(C)] +pub struct objc_class { + // `isa` field is deprecated and not available on GNUStep, so we don't + // expose it here. Use `class_getSuperclass` instead. + _priv: [u8; 0], + _p: OpaqueData, +} + +/// This is `c_char` in GNUStep's libobjc2 and `uint8_t` in Apple's objc4. +/// +/// The pointer represents opaque data, and is definitely not just an integer, +/// so its signedness (i8 vs. u8) is not applicable. +/// +/// So we just assign it here as a private alias to u8, to not document the +/// difference. +type ivar_layout_type = u8; + +extern "C" { + pub fn objc_getClass(name: *const c_char) -> *const objc_class; + pub fn objc_getRequiredClass(name: *const c_char) -> *const objc_class; + pub fn objc_lookUpClass(name: *const c_char) -> *const objc_class; + pub fn objc_getMetaClass(name: *const c_char) -> *const objc_class; + pub fn objc_copyClassList(out_len: *mut c_uint) -> *mut *const objc_class; + pub fn objc_getClassList(buffer: *mut *const objc_class, buffer_len: c_int) -> c_int; + + pub fn objc_allocateClassPair( + superclass: *const objc_class, + name: *const c_char, + extra_bytes: usize, + ) -> *mut objc_class; + #[cfg(apple)] + pub fn objc_duplicateClass( + original: *const objc_class, + name: *const c_char, + extra_bytes: usize, + ) -> *mut objc_class; + pub fn objc_disposeClassPair(cls: *mut objc_class); + pub fn objc_registerClassPair(cls: *mut objc_class); + + pub fn class_addIvar( + cls: *mut objc_class, + name: *const c_char, + size: usize, + alignment: u8, + types: *const c_char, + ) -> BOOL; + pub fn class_addMethod( + cls: *mut objc_class, + name: *const objc_selector, + imp: IMP, + types: *const c_char, + ) -> BOOL; + pub fn class_addProperty( + cls: *mut objc_class, + name: *const c_char, + attributes: *const objc_property_attribute_t, + attributes_count: c_uint, + ) -> BOOL; + pub fn class_addProtocol(cls: *mut objc_class, protocol: *const objc_protocol) -> BOOL; + pub fn class_conformsToProtocol(cls: *const objc_class, protocol: *const objc_protocol) + -> BOOL; + pub fn class_copyIvarList( + cls: *const objc_class, + out_len: *mut c_uint, + ) -> *mut *const objc_ivar; + pub fn class_copyMethodList( + cls: *const objc_class, + out_len: *mut c_uint, + ) -> *mut *const objc_method; + pub fn class_copyPropertyList( + cls: *const objc_class, + out_len: *mut c_uint, + ) -> *mut *const objc_property; + pub fn class_copyProtocolList( + cls: *const objc_class, + out_len: *mut c_uint, + ) -> *mut *const objc_protocol; + + pub fn class_createInstance(cls: *const objc_class, extra_bytes: usize) -> *mut objc_object; + pub fn class_getClassMethod( + cls: *const objc_class, + name: *const objc_selector, + ) -> *const objc_method; + pub fn class_getClassVariable(cls: *const objc_class, name: *const c_char) -> *const objc_ivar; + pub fn class_getImageName(cls: *const objc_class) -> *const c_char; + pub fn class_getInstanceMethod( + cls: *const objc_class, + name: *const objc_selector, + ) -> *const objc_method; + pub fn class_getInstanceSize(cls: *const objc_class) -> usize; + pub fn class_getInstanceVariable( + cls: *const objc_class, + name: *const c_char, + ) -> *const objc_ivar; + pub fn class_getIvarLayout(cls: *const objc_class) -> *const ivar_layout_type; + pub fn class_getName(cls: *const objc_class) -> *const c_char; + pub fn class_getProperty(cls: *const objc_class, name: *const c_char) -> *const objc_property; + pub fn class_getSuperclass(cls: *const objc_class) -> *const objc_class; + pub fn class_getVersion(cls: *const objc_class) -> c_int; + #[cfg(apple)] + pub fn class_getWeakIvarLayout(cls: *const objc_class) -> *const ivar_layout_type; + pub fn class_isMetaClass(cls: *const objc_class) -> BOOL; + pub fn class_replaceMethod( + cls: *mut objc_class, + name: *const objc_selector, + imp: IMP, + types: *const c_char, + ) -> IMP; + pub fn class_replaceProperty( + cls: *mut objc_class, + name: *const c_char, + attributes: *const objc_property_attribute_t, + attributes_len: c_uint, + ); + pub fn class_respondsToSelector(cls: *const objc_class, sel: *const objc_selector) -> BOOL; + pub fn class_setIvarLayout(cls: *mut objc_class, layout: *const ivar_layout_type); + pub fn class_setVersion(cls: *mut objc_class, version: c_int); + #[cfg(apple)] + pub fn class_setWeakIvarLayout(cls: *mut objc_class, layout: *const ivar_layout_type); + + // #[deprecated = "not recommended"] + // pub fn class_setSuperclass + // #[deprecated = "use class_getMethodImplementation instead"] + // #[cfg(apple)] + // pub fn class_lookupMethod + // #[deprecated = "use class_respondsToSelector instead"] + // #[cfg(apple)] + // pub fn class_respondsToMethod +} diff --git a/objc_sys/src/constants.rs b/objc_sys/src/constants.rs new file mode 100644 index 000000000..3cfed1220 --- /dev/null +++ b/objc_sys/src/constants.rs @@ -0,0 +1,44 @@ +//! Various common #defines and enum constants. + +#[cfg(apple)] +use std::os::raw::c_int; + +use crate::{id, Class, BOOL}; + +#[cfg(not(target_arch = "aarch64"))] +/// The equivalent of `true` for Objective-C's [`BOOL`][`super::BOOL`] type. +pub const YES: BOOL = 1; +#[cfg(target_arch = "aarch64")] +/// The equivalent of `true` for Objective-C's [`BOOL`][`super::BOOL`] type. +pub const YES: BOOL = true; + +#[cfg(not(target_arch = "aarch64"))] +/// The equivalent of `false` for Objective-C's [`BOOL`][`super::BOOL`] type. +pub const NO: BOOL = 0; +#[cfg(target_arch = "aarch64")] +/// The equivalent of `false` for Objective-C's [`BOOL`][`super::BOOL`] type. +pub const NO: BOOL = false; + +/// A quick alias for a [`null_mut`][`core::ptr::null_mut`] object / instance. +pub const nil: id = 0 as *mut _; + +/// A quick alias for a [`null_mut`][`core::ptr::null_mut`] class. +pub const Nil: Class = 0 as *mut _; + +pub type objc_AssociationPolicy = usize; +pub const OBJC_ASSOCIATION_ASSIGN: objc_AssociationPolicy = 0; +pub const OBJC_ASSOCIATION_RETAIN_NONATOMIC: objc_AssociationPolicy = 1; +pub const OBJC_ASSOCIATION_COPY_NONATOMIC: objc_AssociationPolicy = 3; +pub const OBJC_ASSOCIATION_RETAIN: objc_AssociationPolicy = 769; +pub const OBJC_ASSOCIATION_COPY: objc_AssociationPolicy = 771; + +#[cfg(apple)] +pub const OBJC_SYNC_SUCCESS: c_int = 0; +#[cfg(apple)] +pub const OBJC_SYNC_NOT_OWNING_THREAD_ERROR: c_int = -1; +/// Only relevant before macOS 10.13 +#[cfg(apple)] +pub const OBJC_SYNC_TIMED_OUT: c_int = -2; +/// Only relevant before macOS 10.13 +#[cfg(apple)] +pub const OBJC_SYNC_NOT_INITIALIZED: c_int = -3; diff --git a/objc_sys/src/exception.rs b/objc_sys/src/exception.rs new file mode 100644 index 000000000..49f9b7379 --- /dev/null +++ b/objc_sys/src/exception.rs @@ -0,0 +1,63 @@ +//! Defined in: +//! Apple: `objc-exception.h` +//! GNUStep: `eh_personality.c`, which is a bit brittle to rely on, but I +//! think it's fine... +use core::ffi::c_void; +#[cfg(any(apple, gnustep))] +use std::os::raw::c_int; + +#[cfg(apple)] +use crate::objc_class; +use crate::objc_object; + +/// Remember that this is non-null! +#[cfg(apple)] +pub type objc_exception_matcher = + unsafe extern "C" fn(catch_type: *mut objc_class, exception: *mut objc_object) -> c_int; + +/// Remember that this is non-null! +#[cfg(apple)] +pub type objc_exception_preprocessor = + unsafe extern "C" fn(exception: *mut objc_object) -> *mut objc_object; + +/// Remember that this is non-null! +#[cfg(apple)] +pub type objc_uncaught_exception_handler = unsafe extern "C" fn(exception: *mut objc_object); + +/// Only available on macOS. +/// +/// Remember that this is non-null! +#[cfg(all(apple, target_os = "macos"))] +pub type objc_exception_handler = + unsafe extern "C" fn(unused: *mut objc_object, context: *mut c_void); + +extern "C" { + pub fn objc_begin_catch(exc_buf: *mut c_void) -> *mut objc_object; + pub fn objc_end_catch(); + pub fn objc_exception_throw(exception: *mut objc_object) -> !; + #[cfg(apple)] + pub fn objc_exception_rethrow() -> !; + #[cfg(gnustep)] + pub fn objc_exception_rethrow(exc_buf: *mut c_void) -> !; + + #[cfg(apple)] + pub fn objc_setExceptionMatcher(f: objc_exception_matcher) -> objc_exception_matcher; + #[cfg(apple)] + pub fn objc_setExceptionPreprocessor( + f: objc_exception_preprocessor, + ) -> objc_exception_preprocessor; + #[cfg(apple)] + pub fn objc_setUncaughtExceptionHandler( + f: objc_uncaught_exception_handler, + ) -> objc_uncaught_exception_handler; + + /// Only available on macOS. + #[cfg(all(apple, target_os = "macos"))] + pub fn objc_addExceptionHandler(f: objc_exception_handler, context: *mut c_void) -> usize; + /// Only available on macOS. + #[cfg(all(apple, target_os = "macos"))] + pub fn objc_removeExceptionHandler(token: usize); + + #[cfg(gnustep)] + pub fn objc_set_apple_compatible_objcxx_exceptions(newValue: c_int) -> c_int; +} diff --git a/objc_sys/src/lib.rs b/objc_sys/src/lib.rs new file mode 100644 index 000000000..406496e3b --- /dev/null +++ b/objc_sys/src/lib.rs @@ -0,0 +1,63 @@ +//! # Raw bindings to Objective-C runtimes +//! +//! These bindings contain almost no documentation, so it is highly +//! recommended to read the documentation of the original libraries: +//! - Apple's [official documentation][apple]. +//! - Apple's `objc4` [source code][objc4] ([`git` mirror][objc4-mirror]), in +//! particular `runtime.h`. +//! - GNUStep's `libobjc2` [source code][libobjc2], in particular `runtime.h`. +//! +//! [apple]: https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc +//! [libobjc2]: https://github.com/gnustep/libobjc2/tree/v2.1/objc +//! [objc4]: https://opensource.apple.com/source/objc4/objc4-818.2/runtime/ +//! [objc4-mirror]: https://github.com/madsmtm/objc4-mirror.git + +#![no_std] +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![doc(html_root_url = "https://docs.rs/objc_sys/0.0.0")] + +// TODO: Replace `extern "C"` with `extern "C-unwind"` where applicable. +// See https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html. + +// TODO: Remove this and add "no-std" category to Cargo.toml +// Requires a better solution for C-types in `no_std` crates. +// See https://github.com/japaric/cty/issues/14. +extern crate std; + +use core::cell::UnsafeCell; +use core::marker::{PhantomData, PhantomPinned}; + +mod class; +mod constants; +mod exception; +mod message; +mod method; +mod object; +mod property; +mod protocol; +mod rc; +mod selector; +mod types; +mod various; + +pub use class::*; +pub use constants::*; +pub use exception::*; +pub use message::*; +pub use method::*; +pub use object::*; +pub use property::*; +pub use protocol::*; +pub use rc::*; +pub use selector::*; +pub use types::*; +pub use various::*; + +/// We don't know much about the actual structs, so better mark them `!Send`, +/// `!Sync`, `!Unpin` and as mutable behind shared references. Downstream +/// libraries can always manually opt in to these types afterwards. (It's +/// also less of a breaking change on our part if we re-add these later). +/// +/// TODO: Replace this with `extern type` to also mark it as unsized. +type OpaqueData = PhantomData<(UnsafeCell<*const ()>, PhantomPinned)>; diff --git a/objc_sys/src/message.rs b/objc_sys/src/message.rs new file mode 100644 index 000000000..e21b5f53d --- /dev/null +++ b/objc_sys/src/message.rs @@ -0,0 +1,85 @@ +//! The `objc_msgSend` familiy of functions. +//! +//! Most of these are `cfg`-gated, these configs are semver-stable. +//! +//! TODO: Some of these are only supported on _some_ GNUStep targets! +use crate::{objc_class, objc_object}; +#[cfg(gnustep)] +use crate::{objc_selector, IMP}; + +/// Specifies data used when sending messages to superclasses. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +// TODO: Does this belong in this file or in types.rs? +pub struct objc_super { + /// The object / instance to send a message to. + pub receiver: *mut objc_object, + /// The particular superclass of the instance to message. + /// + /// Named `class` in older Objective-C versions. + pub super_class: *const objc_class, +} + +#[cfg(gnustep)] +extern "C" { + pub fn objc_msg_lookup(receiver: *mut objc_object, sel: *const objc_selector) -> IMP; + pub fn objc_msg_lookup_super(sup: *const objc_super, sel: *const objc_selector) -> IMP; + // objc_msg_lookup_sender + + // objc_msgLookup family available in macOS >= 10.12 +} + +extern "C" { + // objc_msgSend_noarg + + pub fn objc_msgSend(); + // objc_msgSend_debug + + #[cfg(apple)] + pub fn objc_msgSendSuper(); + // objc_msgSendSuper2 + // objc_msgSendSuper2_debug + + #[cfg(apple)] + pub fn method_invoke(); + #[cfg(apple)] + pub fn _objc_msgForward(); + pub fn class_getMethodImplementation(); +} + +#[cfg(not(target_arch = "aarch64"))] // __arm64__ +extern "C" { + /// Not available on `target_arch = "aarch64"` + pub fn objc_msgSend_stret(); + // objc_msgSend_stret_debug + + /// Not available on `target_arch = "aarch64"` + #[cfg(apple)] + pub fn objc_msgSendSuper_stret(); + // objc_msgSendSuper2_stret + // objc_msgSendSuper2_stret_debug + + /// Not available on `target_arch = "aarch64"` + #[cfg(apple)] + pub fn method_invoke_stret(); + /// Not available on `target_arch = "aarch64"` + #[cfg(apple)] + pub fn _objc_msgForward_stret(); + /// Not available on `target_arch = "aarch64"` + pub fn class_getMethodImplementation_stret(); +} + +#[cfg(any(target_arch = "x86_64", target_arch = "x86"))] // __x86_64__ and __i386__ +extern "C" { + /// Only available on `target_arch = "x86_64"` or `target_arch = "x86"` + pub fn objc_msgSend_fpret(); + // objc_msgSend_fpret_debug +} + +#[cfg(target_arch = "x86_64")] // __x86_64__ +extern "C" { + /// Only available on `target_arch = "x86_64"` + #[cfg(apple)] + pub fn objc_msgSend_fp2ret(); + // objc_msgSend_fp2ret_debug +} diff --git a/objc_sys/src/method.rs b/objc_sys/src/method.rs new file mode 100644 index 000000000..af1612506 --- /dev/null +++ b/objc_sys/src/method.rs @@ -0,0 +1,40 @@ +use std::os::raw::{c_char, c_uint}; + +use crate::{objc_selector, OpaqueData, IMP}; + +/// A type that represents a method in a class definition. +#[repr(C)] +pub struct objc_method { + _priv: [u8; 0], + _p: OpaqueData, +} + +/// Describes an Objective-C method. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct objc_method_description { + /// The name of the method. + pub name: *const objc_selector, + /// The types of the method arguments. + pub types: *const c_char, +} + +extern "C" { + pub fn method_copyArgumentType(method: *const objc_method, index: c_uint) -> *mut c_char; + pub fn method_copyReturnType(method: *const objc_method) -> *mut c_char; + pub fn method_exchangeImplementations(method1: *mut objc_method, method2: *mut objc_method); + pub fn method_getArgumentType( + method: *const objc_method, + index: c_uint, + dst: *mut c_char, + dst_len: usize, + ); + #[cfg(apple)] + pub fn method_getDescription(m: *const objc_method) -> *const objc_method_description; + pub fn method_getImplementation(method: *const objc_method) -> IMP; + pub fn method_getName(method: *const objc_method) -> *const objc_selector; + pub fn method_getNumberOfArguments(method: *const objc_method) -> c_uint; + pub fn method_getReturnType(method: *const objc_method, dst: *mut c_char, dst_len: usize); + pub fn method_getTypeEncoding(method: *const objc_method) -> *const c_char; + pub fn method_setImplementation(method: *const objc_method, imp: IMP) -> IMP; +} diff --git a/objc_sys/src/object.rs b/objc_sys/src/object.rs new file mode 100644 index 000000000..491e90538 --- /dev/null +++ b/objc_sys/src/object.rs @@ -0,0 +1,89 @@ +use core::ffi::c_void; +use std::os::raw::c_char; + +use crate::{objc_class, objc_ivar, OpaqueData}; + +/// An opaque type that represents an object / an instance of a class. +#[repr(C)] +pub struct objc_object { + // `isa` field is deprecated, so we don't expose it here. + // Use `object_getClass` instead. + _priv: [u8; 0], + _p: OpaqueData, +} + +extern "C" { + pub fn object_getClass(obj: *const objc_object) -> *const objc_class; + pub fn object_getClassName(obj: *const objc_object) -> *const c_char; + pub fn object_getIndexedIvars(obj: *const objc_object) -> *const c_void; + pub fn object_getIvar(obj: *const objc_object, ivar: *const objc_ivar) -> *const objc_object; + + pub fn object_setClass(obj: *mut objc_object, cls: *const objc_class) -> *const objc_class; + pub fn object_setIvar(obj: *mut objc_object, ivar: *const objc_ivar, value: *mut objc_object); + + #[deprecated = "Not needed since ARC"] + #[cfg(apple)] + pub fn object_copy(obj: *const objc_object, size: usize) -> *mut objc_object; + #[deprecated = "Not needed since ARC"] + pub fn object_dispose(obj: *mut objc_object) -> *mut objc_object; + #[deprecated = "Not needed since ARC"] + pub fn object_setInstanceVariable( + obj: *mut objc_object, + name: *const c_char, + value: *mut c_void, + ) -> *const objc_ivar; + #[deprecated = "Not needed since ARC"] + #[cfg(apple)] + pub fn object_setInstanceVariableWithStrongDefault( + obj: *mut objc_object, + name: *const c_char, + value: *mut c_void, + ) -> *const objc_ivar; + #[deprecated = "Not needed since ARC"] + pub fn object_getInstanceVariable( + obj: *const objc_object, + name: *const c_char, + out_value: *mut *const c_void, + ) -> *const objc_ivar; + #[deprecated = "Not needed since ARC"] + #[cfg(apple)] + pub fn objc_getFutureClass(name: *const c_char) -> *const objc_class; + #[deprecated = "Not needed since ARC"] + #[cfg(apple)] + pub fn objc_constructInstance(cls: *const objc_class, bytes: *mut c_void) -> *mut objc_object; + #[deprecated = "Not needed since ARC"] + #[cfg(apple)] + pub fn objc_destructInstance(obj: *mut objc_object) -> *mut c_void; + + // TODO: Unsure if we should expose these; are they useful, and stable? + // Defined in objc-abi.h + // pub fn objc_getProperty( + // obj: *const objc_object, + // sel: *const objc_selector, + // offset: isize, + // atomic: BOOL, + // ) -> *mut c_void; + // pub fn objc_setProperty( + // obj: *const objc_object, + // sel: *const objc_selector, + // offset: isize, + // newValue: *const c_void, + // atomic: BOOL, + // shouldCopy: i8, + // ); + // This is generated in setters to struct properties. + // pub fn objc_copyStruct( + // dest: *mut c_void, + // src: *const c_void, + // size: isize, + // atomic: BOOL, + // hasStrong: BOOL, + // ); + + // #[deprecated = "use object_copy instead"] + // #[cfg(all(apple, target_os = "macos"))] + // object_copyFromZone + // #[deprecated = "use class_createInstance instead"] + // #[cfg(all(apple, target_os = "macos"))] + // class_createInstanceFromZone +} diff --git a/objc_sys/src/property.rs b/objc_sys/src/property.rs new file mode 100644 index 000000000..950e2e479 --- /dev/null +++ b/objc_sys/src/property.rs @@ -0,0 +1,35 @@ +use std::os::raw::{c_char, c_uint}; + +use crate::OpaqueData; + +/// An opaque type that describes a property in a class. +#[repr(C)] +pub struct objc_property { + _priv: [u8; 0], + _p: OpaqueData, +} + +/// Describes an Objective-C property attribute. +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct objc_property_attribute_t { + /// The name of the attribute. + pub name: *const c_char, + /// The value of the attribute + /// + /// Usually NULL. + pub value: *const c_char, +} + +extern "C" { + pub fn property_copyAttributeList( + property: *const objc_property, + out_len: *mut c_uint, + ) -> *mut objc_property_attribute_t; + pub fn property_copyAttributeValue( + property: *const objc_property, + attribute_name: *const c_char, + ) -> *mut c_char; + pub fn property_getAttributes(property: *const objc_property) -> *const c_char; + pub fn property_getName(property: *const objc_property) -> *const c_char; +} diff --git a/objc_sys/src/protocol.rs b/objc_sys/src/protocol.rs new file mode 100644 index 000000000..bc957b679 --- /dev/null +++ b/objc_sys/src/protocol.rs @@ -0,0 +1,82 @@ +use std::os::raw::{c_char, c_uint}; + +use crate::{ + objc_method_description, objc_property, objc_property_attribute_t, objc_selector, OpaqueData, + BOOL, +}; + +/// Opaque type for Objective-C protocols. +/// +/// Note that, although protocols are objects, sending messages to them is +/// deprecated and may not work in the future. +/// +/// The naming of this follows GNUStep; this does not exist in Apple's +/// original, there `Protocol` is just a type alias of `objc_object`. +#[repr(C)] +pub struct objc_protocol { + _priv: [u8; 0], + _p: OpaqueData, +} + +extern "C" { + pub fn objc_getProtocol(name: *const c_char) -> *const objc_protocol; + pub fn objc_copyProtocolList(out_len: *mut c_uint) -> *mut *const objc_protocol; + + pub fn objc_allocateProtocol(name: *const c_char) -> *mut objc_protocol; + pub fn objc_registerProtocol(proto: *mut objc_protocol); + + pub fn protocol_addMethodDescription( + proto: *mut objc_protocol, + name: *const objc_selector, + types: *const c_char, + is_required_method: BOOL, + is_instance_method: BOOL, + ); + pub fn protocol_addProperty( + proto: *mut objc_protocol, + name: *const c_char, + attributes: *const objc_property_attribute_t, + attributes_len: c_uint, + is_required_property: BOOL, + is_instance_property: BOOL, + ); + pub fn protocol_addProtocol(proto: *mut objc_protocol, addition: *const objc_protocol); + pub fn protocol_conformsToProtocol( + proto: *const objc_protocol, + other: *const objc_protocol, + ) -> BOOL; + pub fn protocol_copyMethodDescriptionList( + proto: *const objc_protocol, + is_required_method: BOOL, + is_instance_method: BOOL, + out_len: *mut c_uint, + ) -> *mut objc_method_description; + pub fn protocol_copyPropertyList( + proto: *const objc_protocol, + out_len: *mut c_uint, + ) -> *mut *const objc_property; + pub fn protocol_copyProtocolList( + proto: *const objc_protocol, + out_len: *mut c_uint, + ) -> *mut *const objc_protocol; + pub fn protocol_getMethodDescription( + proto: *const objc_protocol, + sel: *const objc_selector, + is_required_method: BOOL, + is_instance_method: BOOL, + ) -> objc_method_description; + pub fn protocol_getName(proto: *const objc_protocol) -> *const c_char; + pub fn protocol_getProperty( + proto: *const objc_protocol, + name: *const c_char, + is_required_property: BOOL, + is_instance_property: BOOL, + ) -> *const objc_property; + pub fn protocol_isEqual(proto: *const objc_protocol, other: *const objc_protocol) -> BOOL; + + // #[cfg(macos >= 10.12)] + // protocol_copyPropertyList2 + + // #[cfg(gnustep)] + // _protocol_getMethodTypeEncoding +} diff --git a/objc_sys/src/rc.rs b/objc_sys/src/rc.rs new file mode 100644 index 000000000..31b38b1f6 --- /dev/null +++ b/objc_sys/src/rc.rs @@ -0,0 +1,53 @@ +//! ARC functions. +//! +//! All available since macOS `10.7`. +//! +//! Defined in `objc-internal.h`, but is available in Clang's +//! [documentation][ARC] so these are safe to rely on. +//! +//! On GNUStep these are defined in `objc-arc.h`. +//! +//! [ARC]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime-support> +use core::ffi::c_void; + +use crate::objc_object; + +extern "C" { + // Autorelease + + pub fn objc_autorelease(value: *mut objc_object) -> *mut objc_object; + pub fn objc_autoreleasePoolPop(pool: *mut c_void); + pub fn objc_autoreleasePoolPush() -> *mut c_void; + pub fn objc_autoreleaseReturnValue(value: *mut objc_object) -> *mut objc_object; + + // Weak pointers + + pub fn objc_copyWeak(to: *mut *mut objc_object, from: *mut *mut objc_object); + pub fn objc_destroyWeak(addr: *mut *mut objc_object); + pub fn objc_initWeak(addr: *mut *mut objc_object, value: *mut objc_object) -> *mut objc_object; + // Defined in runtime.h + pub fn objc_loadWeak(addr: *mut *mut objc_object) -> *mut objc_object; + pub fn objc_loadWeakRetained(addr: *mut *mut objc_object) -> *mut objc_object; + pub fn objc_moveWeak(to: *mut *mut objc_object, from: *mut *mut objc_object); + + // Retain / release + + pub fn objc_release(value: *mut objc_object); + pub fn objc_retain(value: *mut objc_object) -> *mut objc_object; + pub fn objc_retainAutorelease(value: *mut objc_object) -> *mut objc_object; + pub fn objc_retainAutoreleaseReturnValue(value: *mut objc_object) -> *mut objc_object; + pub fn objc_retainAutoreleasedReturnValue(value: *mut objc_object) -> *mut objc_object; + // Defined in objc-abi.h + pub fn objc_retainBlock(value: *mut objc_object) -> *mut objc_object; + + // Storing values + + pub fn objc_storeStrong(addr: *mut *mut objc_object, value: *mut objc_object); + // Defined in runtime.h + pub fn objc_storeWeak(addr: *mut *mut objc_object, value: *mut objc_object) + -> *mut objc_object; + + // TODO: Decide about nonstandard extensions like these: + // #[cfg(gnustep)] + // pub fn objc_delete_weak_refs(obj: *mut objc_object) -> BOOL; +} diff --git a/objc_sys/src/selector.rs b/objc_sys/src/selector.rs new file mode 100644 index 000000000..3338eea51 --- /dev/null +++ b/objc_sys/src/selector.rs @@ -0,0 +1,21 @@ +use std::os::raw::c_char; + +use crate::{OpaqueData, BOOL}; + +/// An opaque type that represents a method selector. +/// +/// Selectors are immutable. +#[repr(C)] +pub struct objc_selector { + _priv: [u8; 0], + _p: OpaqueData, +} + +extern "C" { + pub fn sel_getName(sel: *const objc_selector) -> *const c_char; + pub fn sel_getUid(name: *const c_char) -> *const objc_selector; + pub fn sel_isEqual(lhs: *const objc_selector, rhs: *const objc_selector) -> BOOL; + #[cfg(apple)] + pub fn sel_isMapped(sel: *const objc_selector) -> BOOL; + pub fn sel_registerName(name: *const c_char) -> *const objc_selector; +} diff --git a/objc_sys/src/types.rs b/objc_sys/src/types.rs new file mode 100644 index 000000000..4047cd60c --- /dev/null +++ b/objc_sys/src/types.rs @@ -0,0 +1,61 @@ +//! Objective-C type aliases. + +use crate::{ + objc_class, objc_ivar, objc_method, objc_object, objc_property, objc_protocol, objc_selector, +}; + +#[cfg(all(apple, not(target_arch = "aarch64")))] +type BOOL_INNER = i8; + +#[cfg(all(gnustep, not(target_arch = "aarch64")))] +// TODO: Only if STRICT_APPLE_COMPATIBILITY is NOT defined. +// TODO: (__vxworks || _WIN32) becomes BOOL = c_int. +type BOOL_INNER = u8; + +#[cfg(target_arch = "aarch64")] +type BOOL_INNER = bool; + +/// The Objective-C `BOOL` type. +/// +/// The type of this varies across platforms, so to convert an it into a Rust +/// [`bool`], always compare it with [`YES`][`crate::YES`] or [`NO`][`crate::NO`]. +pub type BOOL = BOOL_INNER; + +/// An immutable pointer to a selector. +/// +/// Type alias provided for convenience. +pub type SEL = *const objc_selector; + +/// A mutable pointer to a class. +/// +/// Type alias provided for convenience. +pub type Class = *mut objc_class; + +/// A mutable pointer to an object / instance. +/// +/// Type alias provided for convenience. +pub type id = *mut objc_object; + +/// An immutable pointer to an instance variable. +/// +/// Type alias provided for convenience. +pub type Ivar = *const objc_ivar; + +/// A mutable pointer to a method. +/// +/// Type alias provided for convenience. +pub type Method = *mut objc_method; + +/// An opaque type that represents a protocol. +/// +/// This is not just a type alias of [`objc_object`], but of [`objc_protocol`] +/// instead, for better type safety. Their internal representation is the same, +/// so the functionality is just a cast away. +/// +/// Type alias provided for convenience. +pub type Protocol = objc_protocol; + +/// A mutable pointer to a property. +/// +/// Type alias provided for convenience. +pub type objc_property_t = *mut objc_property; diff --git a/objc_sys/src/various.rs b/objc_sys/src/various.rs new file mode 100644 index 000000000..0ad6ecb40 --- /dev/null +++ b/objc_sys/src/various.rs @@ -0,0 +1,103 @@ +use core::ffi::c_void; +#[cfg(apple)] +use std::os::raw::c_uint; +use std::os::raw::{c_char, c_int}; + +#[cfg(apple)] +use crate::objc_class; +use crate::{objc_AssociationPolicy, objc_object, OpaqueData, BOOL}; + +/// An opaque type that represents an instance variable. +#[repr(C)] +pub struct objc_ivar { + _priv: [u8; 0], + _p: OpaqueData, +} + +/// A nullable pointer to the start of a method implementation. +/// +/// Not all APIs are guaranteed to take NULL values; read the docs! +pub type IMP = Option; + +/// Not available on macOS x86. +/// +/// Remember that this is non-null! +#[cfg(all(apple, not(all(target_os = "macos", target_arch = "x86"))))] +pub type objc_hook_getClass = + unsafe extern "C" fn(name: *const c_char, out_cls: *mut *const objc_class) -> BOOL; + +/// Not available on macOS x86. +/// +/// Remember that this is non-null! +#[cfg(all(apple, not(all(target_os = "macos", target_arch = "x86"))))] +pub type objc_hook_lazyClassNamer = unsafe extern "C" fn(cls: *const objc_class) -> *const c_char; + +extern "C" { + pub fn imp_getBlock(imp: IMP) -> *mut objc_object; + pub fn imp_implementationWithBlock(block: *mut objc_object) -> IMP; + pub fn imp_removeBlock(imp: IMP) -> BOOL; + + pub fn ivar_getName(ivar: *const objc_ivar) -> *const c_char; + pub fn ivar_getOffset(ivar: *const objc_ivar) -> isize; + pub fn ivar_getTypeEncoding(ivar: *const objc_ivar) -> *const c_char; + + #[cfg(apple)] + pub fn objc_copyClassNamesForImage( + image: *const c_char, + out_len: *mut c_uint, + ) -> *mut *const c_char; + #[cfg(apple)] + pub fn objc_copyImageNames(out_len: *mut c_uint) -> *mut *const c_char; + + // Instead of being able to change this, it's a weak symbol on GNUStep. + #[cfg(apple)] + pub fn objc_enumerationMutation(obj: *mut objc_object); + #[cfg(apple)] + pub fn objc_setEnumerationMutationHandler( + handler: Option, + ); + + pub fn objc_getAssociatedObject( + object: *const objc_object, + key: *const c_void, + ) -> *const objc_object; + pub fn objc_setAssociatedObject( + object: *mut objc_object, + key: *const c_void, + value: *mut objc_object, + policy: objc_AssociationPolicy, + ); + pub fn objc_removeAssociatedObjects(object: *mut objc_object); + + #[cfg(apple)] + pub fn objc_setForwardHandler(fwd: *mut c_void, fwd_stret: *mut c_void); + // These two are defined in: + // - Apple: objc-sync.h + // - GNUStep: dtable.h / associate.m + pub fn objc_sync_enter(obj: *mut objc_object) -> c_int; + pub fn objc_sync_exit(obj: *mut objc_object) -> c_int; + + /// Not available on macOS x86. + /// + /// Remember that this is non-null! + #[cfg(all(apple, not(all(target_os = "macos", target_arch = "x86"))))] + pub fn objc_setHook_getClass( + new_value: objc_hook_getClass, + out_old_value: *mut objc_hook_getClass, + ); + /// Not available on macOS x86. + /// + /// Remember that this is non-null! + #[cfg(all(apple, not(all(target_os = "macos", target_arch = "x86"))))] + pub fn objc_setHook_lazyClassNamer( + new_value: objc_hook_lazyClassNamer, + out_old_value: *mut objc_hook_lazyClassNamer, + ); + + // #[deprecated = "not recommended"] + // #[cfg(apple)] + // pub fn _objc_flush_caches + + // #[cfg(gnustep)] + // objc_test_capability +}