Skip to content

Commit

Permalink
Cache selectors so we don't call sel_registerName over and over.
Browse files Browse the repository at this point in the history
Improves SSheldon#49.

This is what the assembly looks like. The fast path is only 4 instructions.

		lea    r15, [rip + 0xbf936]      ; cocoa::foundation::NSString::alloc::register_sel::SEL::h005daf5ee04d2745
		mov    rbx, qword ptr [r15]
		test   rbx, rbx
		jne    ok
		lea    rdi, [rip + 0x95b2c]      ; byte_str.o.llvm.2369336028792347561
		call   0x10008fa02               ; symbol stub for: sel_registerName
		mov    rbx, rax
		mov    qword ptr [r15], rbx
	ok:
  • Loading branch information
pcwalton committed Jun 22, 2018
1 parent e8f79d8 commit 90ff608
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "objc"
version = "0.2.2"
version = "0.2.3"
authors = ["Steven Sheldon"]

description = "Objective-C Runtime bindings and wrapper for Rust."
Expand Down
47 changes: 29 additions & 18 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
#[doc(hidden)]
#[macro_export]
macro_rules! sel_impl {
// Declare a function to hide unsafety, otherwise we can trigger the
// unused_unsafe lint; see rust-lang/rust#8472
($name:expr) => ({
#[inline(always)]
fn register_sel(name: &str) -> $crate::runtime::Sel {
unsafe {
static SEL: ::std::sync::atomic::AtomicPtr<::std::os::raw::c_void> =
::std::sync::atomic::AtomicPtr::new(0 as *mut _);
let ptr = SEL.load(::std::sync::atomic::Ordering::Relaxed);
// It should be fine to use `Relaxed` ordering here because `sel_registerName` is
// thread-safe.
if ptr.is_null() {
let sel = $crate::runtime::sel_registerName(name.as_ptr() as *const _);
SEL.store(sel.as_ptr() as *mut _, ::std::sync::atomic::Ordering::Relaxed);
sel
} else {
$crate::runtime::Sel::from_ptr(ptr)
}
}
}
register_sel($name)
})
}

/**
Registers a selector, returning a `Sel`.
Expand All @@ -12,24 +39,8 @@ let sel = sel!(setObject:forKey:);
*/
#[macro_export]
macro_rules! sel {
// Declare a function to hide unsafety, otherwise we can trigger the
// unused_unsafe lint; see rust-lang/rust#8472
($name:ident) => ({
#[inline(always)]
fn register_sel(name_with_nul: &str) -> $crate::runtime::Sel {
let ptr = name_with_nul.as_ptr() as *const _;
unsafe { $crate::runtime::sel_registerName(ptr) }
}
register_sel(concat!(stringify!($name), '\0'))
});
($($name:ident :)+) => ({
#[inline(always)]
fn register_sel(name_with_nul: &str) -> $crate::runtime::Sel {
let ptr = name_with_nul.as_ptr() as *const _;
unsafe { $crate::runtime::sel_registerName(ptr) }
}
register_sel(concat!($(stringify!($name), ':'),+, '\0'))
});
($name:ident) => ({sel_impl!(concat!(stringify!($name), '\0'))});
($($name:ident :)+) => ({sel_impl!(concat!($(stringify!($name), ':'),+, '\0'))});
}

/**
Expand Down
16 changes: 16 additions & 0 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,22 @@ impl Sel {
};
str::from_utf8(name.to_bytes()).unwrap()
}

/// Wraps a raw pointer to a selector into a `Sel` object.
///
/// This is almost never what you want; use `Sel::register()` instead.
#[inline]
pub unsafe fn from_ptr(ptr: *const c_void) -> Sel {
Sel {
ptr,
}
}

/// Returns a pointer to the raw selector.
#[inline]
pub fn as_ptr(&self) -> *const c_void {
self.ptr
}
}

impl PartialEq for Sel {
Expand Down

0 comments on commit 90ff608

Please sign in to comment.