Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a terminated dependency and use NulTerminatedStr for inputs #113

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions objc2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }

Expand Down
3 changes: 2 additions & 1 deletion objc2/examples/introspection.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::ptr::NonNull;
use terminated::ntstr;

use objc2::rc::{Id, Owned};
use objc2::runtime::{Class, Object};
Expand Down Expand Up @@ -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")]
Expand Down
35 changes: 21 additions & 14 deletions objc2/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u32>("_number");
//! decl.add_ivar::<u32>(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(
Expand All @@ -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};
Expand Down Expand Up @@ -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<ClassDecl> {
let name = CString::new(name).unwrap();
fn with_superclass(name: &NulTerminatedStr, superclass: Option<&Class>) -> Option<ClassDecl> {
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 {
Expand All @@ -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<ClassDecl> {
pub fn new(name: &NulTerminatedStr, superclass: &Class) -> Option<ClassDecl> {
ClassDecl::with_superclass(name, Some(superclass))
}

Expand All @@ -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<ClassDecl> {
pub fn root(
name: &NulTerminatedStr,
intitialize_fn: extern "C" fn(&Class, Sel),
) -> Option<ClassDecl> {
let mut decl = ClassDecl::with_superclass(name, None);
if let Some(ref mut decl) = decl {
unsafe {
Expand Down Expand Up @@ -261,15 +268,15 @@ impl ClassDecl {
/// # Panics
///
/// If the ivar wasn't successfully added.
pub fn add_ivar<T: Encode>(&mut self, name: &str) {
let c_name = CString::new(name).unwrap();
pub fn add_ivar<T: Encode>(&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::<T>();
let align = log2_align_of::<T>();
let success = Bool::from_raw(unsafe {
ffi::class_addIvar(
self.cls as _,
c_name.as_ptr(),
name_ptr,
size,
align,
encoding.as_ptr(),
Expand Down Expand Up @@ -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<ProtocolDecl> {
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<ProtocolDecl> {
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 {
Expand Down
3 changes: 2 additions & 1 deletion objc2/src/rc/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Object, Owned> = unsafe {
/// Id::new(msg_send![cls, new])
/// };
Expand Down
54 changes: 29 additions & 25 deletions objc2/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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) },
}
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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::<Ivar>().as_ref() }
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -517,7 +519,7 @@ impl UnwindSafe for Protocol {}
impl RefUnwindSafe for Protocol {}
// Note that Unpin is not applicable.

fn ivar_offset<T: Encode>(cls: &Class, name: &str) -> isize {
fn ivar_offset<T: Encode>(cls: &Class, name: &NulTerminatedStr) -> isize {
match cls.instance_variable(name) {
Some(ivar) => {
assert!(T::ENCODING.equivalent_to_str(ivar.type_encoding()));
Expand Down Expand Up @@ -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<T: Encode>(&self, name: &str) -> &T {
pub unsafe fn ivar<T: Encode>(&self, name: &NulTerminatedStr) -> &T {
let offset = ivar_offset::<T>(self.class(), name);
// `offset` is given in bytes, so we convert to `u8`
let ptr = self as *const Self as *const u8;
Expand All @@ -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<T: Encode>(&mut self, name: &str) -> &mut T {
pub unsafe fn ivar_mut<T: Encode>(&mut self, name: &NulTerminatedStr) -> &mut T {
let offset = ivar_offset::<T>(self.class(), name);
// `offset` is given in bytes, so we convert to `u8`
let ptr = self as *mut Self as *mut u8;
Expand All @@ -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<T: Encode>(&mut self, name: &str, value: T) {
pub unsafe fn set_ivar<T: Encode>(&mut self, name: &NulTerminatedStr, value: T) {
// SAFETY: Invariants upheld by caller
unsafe { *self.ivar_mut::<T>(name) = value };
}
Expand Down Expand Up @@ -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!(<u32>::ENCODING.equivalent_to_str(ivar.type_encoding()));
assert!(ivar.offset() > 0);
Expand All @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
22 changes: 12 additions & 10 deletions objc2/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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::<u32>("_foo");
decl.add_ivar::<u32>(ntstr!("_foo"));

extern "C" fn custom_obj_set_foo(this: &mut Object, _cmd: Sel, foo: u32) {
unsafe {
this.set_ivar::<u32>("_foo", foo);
this.set_ivar::<u32>(ntstr!("_foo"), foo);
}
}

extern "C" fn custom_obj_get_foo(this: &Object, _cmd: Sel) -> u32 {
unsafe { *this.ivar::<u32>("_foo") }
unsafe { *this.ivar::<u32>(ntstr!("_foo")) }
}

extern "C" fn custom_obj_get_struct(_this: &Object, _cmd: Sel) -> CustomStruct {
Expand All @@ -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::<u32>("_foo", bar);
this.set_ivar::<u32>(ntstr!("_foo"), bar);
}
}

Expand Down Expand Up @@ -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);
Expand All @@ -155,23 +157,23 @@ 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 {
static REGISTER_CUSTOM_SUBPROTOCOL: Once = Once::new();

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);

decl.register();
});

Protocol::get("CustomSubProtocol").unwrap()
Protocol::get(ntstr!("CustomSubProtocol")).unwrap()
}

pub(crate) fn custom_object() -> CustomObject {
Expand All @@ -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] };
Expand Down