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

Faster msg_send! #100

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft
89 changes: 64 additions & 25 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,70 @@
language: rust
rust:
- stable
- nightly
os:
- osx
- linux
env:
- FEATURES="" IOS_ARCHS=""
matrix:
include:
- os: osx
rust: stable
env: FEATURES="exception verify_message" IOS_ARCHS=""
- os: osx
osx_image: xcode7.3
rust: 1.41.0
env: FEATURES="exception" IOS_ARCHS="i386 x86_64 armv7 armv7s aarch64"
sudo: false
install: ./travis_install.sh
language: rust
cache: cargo

addons:
apt:
packages:
- clang
- cmake
install: >
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
git clone -b 1.9 https://github.com/gnustep/libobjc2.git &&
mkdir libobjc2/build &&
cd libobjc2/build &&
export CC="clang" &&
export CXX="clang++" &&
cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/libobjc2_staging ../ &&
make install
fi
before_script: >
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
export LIBRARY_PATH=$HOME/libobjc2_staging/lib:$LIBRARY_PATH;
export LD_LIBRARY_PATH=$HOME/libobjc2_staging/lib:$LD_LIBRARY_PATH;
fi
script: ./travis_test.sh
addons:
apt:
packages:
- clang
- cmake
script: cargo test --verbose

jobs:
include:
- name: Check formatting
os: linux
rust: stable
install: rustup component add rustfmt
script: cargo fmt --all -- --check

- name: MacOS stable
os: osx
rust: stable
- name: MacOS nightly
os: osx
rust: nightly
- name: MacOS stable w. features
os: osx
rust: stable
script: cargo test --verbose --features "exception verify_message"
- name: MacOS 32bit
os: osx
osx_image: xcode9.4
rust: nightly
# 32-bit targets only have tier 3 support
install: rustup component add rust-src
script: cargo test -Z build-std --target i686-apple-darwin --verbose

- name: Linux stable
os: linux
rust: stable
- name: Linux nightly
os: linux
rust: nightly

- name: iOS nightly
os: osx
osx_image: xcode7.3
rust: nightly
before_install: rustup component add rust-src
# Install rust-test-ios
install: curl -LO https://github.com/SSheldon/rust-test-ios/releases/download/0.1.1/rust-test-ios && chmod +x rust-test-ios
# Enable -Z build-std, 32-bit targets only have tier 3 support
before_script: printf '[unstable]\nbuild-std = ["std"]\n' > $HOME/.cargo/config.toml
env: FEATURES="exception"
script: ./rust-test-ios
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
* C types are now used from `std::os::raw` rather than `libc`. This means
`Encode` may not be implemented for `libc` types; switch them to the
`std::os::raw` equivalents instead. This avoids an issue that would arise
from simultaneously using different versions of the libc crate.
from simultaneously using different versions of the libc crate.

* Dynamic messaging was moved into the `Message` trait; instead of
`().send(obj, sel!(description))`, use
Expand All @@ -110,7 +110,7 @@
### Fixed

* Corrected alignment of ivars in `ClassDecl`; declared classes may now have a
smaller size.
smaller size.

* With the `"exception"` or `"verify_message"` feature enabled, panics from
`msg_send!` will now be triggered from the line and file where the macro is
Expand Down
10 changes: 3 additions & 7 deletions examples/example.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#[macro_use]
extern crate objc;

use objc::Encode;
use objc::rc::StrongPtr;
use objc::runtime::{Class, Object};
use objc::Encode;

