diff --git a/objc2/Cargo.toml b/objc2/Cargo.toml index 896d7091a..9ad78c840 100644 --- a/objc2/Cargo.toml +++ b/objc2/Cargo.toml @@ -42,6 +42,7 @@ unstable_autoreleasesafe = [] [dependencies] malloc_buf = { version = "1.0", optional = true } +terminated = "1.0" objc-sys = { path = "../objc-sys", version = "=0.2.0-alpha.1" } objc2-encode = { path = "../objc2-encode", version = "=2.0.0-beta.2" } diff --git a/objc2/examples/introspection.rs b/objc2/examples/introspection.rs index 5a10b2736..403e779e7 100644 --- a/objc2/examples/introspection.rs +++ b/objc2/examples/introspection.rs @@ -1,4 +1,5 @@ use core::ptr::NonNull; +use terminated::ntstr; use objc2::rc::{Id, Owned}; use objc2::runtime::{Class, Object}; @@ -30,7 +31,7 @@ fn main() { // Access an ivar of the object // TODO: Fix this! - let isa: *const Class = unsafe { *obj.ivar("isa") }; + let isa: *const Class = unsafe { *obj.ivar(ntstr!("isa")) }; println!("NSObject isa: {:?}", isa); #[cfg(feature = "malloc")] diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index c5128d7e5..7e28b81cf 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -13,16 +13,17 @@ //! use objc2::{class, sel}; //! use objc2::declare::ClassDecl; //! use objc2::runtime::{Class, Object, Sel}; +//! use terminated::ntstr; //! //! let superclass = class!(NSObject); -//! let mut decl = ClassDecl::new("MyNumber", superclass).unwrap(); +//! let mut decl = ClassDecl::new(ntstr!("MyNumber"), superclass).unwrap(); //! //! // Add an instance variable -//! decl.add_ivar::("_number"); +//! decl.add_ivar::(ntstr!("_number")); //! //! // Add an ObjC method for getting the number //! extern "C" fn my_number_get(this: &Object, _cmd: Sel) -> u32 { -//! unsafe { *this.ivar("_number") } +//! unsafe { *this.ivar(ntstr!("_number")) } //! } //! unsafe { //! decl.add_method( @@ -40,6 +41,9 @@ use core::mem; use core::mem::ManuallyDrop; use core::ptr; use std::ffi::CString; +use std::os::raw::c_char; + +use terminated::NulTerminatedStr; use crate::runtime::{Bool, Class, Imp, Object, Protocol, Sel}; use crate::{ffi, Encode, EncodeArguments, Encoding, Message}; @@ -138,10 +142,10 @@ unsafe impl Send for ClassDecl {} unsafe impl Sync for ClassDecl {} impl ClassDecl { - fn with_superclass(name: &str, superclass: Option<&Class>) -> Option { - let name = CString::new(name).unwrap(); + fn with_superclass(name: &NulTerminatedStr, superclass: Option<&Class>) -> Option { + let name_ptr = name.as_ptr() as *const c_char; let super_ptr = superclass.map_or(ptr::null(), |c| c) as _; - let cls = unsafe { ffi::objc_allocateClassPair(super_ptr, name.as_ptr(), 0) }; + let cls = unsafe { ffi::objc_allocateClassPair(super_ptr, name_ptr, 0) }; if cls.is_null() { None } else { @@ -153,7 +157,7 @@ impl ClassDecl { /// /// Returns [`None`] if the class couldn't be allocated, or a class with /// that name already exist. - pub fn new(name: &str, superclass: &Class) -> Option { + pub fn new(name: &NulTerminatedStr, superclass: &Class) -> Option { ClassDecl::with_superclass(name, Some(superclass)) } @@ -171,7 +175,10 @@ impl ClassDecl { /// the entire `NSObject` protocol is implemented. /// Functionality it expects, like implementations of `-retain` and /// `-release` used by ARC, will not be present otherwise. - pub fn root(name: &str, intitialize_fn: extern "C" fn(&Class, Sel)) -> Option { + pub fn root( + name: &NulTerminatedStr, + intitialize_fn: extern "C" fn(&Class, Sel), + ) -> Option { let mut decl = ClassDecl::with_superclass(name, None); if let Some(ref mut decl) = decl { unsafe { @@ -261,15 +268,15 @@ impl ClassDecl { /// # Panics /// /// If the ivar wasn't successfully added. - pub fn add_ivar(&mut self, name: &str) { - let c_name = CString::new(name).unwrap(); + pub fn add_ivar(&mut self, name: &NulTerminatedStr) { + let name_ptr = name.as_ptr() as *const c_char; let encoding = CString::new(T::ENCODING.to_string()).unwrap(); let size = mem::size_of::(); let align = log2_align_of::(); let success = Bool::from_raw(unsafe { ffi::class_addIvar( self.cls as _, - c_name.as_ptr(), + name_ptr, size, align, encoding.as_ptr(), @@ -322,9 +329,9 @@ impl ProtocolDecl { /// Constructs a [`ProtocolDecl`] with the given name. /// /// 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 { ffi::objc_allocateProtocol(c_name.as_ptr()) } as *mut Protocol; + pub fn new(name: &NulTerminatedStr) -> Option { + let name_ptr = name.as_ptr() as *const c_char; + let proto = unsafe { ffi::objc_allocateProtocol(name_ptr) } as *mut Protocol; if proto.is_null() { None } else { diff --git a/objc2/src/rc/id.rs b/objc2/src/rc/id.rs index 44ecc956f..45f660ec3 100644 --- a/objc2/src/rc/id.rs +++ b/objc2/src/rc/id.rs @@ -62,8 +62,9 @@ use crate::Message; /// use objc2::msg_send; /// use objc2::runtime::{Class, Object}; /// use objc2::rc::{Id, Owned, Shared, WeakId}; +/// use terminated::ntstr; /// -/// let cls = Class::get("NSObject").unwrap(); +/// let cls = Class::get(ntstr!("NSObject")).unwrap(); /// let obj: Id = unsafe { /// Id::new(msg_send![cls, new]) /// }; diff --git a/objc2/src/runtime.rs b/objc2/src/runtime.rs index 3ce337187..3fd32c4f8 100644 --- a/objc2/src/runtime.rs +++ b/objc2/src/runtime.rs @@ -11,9 +11,11 @@ use core::ptr; use core::str; #[cfg(feature = "malloc")] use malloc_buf::Malloc; -use std::ffi::{CStr, CString}; +use std::ffi::CStr; +use std::os::raw::c_char; #[cfg(feature = "malloc")] use std::os::raw::c_uint; +use terminated::NulTerminatedStr; pub use super::bool::Bool; use crate::{ffi, Encode, Encoding, RefEncode}; @@ -96,10 +98,10 @@ pub type Imp = unsafe extern "C" fn(); 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) -> Self { - let name = CString::new(name).unwrap(); + pub fn register(name: &NulTerminatedStr) -> Self { + let name_ptr = name.as_ptr() as *const c_char; Self { - ptr: unsafe { ffi::sel_registerName(name.as_ptr()) }, + ptr: unsafe { ffi::sel_registerName(name_ptr) }, } } @@ -252,10 +254,10 @@ impl Class { /// 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 Self> { - let name = CString::new(name).unwrap(); + pub fn get(name: &NulTerminatedStr) -> Option<&'static Self> { + let name_ptr = name.as_ptr() as *const c_char; unsafe { - let cls = ffi::objc_getClass(name.as_ptr()); + let cls = ffi::objc_getClass(name_ptr); if cls.is_null() { None } else { @@ -335,10 +337,10 @@ impl Class { /// Returns the ivar for a specified instance variable of self, or /// [`None`] if self has no ivar with the given name. - pub fn instance_variable(&self, name: &str) -> Option<&Ivar> { - let name = CString::new(name).unwrap(); + pub fn instance_variable(&self, name: &NulTerminatedStr) -> Option<&Ivar> { + let name_ptr = name.as_ptr() as *const c_char; unsafe { - let ivar = ffi::class_getInstanceVariable(self.as_ptr(), name.as_ptr()); + let ivar = ffi::class_getInstanceVariable(self.as_ptr(), name_ptr); if ivar.is_null() { None } else { @@ -358,9 +360,9 @@ impl Class { } #[allow(unused)] - fn class_variable(&self, name: &str) -> Option<&Ivar> { - let name = CString::new(name).unwrap(); - let ivar = unsafe { ffi::class_getClassVariable(self.as_ptr(), name.as_ptr()) }; + fn class_variable(&self, name: &NulTerminatedStr) -> Option<&Ivar> { + let name_ptr = name.as_ptr() as *const c_char; + let ivar = unsafe { ffi::class_getClassVariable(self.as_ptr(), name_ptr) }; // SAFETY: TODO unsafe { ivar.cast::().as_ref() } } @@ -439,10 +441,10 @@ impl Protocol { /// 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> { - let name = CString::new(name).unwrap(); + pub fn get(name: &NulTerminatedStr) -> Option<&'static Protocol> { + let name_ptr = name.as_ptr() as *const c_char; unsafe { - let proto = ffi::objc_getProtocol(name.as_ptr()); + let proto = ffi::objc_getProtocol(name_ptr); if proto.is_null() { None } else { @@ -517,7 +519,7 @@ impl UnwindSafe for Protocol {} impl RefUnwindSafe for Protocol {} // Note that Unpin is not applicable. -fn ivar_offset(cls: &Class, name: &str) -> isize { +fn ivar_offset(cls: &Class, name: &NulTerminatedStr) -> isize { match cls.instance_variable(name) { Some(ivar) => { assert!(T::ENCODING.equivalent_to_str(ivar.type_encoding())); @@ -549,7 +551,7 @@ impl Object { /// The caller must ensure that the ivar is actually of type `T`. /// /// Library implementors should expose a safe interface to the ivar. - pub unsafe fn ivar(&self, name: &str) -> &T { + pub unsafe fn ivar(&self, name: &NulTerminatedStr) -> &T { let offset = ivar_offset::(self.class(), name); // `offset` is given in bytes, so we convert to `u8` let ptr = self as *const Self as *const u8; @@ -569,7 +571,7 @@ impl Object { /// The caller must ensure that the ivar is actually of type `T`. /// /// Library implementors should expose a safe interface to the ivar. - pub unsafe fn ivar_mut(&mut self, name: &str) -> &mut T { + pub unsafe fn ivar_mut(&mut self, name: &NulTerminatedStr) -> &mut T { let offset = ivar_offset::(self.class(), name); // `offset` is given in bytes, so we convert to `u8` let ptr = self as *mut Self as *mut u8; @@ -589,7 +591,7 @@ impl Object { /// The caller must ensure that the ivar is actually of type `T`. /// /// Library implementors should expose a safe interface to the ivar. - pub unsafe fn set_ivar(&mut self, name: &str, value: T) { + pub unsafe fn set_ivar(&mut self, name: &NulTerminatedStr, value: T) { // SAFETY: Invariants upheld by caller unsafe { *self.ivar_mut::(name) = value }; } @@ -635,10 +637,12 @@ mod tests { use crate::test_utils; use crate::Encode; + use terminated::ntstr; + #[test] fn test_ivar() { let cls = test_utils::custom_class(); - let ivar = cls.instance_variable("_foo").unwrap(); + let ivar = cls.instance_variable(ntstr!("_foo")).unwrap(); assert_eq!(ivar.name(), "_foo"); assert!(::ENCODING.equivalent_to_str(ivar.type_encoding())); assert!(ivar.offset() > 0); @@ -650,7 +654,7 @@ mod tests { #[test] fn test_method() { let cls = test_utils::custom_class(); - let sel = Sel::register("foo"); + let sel = Sel::register(ntstr!("foo")); let method = cls.instance_method(sel).unwrap(); assert_eq!(method.name().name(), "foo"); assert_eq!(method.arguments_count(), 2); @@ -671,7 +675,7 @@ mod tests { assert!(cls.instance_size() > 0); assert!(cls.superclass().is_none()); - assert_eq!(Class::get(cls.name()), Some(cls)); + assert_eq!(Class::get(ntstr!("CustomObject")), Some(cls)); let metaclass = cls.metaclass(); // The metaclass of a root class is a subclass of the root class @@ -733,8 +737,8 @@ mod tests { let mut obj = test_utils::custom_object(); assert_eq!(obj.class(), test_utils::custom_class()); let result: u32 = unsafe { - obj.set_ivar("_foo", 4u32); - *obj.ivar("_foo") + obj.set_ivar(ntstr!("_foo"), 4u32); + *obj.ivar(ntstr!("_foo")) }; assert_eq!(result, 4); } diff --git a/objc2/src/test_utils.rs b/objc2/src/test_utils.rs index 12f44d82a..6a0ce0960 100644 --- a/objc2/src/test_utils.rs +++ b/objc2/src/test_utils.rs @@ -1,6 +1,7 @@ use core::ops::{Deref, DerefMut}; use std::os::raw::c_char; use std::sync::Once; +use terminated::ntstr; use crate::declare::{ClassDecl, ProtocolDecl}; use crate::runtime::{Class, Object, Protocol, Sel}; @@ -75,20 +76,21 @@ pub(crate) fn custom_class() -> &'static Class { // The runtime will call this method, so it has to be implemented extern "C" fn custom_obj_class_initialize(_this: &Class, _cmd: Sel) {} - let mut decl = ClassDecl::root("CustomObject", custom_obj_class_initialize).unwrap(); + let mut decl = + ClassDecl::root(ntstr!("CustomObject"), custom_obj_class_initialize).unwrap(); let proto = custom_protocol(); decl.add_protocol(proto); - decl.add_ivar::("_foo"); + decl.add_ivar::(ntstr!("_foo")); extern "C" fn custom_obj_set_foo(this: &mut Object, _cmd: Sel, foo: u32) { unsafe { - this.set_ivar::("_foo", foo); + this.set_ivar::(ntstr!("_foo"), foo); } } extern "C" fn custom_obj_get_foo(this: &Object, _cmd: Sel) -> u32 { - unsafe { *this.ivar::("_foo") } + unsafe { *this.ivar::(ntstr!("_foo")) } } extern "C" fn custom_obj_get_struct(_this: &Object, _cmd: Sel) -> CustomStruct { @@ -106,7 +108,7 @@ pub(crate) fn custom_class() -> &'static Class { extern "C" fn custom_obj_set_bar(this: &mut Object, _cmd: Sel, bar: u32) { unsafe { - this.set_ivar::("_foo", bar); + this.set_ivar::(ntstr!("_foo"), bar); } } @@ -146,7 +148,7 @@ pub(crate) fn custom_protocol() -> &'static Protocol { static REGISTER_CUSTOM_PROTOCOL: Once = Once::new(); REGISTER_CUSTOM_PROTOCOL.call_once(|| { - let mut decl = ProtocolDecl::new("CustomProtocol").unwrap(); + let mut decl = ProtocolDecl::new(ntstr!("CustomProtocol")).unwrap(); decl.add_method_description::<(i32,), ()>(sel!(setBar:), true); decl.add_method_description::<(), *const c_char>(sel!(getName), false); @@ -155,7 +157,7 @@ pub(crate) fn custom_protocol() -> &'static Protocol { decl.register(); }); - Protocol::get("CustomProtocol").unwrap() + Protocol::get(ntstr!("CustomProtocol")).unwrap() } pub(crate) fn custom_subprotocol() -> &'static Protocol { @@ -163,7 +165,7 @@ pub(crate) fn custom_subprotocol() -> &'static Protocol { REGISTER_CUSTOM_SUBPROTOCOL.call_once(|| { let super_proto = custom_protocol(); - let mut decl = ProtocolDecl::new("CustomSubProtocol").unwrap(); + let mut decl = ProtocolDecl::new(ntstr!("CustomSubProtocol")).unwrap(); decl.add_protocol(super_proto); decl.add_method_description::<(u32,), u32>(sel!(calculateFoo:), true); @@ -171,7 +173,7 @@ pub(crate) fn custom_subprotocol() -> &'static Protocol { decl.register(); }); - Protocol::get("CustomSubProtocol").unwrap() + Protocol::get(ntstr!("CustomSubProtocol")).unwrap() } pub(crate) fn custom_object() -> CustomObject { @@ -183,7 +185,7 @@ pub(crate) fn custom_subclass() -> &'static Class { REGISTER_CUSTOM_SUBCLASS.call_once(|| { let superclass = custom_class(); - let mut decl = ClassDecl::new("CustomSubclassObject", superclass).unwrap(); + let mut decl = ClassDecl::new(ntstr!("CustomSubclassObject"), superclass).unwrap(); extern "C" fn custom_subclass_get_foo(this: &Object, _cmd: Sel) -> u32 { let foo: u32 = unsafe { msg_send![super(this, custom_class()), foo] };