Skip to content

Commit

Permalink
Replace Encoding with the objc_encode crate
Browse files Browse the repository at this point in the history
  • Loading branch information
SSheldon committed Apr 7, 2019
1 parent 33f1daa commit 6092caa
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 274 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ verify_message = []

[dependencies]
malloc_buf = "1.0"
objc-encode = "1.0"

[dependencies.objc_exception]
version = "0.1"
Expand Down
2 changes: 1 addition & 1 deletion examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn main() {
let hash_method = cls.instance_method(hash_sel).unwrap();
let hash_return = hash_method.return_type();
println!("-[NSObject hash] return type: {:?}", hash_return);
assert!(hash_return == usize::encode());
assert!(*hash_return == usize::ENCODING);

// Invoke a method on the object
let hash: usize = unsafe {
Expand Down
27 changes: 13 additions & 14 deletions src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ fn count_args(sel: Sel) -> usize {
}

fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString {
let mut types = ret.as_str().to_owned();
// First two arguments are always self and the selector
types.push_str(<*mut Object>::encode().as_str());
types.push_str(Sel::encode().as_str());
types.extend(args.iter().map(|e| e.as_str()));
let mut types = format!("{}{}{}",
ret, <*mut Object>::ENCODING, Sel::ENCODING);
for enc in args {
use std::fmt::Write;
write!(&mut types, "{}", enc).unwrap();
}
CString::new(types).unwrap()
}

Expand Down Expand Up @@ -166,15 +168,14 @@ impl ClassDecl {
/// are expected when the method is invoked from Objective-C.
pub unsafe fn add_method<F>(&mut self, sel: Sel, func: F)
where F: MethodImplementation<Callee=Object> {
let encs = F::Args::encodings();
let encs = encs.as_ref();
let encs = F::Args::ENCODINGS;
let sel_args = count_args(sel);
assert!(sel_args == encs.len(),
"Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
);

let types = method_type_encoding(&F::Ret::encode(), encs);
let types = method_type_encoding(&F::Ret::ENCODING, encs);
let success = runtime::class_addMethod(self.cls, sel, func.imp(),
types.as_ptr());
assert!(success != NO, "Failed to add method {:?}", sel);
Expand All @@ -187,15 +188,14 @@ impl ClassDecl {
/// are expected when the method is invoked from Objective-C.
pub unsafe fn add_class_method<F>(&mut self, sel: Sel, func: F)
where F: MethodImplementation<Callee=Class> {
let encs = F::Args::encodings();
let encs = encs.as_ref();
let encs = F::Args::ENCODINGS;
let sel_args = count_args(sel);
assert!(sel_args == encs.len(),
"Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
);

let types = method_type_encoding(&F::Ret::encode(), encs);
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());
Expand All @@ -206,7 +206,7 @@ impl ClassDecl {
/// Panics if the ivar wasn't successfully added.
pub fn add_ivar<T>(&mut self, name: &str) where T: Encode {
let c_name = CString::new(name).unwrap();
let encoding = CString::new(T::encode().as_str()).unwrap();
let encoding = CString::new(T::ENCODING.to_string()).unwrap();
let size = mem::size_of::<T>();
let align = log2_align_of::<T>();
let success = unsafe {
Expand Down Expand Up @@ -269,14 +269,13 @@ impl ProtocolDecl {
is_instance_method: bool)
where Args: EncodeArguments,
Ret: Encode {
let encs = Args::encodings();
let encs = encs.as_ref();
let encs = Args::ENCODINGS;
let sel_args = count_args(sel);
assert!(sel_args == encs.len(),
"Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
);
let types = method_type_encoding(&Ret::encode(), encs);
let types = method_type_encoding(&Ret::ENCODING, encs);
unsafe {
runtime::protocol_addMethodDescription(
self.proto, sel, types.as_ptr(), is_required as BOOL, is_instance_method as BOOL);
Expand Down
247 changes: 20 additions & 227 deletions src/encode.rs
Original file line number Diff line number Diff line change
@@ -1,223 +1,39 @@
use std::fmt;
use std::os::raw::{c_char, c_void};
use std::str;

use malloc_buf::Malloc;

use runtime::{Class, Object, Sel};
use {Encode, Encoding};

const QUALIFIERS: &'static [char] = &[
'r', // const
'n', // in
'N', // inout
'o', // out
'O', // bycopy
'R', // byref
'V', // oneway
];

#[cfg(target_pointer_width = "64")]
const CODE_INLINE_CAP: usize = 30;

#[cfg(target_pointer_width = "32")]
const CODE_INLINE_CAP: usize = 14;

enum Code {
Slice(&'static str),
Owned(String),
Inline(u8, [u8; CODE_INLINE_CAP]),
Malloc(Malloc<str>)
}

/// An Objective-C type encoding.
///
/// For more information, see Apple's documentation:
/// <https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html>
pub struct Encoding {
code: Code,
}

impl Encoding {
/// Constructs an `Encoding` from its string representation.
/// Unsafe because the caller must ensure the string is a valid encoding.
pub unsafe fn from_str(code: &str) -> Encoding {
from_str(code)
}

/// Returns self as a `str`.
pub fn as_str(&self) -> &str {
match self.code {
Code::Slice(code) => code,
Code::Owned(ref code) => code,
Code::Inline(len, ref bytes) => unsafe {
str::from_utf8_unchecked(&bytes[..len as usize])
},
Code::Malloc(ref buf) => &*buf,
}
}
}

impl Clone for Encoding {
fn clone(&self) -> Encoding {
if let Code::Slice(code) = self.code {
from_static_str(code)
} else {
from_str(self.as_str())
}
}
}

impl PartialEq for Encoding {
fn eq(&self, other: &Encoding) -> bool {
// strip qualifiers when comparing
let s = self.as_str().trim_left_matches(QUALIFIERS);
let o = other.as_str().trim_left_matches(QUALIFIERS);
s == o
}
}

impl fmt::Debug for Encoding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}

pub fn from_static_str(code: &'static str) -> Encoding {
Encoding { code: Code::Slice(code) }
}

pub fn from_str(code: &str) -> Encoding {
if code.len() > CODE_INLINE_CAP {
Encoding { code: Code::Owned(code.to_owned()) }
} else {
let mut bytes = [0; CODE_INLINE_CAP];
for (dst, byte) in bytes.iter_mut().zip(code.bytes()) {
*dst = byte;
}
Encoding { code: Code::Inline(code.len() as u8, bytes) }
}
}

pub unsafe fn from_malloc_str(ptr: *mut c_char) -> Encoding {
let buf = Malloc::from_c_str(ptr);
Encoding { code: Code::Malloc(buf.unwrap()) }
}

/// Types that have an Objective-C type encoding.
///
/// Unsafe because Objective-C will make assumptions about the type (like its
/// size and alignment) from its encoding, so the implementer must verify that
/// the encoding is accurate.
pub unsafe trait Encode {
/// Returns the Objective-C type encoding for Self.
fn encode() -> Encoding;
unsafe impl Encode for Sel {
const ENCODING: Encoding<'static> = Encoding::Sel;
}

macro_rules! encode_impls {
($($t:ty : $s:expr,)*) => ($(
unsafe impl Encode for $t {
fn encode() -> Encoding { from_static_str($s) }
}
)*);
unsafe impl<'a> Encode for &'a Object {
const ENCODING: Encoding<'static> = Encoding::Object;
}

encode_impls!(
i8: "c",
i16: "s",
i32: "i",
i64: "q",
u8: "C",
u16: "S",
u32: "I",
u64: "Q",
f32: "f",
f64: "d",
bool: "B",
(): "v",
*mut c_char: "*",
*const c_char: "r*",
*mut c_void: "^v",
*const c_void: "r^v",
Sel: ":",
);

unsafe impl Encode for isize {
#[cfg(target_pointer_width = "32")]
fn encode() -> Encoding { i32::encode() }

#[cfg(target_pointer_width = "64")]
fn encode() -> Encoding { i64::encode() }
unsafe impl<'a> Encode for &'a mut Object {
const ENCODING: Encoding<'static> = Encoding::Object;
}

unsafe impl Encode for usize {
#[cfg(target_pointer_width = "32")]
fn encode() -> Encoding { u32::encode() }

#[cfg(target_pointer_width = "64")]
fn encode() -> Encoding { u64::encode() }
unsafe impl<'a> Encode for &'a Class {
const ENCODING: Encoding<'static> = Encoding::Class;
}

macro_rules! encode_message_impl {
($code:expr, $name:ident) => (
encode_message_impl!($code, $name,);
);
($code:expr, $name:ident, $($t:ident),*) => (
unsafe impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
fn encode() -> Encoding { from_static_str($code) }
}

unsafe impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
fn encode() -> Encoding { from_static_str($code) }
}

unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
fn encode() -> Encoding { from_static_str($code) }
}

unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
fn encode() -> Encoding { from_static_str($code) }
}

unsafe impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
fn encode() -> Encoding { from_static_str($code) }
}

unsafe impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
fn encode() -> Encoding { from_static_str($code) }
}
);
unsafe impl<'a> Encode for &'a mut Class {
const ENCODING: Encoding<'static> = Encoding::Class;
}

encode_message_impl!("@", Object);

encode_message_impl!("#", Class);

/// Types that represent a group of arguments, where each has an Objective-C
/// type encoding.
pub trait EncodeArguments {
/// The type as which the encodings for Self will be returned.
type Encs: AsRef<[Encoding]>;

/// Returns the Objective-C type encodings for Self.
fn encodings() -> Self::Encs;
}

macro_rules! count_idents {
() => (0);
($a:ident) => (1);
($a:ident, $($b:ident),+) => (1 + count_idents!($($b),*));
const ENCODINGS: &'static [Encoding<'static>];
}

macro_rules! encode_args_impl {
($($t:ident),*) => (
impl<$($t: Encode),*> EncodeArguments for ($($t,)*) {
type Encs = [Encoding; count_idents!($($t),*)];

fn encodings() -> Self::Encs {
[
$($t::encode()),*
]
}
const ENCODINGS: &'static [Encoding<'static>] = &[
$($t::ENCODING),*
];
}
);
}
Expand All @@ -238,37 +54,14 @@ encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);

#[cfg(test)]
mod tests {
use objc_encode::Encode;
use runtime::{Class, Object, Sel};
use super::{Encode, Encoding};

#[test]
fn test_encode() {
assert!(u32::encode().as_str() == "I");
assert!(<()>::encode().as_str() == "v");
assert!(<&Object>::encode().as_str() == "@");
assert!(<*mut Object>::encode().as_str() == "@");
assert!(<&Class>::encode().as_str() == "#");
assert!(Sel::encode().as_str() == ":");
}

#[test]
fn test_inline_encoding() {
let enc = unsafe { Encoding::from_str("C") };
assert!(enc.as_str() == "C");

let enc2 = enc.clone();
assert!(enc2 == enc);
assert!(enc2.as_str() == "C");
}

#[test]
fn test_owned_encoding() {
let s = "{Test=CCCCCCCCCCCCCCCCCCCCCCCCC}";
let enc = unsafe { Encoding::from_str(s) };
assert!(enc.as_str() == s);

let enc2 = enc.clone();
assert!(enc2 == enc);
assert!(enc2.as_str() == s);
assert!(<&Object>::ENCODING.to_string() == "@");
assert!(<*mut Object>::ENCODING.to_string() == "@");
assert!(<&Class>::ENCODING.to_string() == "#");
assert!(Sel::ENCODING.to_string() == ":");
}
}
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,13 @@ The bindings can be used on Linux or *BSD utilizing the
#![warn(missing_docs)]

extern crate malloc_buf;
extern crate objc_encode;
#[cfg(feature = "exception")]
extern crate objc_exception;

pub use encode::{Encode, EncodeArguments, Encoding};
pub use objc_encode::{Encode, Encoding};

pub use encode::EncodeArguments;
pub use message::{Message, MessageArguments, MessageError};

pub use message::send_message as __send_message;
Expand Down
Loading

0 comments on commit 6092caa

Please sign in to comment.