Skip to content

Commit

Permalink
Use static selector for ObjCObject::class
Browse files Browse the repository at this point in the history
This uses the same approach as native selectors in Objective-C. Swift
selectors are similar, and in this case would use `L_selector(class)`
for the selector reference and `L_selector_data(class)` for the "class"
string data.

This is a step towards #2.

However, this currently fails to compile when used externally. That is
because a private symbols (required by linker for selector sections)
cannot cross compilation units. This appears to be a bug in LLVM.
See rust-lang/rust#53929 for current status.
  • Loading branch information
nvzqz committed Dec 17, 2020
1 parent 8a1bbb1 commit c15a1f6
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 2 deletions.
5 changes: 4 additions & 1 deletion src/objc/objc_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ impl<'data> super::ObjectType<'data> for ObjCObject<'data> {
// - iOS (unknown)
// - tvOS (unknown)
// - watchOS (unknown)
unsafe { _msg_send_strict_cached![self, class] }

// TODO: Use macro to dereference a `GlobalSel`.
let sel = crate::objc::sel::global::class.get();
unsafe { self._msg_send_strict(sel) }
}
}

Expand Down
1 change: 0 additions & 1 deletion src/objc/sel/cached.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

use super::atomic::AtomicSel;

pub(crate) static class: AtomicSel = AtomicSel::null();
pub(crate) static copy: AtomicSel = AtomicSel::null();
pub(crate) static mutableCopy: AtomicSel = AtomicSel::null();
pub(crate) static hash: AtomicSel = AtomicSel::null();
Expand Down
65 changes: 65 additions & 0 deletions src/objc/sel/global.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use super::Sel;
use std::cell::Cell;

/// Stores a static selector that is registered at program start by the
/// Objective-C runtime.
///
/// This always stores a valid `Sel` instance.
#[repr(transparent)]
pub(crate) struct GlobalSel {
/// The selector stored by this instance.
///
/// `Cell` is used here for three reasons:
///
/// 1. We want to inform the compiler that this value is internally mutable
/// and thus should always be initially read from memory.
///
/// This is also achievable with `AtomicPtr`, however we don't use it
/// because of the second reason:
///
/// 2. We want the compiler to assume that multiple reads within the same
/// scope will produce the same value. This optimization helps in
/// situations like calling `objc_msgSend` in a loop, where the selector
/// read can be hoisted out of the loop.
///
/// This does not currently happen for `AtomicPtr` with `Relaxed` loads,
/// even though it should be possible.
///
/// 3. "Cell Sel" is funny to say.
inner: Cell<Sel>,
}

// SAFETY: The value is re-assigned before it is read in Rust, and it is never
// written to by Rust code.
unsafe impl Send for GlobalSel {}
unsafe impl Sync for GlobalSel {}

impl GlobalSel {
#[inline]
pub const fn new(sel: Sel) -> Self {
Self {
inner: Cell::new(sel),
}
}

#[inline]
pub fn get(&self) -> Sel {
self.inner.get()
}
}

// TODO: Make private symbols work across compilation units.
// See https://github.com/rust-lang/rust/issues/53929.
//
// TODO: Once the above TODO is done, make all selectors use `GlobalSel`.
#[used]
#[link_section = "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"]
#[export_name = "\x01L_OBJC_SELECTOR_REFERENCES_.fruity(class)"]
#[allow(non_upper_case_globals)]
pub(crate) static class: GlobalSel = {
#[link_section = "__TEXT,__objc_methname,cstring_literals"]
#[export_name = "\x01L_OBJC_METH_VAR_NAME_.fruity(class)"]
static SELECTOR: [u8; 6] = *b"class\0";

GlobalSel::new(unsafe { Sel::from_ptr(SELECTOR.as_ptr().cast()) })
};
1 change: 1 addition & 0 deletions src/objc/sel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod macros;

pub(crate) mod atomic;
pub(crate) mod cached;
pub(crate) mod global;

/// A method selector.
///
Expand Down

0 comments on commit c15a1f6

Please sign in to comment.