fn main() {
// Get a class
Expand All @@ -25,9 +25,7 @@ fn main() {
println!("NSObject address: {:p}", obj);

// Access an ivar of the object
let isa: *const Class = unsafe {
*(**obj).get_ivar("isa")
};
let isa: *const Class = unsafe { *(**obj).get_ivar("isa") };
println!("NSObject isa: {:?}", isa);

// Inspect a method of the class
Expand All @@ -38,8 +36,6 @@ fn main() {
assert!(*hash_return == usize::ENCODING);

// Invoke a method on the object
let hash: usize = unsafe {
msg_send![*obj, hash]
};
let hash: usize = unsafe { msg_send![*obj, hash] };
println!("NSObject hash: {}", hash);
}
10 changes: 5 additions & 5 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ use std::os::raw::c_void;
use std::ptr;
use std::sync::atomic::{AtomicPtr, Ordering};

use crate::runtime::{Class, Sel, self};
use crate::runtime::{self, Class, Sel};

/// Allows storing a `Sel` in a static and lazily loading it.
#[doc(hidden)]
pub struct CachedSel {
ptr: AtomicPtr<c_void>
ptr: AtomicPtr<c_void>,
}

impl CachedSel {
/// Constructs a new `CachedSel`.
pub const fn new() -> CachedSel {
CachedSel {
ptr: AtomicPtr::new(ptr::null_mut())
ptr: AtomicPtr::new(ptr::null_mut()),
}
}

Expand All @@ -38,14 +38,14 @@ impl CachedSel {
/// Allows storing a `Class` reference in a static and lazily loading it.
#[doc(hidden)]
pub struct CachedClass {
ptr: AtomicPtr<Class>
ptr: AtomicPtr<Class>,
}

impl CachedClass {
/// Constructs a new `CachedClass`.
pub const fn new() -> CachedClass {
CachedClass {
ptr: AtomicPtr::new(ptr::null_mut())
ptr: AtomicPtr::new(ptr::null_mut()),
}
}

Expand Down
93 changes: 55 additions & 38 deletions src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use std::ffi::CString;
use std::mem;
use std::ptr;

use crate::runtime::{BOOL, Class, Imp, NO, Object, Protocol, Sel, self};
use crate::runtime::{self, Class, Imp, Object, Protocol, Sel, BOOL, NO};
use crate::{Encode, EncodeArguments, Encoding, Message};

/// Types that can be used as the implementation of an Objective-C method.
Expand Down Expand Up @@ -93,8 +93,7 @@ fn count_args(sel: Sel) -> usize {

fn method_type_encoding(ret: &Encoding, args: &[Encoding]) -> CString {
// First two arguments are always self and the selector
let mut types = format!("{}{}{}",
ret, <*mut Object>::ENCODING, Sel::ENCODING);
let mut types = format!("{}{}{}", ret, <*mut Object>::ENCODING, Sel::ENCODING);
for enc in args {
use std::fmt::Write;
write!(&mut types, "{}", enc).unwrap();
Expand All @@ -117,13 +116,10 @@ pub struct ClassDecl {
}

impl ClassDecl {
fn with_superclass(name: &str, superclass: Option<&Class>)
-> Option<ClassDecl> {
fn with_superclass(name: &str, superclass: Option<&Class>) -> Option<ClassDecl> {
let name = CString::new(name).unwrap();
let super_ptr = superclass.map_or(ptr::null(), |c| c);
let cls = unsafe {
runtime::objc_allocateClassPair(super_ptr, name.as_ptr(), 0)
};
let cls = unsafe { runtime::objc_allocateClassPair(super_ptr, name.as_ptr(), 0) };
if cls.is_null() {
None
} else {
Expand All @@ -150,8 +146,7 @@ impl ClassDecl {
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 fn(&Class, Sel))
-> Option<ClassDecl> {
pub fn root(name: &str, 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 All @@ -167,17 +162,20 @@ impl ClassDecl {
/// Unsafe because the caller must ensure that the types match those that
/// 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> {
where
F: MethodImplementation<Callee = Object>,
{
let encs = F::Args::ENCODINGS;
let sel_args = count_args(sel);
assert!(sel_args == encs.len(),
assert!(
sel_args == encs.len(),
"Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
sel_args,
encs.len(),
);

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, sel, func.imp(), types.as_ptr());
assert!(success != NO, "Failed to add method {:?}", sel);
}

Expand All @@ -187,31 +185,36 @@ impl ClassDecl {
/// Unsafe because the caller must ensure that the types match those that
/// 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> {
where
F: MethodImplementation<Callee = Class>,
{
let encs = F::Args::ENCODINGS;
let sel_args = count_args(sel);
assert!(sel_args == encs.len(),
assert!(
sel_args == encs.len(),
"Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
sel_args,
encs.len(),
);

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, func.imp(), types.as_ptr());
assert!(success != NO, "Failed to add class method {:?}", sel);
}

/// Adds an ivar with type `T` and the provided name to self.
/// Panics if the ivar wasn't successfully added.
pub fn add_ivar<T>(&mut self, name: &str) where T: Encode {
pub fn add_ivar<T>(&mut self, name: &str)
where
T: Encode,
{
let c_name = CString::new(name).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 {
runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align,
encoding.as_ptr())
runtime::class_addIvar(self.cls, c_name.as_ptr(), size, align, encoding.as_ptr())
};
assert!(success != NO, "Failed to add ivar {}", name);
}
Expand Down Expand Up @@ -247,52 +250,66 @@ impl Drop for ClassDecl {
/// A type for declaring a new protocol and adding new methods to it
/// before registering it.
pub struct ProtocolDecl {
proto: *mut Protocol
proto: *mut Protocol,
}

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 {
runtime::objc_allocateProtocol(c_name.as_ptr())
};
let proto = unsafe { runtime::objc_allocateProtocol(c_name.as_ptr()) };
if proto.is_null() {
None
} else {
Some(ProtocolDecl { proto: proto })
}
}

fn add_method_description_common<Args, Ret>(&mut self, sel: Sel, is_required: bool,
is_instance_method: bool)
where Args: EncodeArguments,
Ret: Encode {
fn add_method_description_common<Args, Ret>(
&mut self,
sel: Sel,
is_required: bool,
is_instance_method: bool,
) where
Args: EncodeArguments,
Ret: Encode,
{
let encs = Args::ENCODINGS;
let sel_args = count_args(sel);
assert!(sel_args == encs.len(),
assert!(
sel_args == encs.len(),
"Selector accepts {} arguments, but function accepts {}",
sel_args, encs.len(),
sel_args,
encs.len(),
);
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);
self.proto,
sel,
types.as_ptr(),
is_required as BOOL,
is_instance_method as BOOL,
);
}
}

/// Adds an instance method declaration with a given description to self.
pub fn add_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
where Args: EncodeArguments,
Ret: Encode {
where
Args: EncodeArguments,
Ret: Encode,
{
self.add_method_description_common::<Args, Ret>(sel, is_required, true)
}

/// Adds a class method declaration with a given description to self.
pub fn add_class_method_description<Args, Ret>(&mut self, sel: Sel, is_required: bool)
where Args: EncodeArguments,
Ret: Encode {
where
Args: EncodeArguments,
Ret: Encode,
{
self.add_method_description_common::<Args, Ret>(sel, is_required, false)
}

Expand Down
2 changes: 1 addition & 1 deletion src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);

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

#[test]
fn test_encode() {
Expand Down
Loading